hyperledger-web3j/web3j

Request send() hangs if response can not be parsed when using WebSocketService

Closed this issue · 0 comments

no result from send()

I'm trying to load blocks in batch from infura.io and sometimes due to some issue in infura I'm getting incorrect result. Instead of block object it could be just string, or id could be some uuid instead of long. If this situation occurs exception is logged in console and application just stops because send() method never exits.

As I see it, WebSocketService.processBatchRequestReply receives response, removes it from map, so timeout never happens, parsing throws one of Jackson exceptions and WebSocketService doesn't catch it. It is logged in WebSocketClient.onMessage(), but request never gets any response.

ERROR [org.web3j.protocol.websocket.WebSocketClient] (WebSocketConnectReadThread-186) Failed to process message '...' from server wss://holesky.infura.io/ws/v3/<mykey>: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.web3j.protocol.core.methods.response.EthBlock$Block` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('0x0000000000000000000000000000000000000000000000000000000000002460') at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: org.web3j.protocol.core.methods.response.EthBlock["result"])
        at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
        at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1754)
        at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1379)
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer._deserializeFromString(StdDeserializer.java:311)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1592)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:197)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
        at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:2099)
        at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1249)
        at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1267)
        at org.web3j.protocol.core.methods.response.EthBlock$ResponseDeserialiser.deserialize(EthBlock.java:1001)
        at org.web3j.protocol.core.methods.response.EthBlock$ResponseDeserialiser.deserialize(EthBlock.java:992)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
        at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:4893)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3036)
        at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:3500)
        at org.web3j.protocol.websocket.WebSocketService.processBatchRequestReply(WebSocketService.java:319)
        at org.web3j.protocol.websocket.WebSocketService.onWebSocketMessage(WebSocketService.java:281)
        at org.web3j.protocol.websocket.WebSocketService$1.onMessage(WebSocketService.java:155)
        at org.web3j.protocol.websocket.WebSocketClient.lambda$onMessage$0(WebSocketClient.java:52)
        at java.base/java.util.Optional.ifPresent(Optional.java:178)
        at org.web3j.protocol.websocket.WebSocketClient.onMessage(WebSocketClient.java:49)
        at org.java_websocket.client.WebSocketClient.onWebsocketMessage(WebSocketClient.java:644)
        at org.java_websocket.drafts.Draft_6455.processFrameText(Draft_6455.java:986)
        at org.java_websocket.drafts.Draft_6455.processFrame(Draft_6455.java:910)
        at org.java_websocket.WebSocketImpl.decodeFrames(WebSocketImpl.java:402)
        at org.java_websocket.WebSocketImpl.decode(WebSocketImpl.java:234)
        at org.java_websocket.client.WebSocketClient.run(WebSocketClient.java:527)
        at java.base/java.lang.Thread.run(Thread.java:1583)

Exception can be different depending on what faulty response I get

Steps To Reproduce

    web3jService = new WebSocketService(network.getRpcUrl(), false);
    web3jService.connect();
    web3j = Web3j.build(web3jService);

    BatchRequest batchRequest = web3j.newBatch();
    for (long i = startBlockNum; i < endBlockNum; i++) {
        batchRequest.add(web3j.ethGetBlockByNumber(DefaultBlockParameter.valueOf(BigInteger.valueOf(i)), true));
    }

    //this call never exists if response is incorrect
    BatchResponse batchResponse = batchRequest.send();

Expected behavior

IOException or RuntimeException should be thrown or in case of sendAsync future should complete exceptionally.

Actual behavior

send method never exits

Environment

  • Web3j version - 4.12.2
  • Jackson databind - 2.18.1, but I think could be any

Additional context

I think problem and its possible solution is clear, but ask me if anything more information is needed