ConsenSysMesh / cava

ConsenSys core libraries for Java & Kotlin
Apache License 2.0
84 stars 34 forks source link

Cannot decrypt response header for scuttlebutt RPC request #192

Closed Happy0 closed 5 years ago

Happy0 commented 5 years ago

Hi - thanks for working on these Java libraries for using the scuttlebutt protocol!

I have encountered a bug while attempting to use the scuttlebutt-handshake library to

Expected behaviour

Actual behaviour

Steps I used to reproduce:

*
 * This Java source file was generated by the Gradle 'init' task.
 */
package cava.test;

import io.vertx.core.Vertx;
import net.consensys.cava.bytes.Bytes;
import net.consensys.cava.bytes.Bytes32;
import net.consensys.cava.concurrent.AsyncCompletion;
import net.consensys.cava.scuttlebutt.handshake.vertx.SecureScuttlebuttVertxClient;

import net.consensys.cava.crypto.sodium.Signature;
import net.consensys.cava.scuttlebutt.rpc.RPCCodec;
import net.consensys.cava.scuttlebutt.rpc.RPCFlag;

import java.util.Base64;

public class App {

    public static void main(String[] args) throws Exception {

        int port = 8008;

        String host = "localhost";

        String publicKeyBase64 = System.getenv("scuttle_public_key");
        String privateKeyBase64 = System.getenv("scuttle_private_key");

        if (publicKeyBase64 == null) {
            throw new Exception("scuttle_public_key environment variable missing");
        }

        if (privateKeyBase64 == null) {
            throw new Exception("scuttle_private_key environment variable missing");
        }

        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);

        Signature.PublicKey publicKey = Signature.PublicKey.fromBytes(publicKeyBytes);
        Signature.SecretKey privateKey = Signature.SecretKey.fromBytes(privateKeyBytes);

        Signature.KeyPair keyPair = new Signature.KeyPair(publicKey, privateKey);

        Vertx vertx = Vertx.vertx();

        // The default scuttlebutt network key (the one Patchwork uses.)
        String networkKeyBase64 = "1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=";
        byte[] networkKeyBytes = Base64.getDecoder().decode(networkKeyBase64);

        Bytes32 networkKeyBytes32 = Bytes32.wrap(networkKeyBytes);

        SecureScuttlebuttVertxClient secureScuttlebuttVertxClient =
                new SecureScuttlebuttVertxClient(vertx, keyPair, networkKeyBytes32);

        AsyncCompletion onConnect = secureScuttlebuttVertxClient.connectTo(port, host, publicKey, (senderFn, stopFn) -> {

            // Tell the client handler how to send bytes to the server
            MyClientHandler clientHandler = new MyClientHandler(senderFn, stopFn);

            // An RPC command that just tells us our public key (like ssb-server whoami on the command line.)
            String rpcRequestBody = "{\n" +
                    "  \"name\": [\"whoami\"],\n" +
                    "  \"type\": \"async\" " +
                    "}";
            Bytes rpcRequest = RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON);

            System.out.println("Attempting RPC request...");
            clientHandler.sendMessage(rpcRequest);

            // We hand over a ClientHandler so we get called back when new bytes arrive
            return clientHandler;
        });

    }
}

Define a ClientHandler implementation


package cava.test;

import net.consensys.cava.bytes.Bytes;
import net.consensys.cava.scuttlebutt.handshake.vertx.ClientHandler;

import java.util.function.Consumer;

public class MyClientHandler implements ClientHandler {

    private final Consumer<Bytes> sender;
    private final Runnable terminationFn;

    public MyClientHandler(Consumer<Bytes> sender, Runnable terminationFn) {
        this.sender = sender;
        this.terminationFn = terminationFn;
    }

    @Override
    public void receivedMessage(Bytes message) {

        System.out.println("We received a message?");

    }

    @Override
    public void streamClosed() {

        System.out.println("Stream closed?");

    }

    void sendMessage(Bytes bytes) {
        System.out.println("Sending message?");
        sender.accept(bytes);
    }

    void closeStream() {
        terminationFn.run();
    }
}

I'm trying to debug the issue at the moment (i wonder if there's something strange going on with the keys), but I wondered if you had any ideas?

atoulme commented 5 years ago

The main thing I can see from your code is that you don't join on the AsyncCompletion.

Effectively, your program doesn't wait for onConnect to finish, and terminates early.

Try to add onConnect.join();

Please print out the stacktrace of the NPE you see as well?

atoulme commented 5 years ago

The latest build of master is here: https://bintray.com/consensys/consensys/cava/1.0.0-C01FE1-snapshot.

You can use this build by relying on jcenter.

atoulme commented 5 years ago

I have an integration test running here: https://github.com/ConsenSys/cava/blob/master/scuttlebutt-handshake/src/test/java/net/consensys/cava/scuttlebutt/handshake/vertx/VertxIntegrationTest.java

Is your test much different from this approach? I'll try to reproduce using a unit test.

Happy0 commented 5 years ago

Thanks for your thoughts :)

.join()

Thanks - I added .join() - i actually had it previously as I was following the style of your unit tests, but then I forgot to add it back in after I made some changes :)

Stack trace

Here is a stack trace. However, this isn't actually printed to the console - I got this by running printStackTrace on the exception object in the step through debugger in intellij.

I still haven't worked out how to get vertx to not swallow the error.

java.lang.NullPointerException
    at net.consensys.cava.scuttlebutt.handshake.SecureScuttlebuttStream.decryptMessage(SecureScuttlebuttStream.java:87)
    at net.consensys.cava.scuttlebutt.handshake.SecureScuttlebuttStream.decrypt(SecureScuttlebuttStream.java:75)
    at net.consensys.cava.scuttlebutt.handshake.SecureScuttlebuttStream.readFromServer(SecureScuttlebuttStream.java:53)
    at net.consensys.cava.scuttlebutt.handshake.vertx.SecureScuttlebuttVertxClient$NetSocketClientHandler.handle(SecureScuttlebuttVertxClient.java:77)
    at io.vertx.core.net.impl.NetSocketImpl$DataMessageHandler.handle(NetSocketImpl.java:392)
    at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:225)
    at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:123)
    at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:370)
    at io.vertx.core.net.impl.ConnectionBase.handleRead(ConnectionBase.java:397)
    at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
    at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:43)
    at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:188)
    at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:174)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)

I tried calling the exceptionHandler method on the NetSocket object in the connectTo method, but that didn't work. Here's a screen shot where the error gets swallowed due to the exception handler being null in the InboundBuffer class in Vertx.

The exception handler is null

Published jar

Thanks!

What is the naming convention for the latest SNAPSHOT? I'd looked at all the versions here ( https://consensys.bintray.com/consensys/net/consensys/cava/cava-scuttlebutt-handshake/ ) and assumed that the one beginning with 'F' was the latest because it's the last one on the list and the highest letter at the start.

Integration test

I actually followed the example of that very test to learn to use the library, so my code shouldn't be that much different. The only difference is that I don't create an AtomicReference for the handler as I don't need to use it outside the connectTo callback for now.

For what it's worth, I spent a while comparing your code to the protocol guide today (the handshake and shared secret key and nonce generation for decrypting the headers and body), and I couldn't see any differences - looks perfect to me. I'm really confused!

I hope this is reproducible with a unit test! You could try copying my classes for the above and running against a local scuttlebot if that helps?

atoulme commented 5 years ago

Yes, that's what I am doing now. I assumed you were connecting to the local Patchwork running on your machine.

So far I don't see the exception being thrown, digging in more now.

Happy0 commented 5 years ago

@atoulme - yeah, that's what I was doing (running it against patchbay which is an alternative client to patchwork) on my local machine :)

So far I don't see the exception being thrown, digging in more now.

it doesn't get printed to the console as Vertx is (strangely) swallowing it, if that's what you mean? Or do you mean you don't see it being thrown when step through debugging to line 87 on SecureScuttlebuttStream.java?

atoulme commented 5 years ago

Not thrown - but I suspect I need to tune a bit better the keys used for the exchange.

On Mar 20, 2019, at 3:41 PM, Gordon Martin notifications@github.com wrote:

@atoulme https://github.com/atoulme - yeah, that's what I was doing (running it against patchbay which is an alternative client to patchwork) on my local machine :)

So far I don't see the exception being thrown, digging in more now.

it doesn't get printed to the console as Vertx is (strangely) swallowing it, if that's what you mean? Or do you mean you don't see it being thrown when step through debugging to line 87 on SecureScuttlebuttStream.java?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ConsenSys/cava/issues/192#issuecomment-475057017, or mute the thread https://github.com/notifications/unsubscribe-auth/AABBdhlzOy5TrYBlrkDAHfIYwNT_DTKoks5vYrkVgaJpZM4b_aqM.

atoulme commented 5 years ago

OK, I have created a unit test that attempts to connect to a host on localhost at port 8008. You need to provide the public key (see the TODO). It's checked in in this branch. https://github.com/atoulme/cava/tree/integration_testing_ssb

With this unit test, handshake proceeds and the client sends correctly a message to the remote host. The host doesn't respond however. Do you know how to get patchwork or patchbay to log a detailed log of requests they are receiving?

Happy0 commented 5 years ago

Thanks a lot, @atoulme . I'll try this out tomorrow and look into getting patchwork to print RPC activity :)

Happy0 commented 5 years ago

@atoulme - I tried the test and I got the same issue. I also got handshake failures on some of the runs, so I thought it might make sense to only complete the future if the handshake succeeds - I made a PR against your integration_testing_ssb branch here ( https://github.com/atoulme/cava/pull/1.) That way the consuming code knows it's safe to make RPC requests because the connection is complete and the handshake has finished.

I made a gif of the exception happening and uploaded it here: https://imgur.com/a/OiXbLvc

Note: the error is actually swallowed by Vertx and not printed by the logger through the following code path:

I wonder if the same happens for you if you step through debug?

Happy0 commented 5 years ago

I just updated the https://github.com/atoulme/cava/pull/1 pull request to throw a more meaningful error than 'null pointer exception' if we failed to decrypt the header. This stops the error being swallowed before it's printed out.

atoulme commented 5 years ago

I will test with patchbay today. Did you find a way to get debug output from patchbay?

Happy0 commented 5 years ago

@atoulme - it doesn't look like there's a way to do it at the moment, but I'll add some modifications to the secret-stack library's core.js file to print RPC request and responses and put together a patchwork version that uses it. I'll let you know when I've done that.

Happy0 commented 5 years ago

@atoulme

I added the custom RPC logging to a patchbay branch ( https://github.com/ssbc/patchbay/tree/print-rpc ) for the incoming and outgoing socket streams .

It seems like an error is being thrown here in mux-rpc https://github.com/ssbc/muxrpc/blob/v6/stream.js#L39 . I'll see if I can add logging at this level too - maybe something is malformed about our RPC request and it's causing the RPC response to be malformed (not have encrypted headers or something...) I'll investigate.

Note: to make sure it wasn't just because args is missing in our test, I copied and pasted the RPC request that's printed when I ran sbot whoami on the command line to send the same command, and ran the test again (with the same effect.)

    String rpcRequestBody = "{\"name\":[\"whoami\"],\"args\":[]}";

Assuming you have npm and node installed, you can run it with my custom logging by doing:

git clone https://github.com/ssbc/patchbay
git checkout print-rpc
npm install
npm start

Here's the log for when I ran the test (where source is incoming socket data and sink is outgoing socket data.)

[source] {"name":["gossip","peers"],"args":[]} [sink] ���� [sink] []

[sink] : [sink] {"name":["blobs","createWants"],"args":[],"type":"source"} [source] �: { "name": ["whoami"], "type": "async", "args": [] } [sink] ����� [sink] {"message":"invalid request, args should be array, was:{\"type\":\"Buffer\",\"data\":[10,123,10,32,32,34,110,97,109,101,34,58,32,91,34,119,104,111,97,109,105,34,93,44,10,32,32,34,116,121,112,101,34,58,32,34,97,115,121,110,99,34,44,10,32,32,34,97,114,103,115,34,58,32,91,93,10,125]}","name":"Error","stack":"Error: invalid request, args should be array, was:{\"type\":\"Buffer\",\"data\":[10,123,10,32,32,34,110,97,109,101,34,58,32,91,34,119,104,111,97,109,105,34,93,44,10,32,32,34,116,121,112,101,34,58,32,34,97,115,121,110,99,34,44,10,32,32,34,97,114,103,115,34,58,32,91,93,10,125]}\n at Object.request (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/muxrpc/stream.js:39:19)\n at PacketStream._onrequest (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/packet-stream/index.js:161:17)\n at PacketStream.write (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/packet-stream/index.js:134:41)\n at /home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/muxrpc/pull-weird.js:56:15\n at /home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-stream/sinks/drain.js:24:37\n at /home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-goodbye/node_modules/pull-stream/throughs/filter.js:17:11\n at Object.cb (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/packet-stream-codec/index.js:111:11)\n at drain (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-reader/index.js:39:14)\n at more (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-reader/index.js:55:13)\n at Function.reader.read (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-reader/index.js:99:7)"}

atoulme commented 5 years ago

Great. On my end I ran Patchwork directly from CLI, and it's outputting some logging to stdout.


    at Object.request (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/muxrpc/stream.js:41:12)
    at PacketStream._onrequest (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/packet-stream/index.js:161:17)
    at PacketStream.write (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/packet-stream/index.js:134:41)
    at /Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/muxrpc/pull-weird.js:56:15
    at /Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/pull-stream/sinks/drain.js:24:37
    at /Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/pull-goodbye/node_modules/pull-stream/throughs/filter.js:17:11
    at Object.cb (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/packet-stream-codec/index.js:111:11)
    at drain (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/pull-reader/index.js:39:14)
    at more (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/pull-reader/index.js:51:13)
    at Function.reader.read (/Applications/Patchwork.app/Contents/Resources/app.asar/node_modules/pull-reader/index.js:95:7)```

I suspect we're seeing the same issue.
atoulme commented 5 years ago

I tried to add "args:[]" to the RPC payload as it sounds like that was the issue.

But I didn't get a different error.

I'm switching gears. I tried running with ssb-server and getting proper connection and disconnection, but no error being logged:

info @0r9 SBOT @WZQEaekgF2I0AeGTqdamAeKtW8P6PTxKumiqQF7X7zM=.ed25519 Connected 
info @0r9 SBOT @WZQEaekgF2I0AeGTqdamAeKtW8P6PTxKumiqQF7X7zM=.ed25519 Disconnected 
info @0r9 SBOT @vUbEJ8HiAt4QT9iecz+nM+fv64cqmJWfULH18XlRKhY=.ed25519 Connected 
info @0r9 SBOT @vUbEJ8HiAt4QT9iecz+nM+fv64cqmJWfULH18XlRKhY=.ed25519 Disconnected 
info @0r9 SBOT @EnUFXgO/eQqZOT0tYhb6lakPOHiSnlV5LcGCOMVvQWg=.ed25519 Connected 
info @0r9 SBOT @EnUFXgO/eQqZOT0tYhb6lakPOHiSnlV5LcGCOMVvQWg=.ed25519 Disconnected 
atoulme commented 5 years ago

I'll try your build now.

cleishm commented 5 years ago

@atoulme: as a related aside, what do you think about converting all this to coroutines (with an AsyncResult/Completion wrapper for the Luddites)? At least then we can encourage people to use the Kotlin API which will prevent any missing calls to .join(), etc.

atoulme commented 5 years ago

Well, that's quite unrelated to the issue at hand here.

There is a couple things that need to be straightened out in the vert.x client and server impls so they really do a better job with multiple connections. We should also build a kotlin version of those constructs for easier consumption, yes.

The rest of the code is quite procedural and can stay in Java.

Now back to debugging this thing.

Happy0 commented 5 years ago

As another data point, the byte buffer printed out in the error above contains the payload itself when converted to UTF-8

I wonder if the codec is encoding the body as a string rather than JSON in the headers or something? (just clutching at straws - I'll see if I can add some logging at the mux-rpc decoding layer)

> var a = new Buffer([123,34,110,97,109,101,34,58,91,34,119,104,111,97,109,105,34,93,44,34,97,114,103,115,34,58,91,93,125])
undefined
> a.toString()
'{"name":["whoami"],"args":[]}'
> 

Another note:

I don't think the error decrypting the header issue is related to the malformed RPC requests error. The failure to decrypt header error happens even when the clientHandler.sendMessage(rpcRequest); line is commented out in the unit tests.

I guess it's possible that patchbay / patchwork is sending an RPC request to the Java process or there are some unexpected bytes or something.

I see some of these in the console, so I think it might be sending it to the Java process:

[sink] {"name":["blobs","createWants"],"args":[],"type":"source"}
atoulme commented 5 years ago

Yes, we're having an issue with how the type of the RPC message is encoded.

It looks like https://github.com/ssbc/packet-stream-codec/blob/master/index.js does not interpret the byte correctly - the type is therefore set to 0, and the RPC method body is a buffer, which is a problem when you try to read its contents, as a JSON object is expected.

I am missing something in the way RPC flags are encoded - looking at it now.

cleishm commented 5 years ago

Comment based on https://github.com/ConsenSys/cava/issues/192#issuecomment-475053532, but yes, otherwise offtopic :)

atoulme commented 5 years ago

OK this is fixed - I got RPC flags inverted, sorry about that! Here is the PR with a fix: https://github.com/ConsenSys/cava/pull/194/files

Please take a look. That might maybe solve the other issue you see with the incoming RPC message.

FWIW when I run whoami something about allowed functions.

Happy0 commented 5 years ago

Yeah, that's actually to be expected. You can only run whoami if you're using your own key (much like you could only publish a message if you're using your own key)

{"message":"method:whoami is not in list of allowed methods","name":"Error","stack":"Error: method:whoami is not in list of allowed methods\n    at Function.perms.pre (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/muxrpc/permissions.js:88:14)\n    at Object.<anonymous> (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/muxrpc/local-api.js:35:21)\n    at Object.request (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/muxrpc/stream.js:48:17)\n    at PacketStream._onrequest (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/packet-stream/index.js:161:17)\n    at PacketStream.write (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/packet-stream/index.js:134:41)\n    at /home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/muxrpc/pull-weird.js:56:15\n    at /home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-stream/sinks/drain.js:24:37\n    at /home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-goodbye/node_modules/pull-stream/throughs/filter.js:17:11\n    at Object.cb (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/packet-stream-codec/index.js:111:11)\n    at drain (/home/happy0/projects/patchbay/node_modules/ssb-server/node_modules/pull-reader/index.js:39:14)"}

When I use the same keys as the ones in my ~/.ssb directory, patchbay attempts to send the right response over the socket:

[source] /{"name": ["whoami"],"type": "source","args":[]}
[sink] >����
[sink] {"id":"@JwQoZfY1oPn7wxYJw7rDBJAxJOFhaBcixuLoEV1enXM=.ed25519"}

However, I'm still getting the NullPointerException when the library tries to decrypt the headers.

So the We received a message? message is now printed for you in the test? I wonder if there's something wrong with my libsodium library versions compared to you or something :S

Your pull request looks good to me =]. I think it'd be better if the future was completed with the exception if something goes wrong in the secret handshake: https://github.com/ConsenSys/cava/pull/195

atoulme commented 5 years ago

No, it didn't print, since patchbay would throw an error about the allowed methods. I can look at going past that error and see if I reproduce your problem.

I have changed the code so it waits until handshake completion to return already, so the changes in your PR might not work.

Happy0 commented 5 years ago

@atoulme - if it helps, I've made a PR that updates the test to read your secret file in ~/.ssb/secret so that it's connecting with the same keys as the server (which means you're authenticated for any RPC request.)

PR here: https://github.com/ConsenSys/cava/pull/196/ :)

atoulme commented 5 years ago

That's great! I will try to make the build pass on it and merge it. I have a few things in the way today, I will get to it early this afternoon my time.

atoulme commented 5 years ago

With your patch I do see an exception. Great! Now I can concentrate on fixing the issue. Thank you for your help on those issues.

atoulme commented 5 years ago

PR is here: #199

Thank you Gordon for helping out along the way. With this PR I receive messages from patchbay!

Please let me know if you see other problems.

Happy0 commented 5 years ago

Fantastic! Thanks @atoulme =]. Works for me too - very exciting. I was comparing the nonce stuff in the protocol specs to the code and that totally passed me by - I'm glad you spotted it.

I'm going to add some abstractions to these libraries to multiplex the RPC requests into Consumer<RPCMessage> for source type RPC requests and Future<RPCMessage> for async type RPC requests. Then perhaps add another layer on top of that which marshalls the JSON in the body of these RPCMessages into a specified Java class.

Is this something you'd be interested in having in Cava, or should it be a separate library which leverages Cava's scuttlebutt libraries?

Happy0 commented 5 years ago

It actually fails some of the time. I wonder if there's some subtle edge case with the buffering. I'll look into it :).

I added some code to the runWithPatchWork to perform the RPC request multiple times:

((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));
    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));
    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));
    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));

    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));
    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));
    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));
    ((MyClientHandler) clientHandler).sendMessage(RPCCodec.encodeRequest(rpcRequestBody, RPCFlag.BodyType.JSON));

Sometimes it fails to decode one of the calls:

/usr/lib/jvm/java-8-openjdk-amd64/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/home/happy0/intellij/lib/idea_rt.jar=34465:/home/happy0/intellij/bin -Dfile.encoding=UTF-8 -classpath /home/happy0/intellij/lib/idea_rt.jar:/home/happy0/intellij/plugins/junit/lib/junit-rt.jar:/home/happy0/intellij/plugins/junit/lib/junit5-rt.jar:/home/happy0/.m2/repository/org/junit/platform/junit-platform-launcher/1.3.2/junit-platform-launcher-1.3.2.jar:/home/happy0/.m2/repository/org/apiguardian/apiguardian-api/1.0.0/apiguardian-api-1.0.0.jar:/home/happy0/.m2/repository/org/junit/platform/junit-platform-engine/1.3.2/junit-platform-engine-1.3.2.jar:/home/happy0/.m2/repository/org/junit/platform/junit-platform-commons/1.3.2/junit-platform-commons-1.3.2.jar:/home/happy0/.m2/repository/org/opentest4j/opentest4j/1.1.1/opentest4j-1.1.1.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/java-atk-wrapper.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/home/happy0/projects/cava/scuttlebutt-rpc/out/test/classes:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotation/2.3.2/7c554c59dd2ea8c4e9e36b3308f8bf92db83e70c/error_prone_annotation-2.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.googlecode.java-diff-utils/diffutils/1.3.0/7e060dd5b19431e6d198e91ff670644372f60fbd/diffutils-1.3.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.auto/auto-common/0.10/c8f153ebe04a17183480ab4016098055fb474364/auto-common-0.10.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.checkerframework/javacutil/2.5.3/c545ca6fc7a57e3bc65d46e8e9438376f0db35ea/javacutil-2.5.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.pcollections/pcollections/2.1.2/15925fd6c32a29fe3f40a048d238c5ca58cb8362/pcollections-2.1.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_check_api/2.3.2/7415438c00adec8ba707689ec4168c8484d8403b/error_prone_check_api-2.3.2.jar:/home/happy0/projects/cava/scuttlebutt-rpc/out/production/classes:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_core/2.3.2/d5d121a23bcd48df2fe42dc3f1424cd05872993/error_prone_core-2.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.protobuf/protobuf-java/3.4.0/b32aba0cbe737a4ca953f71688725972e3ee927c/protobuf-java-3.4.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.checkerframework/dataflow/2.5.3/edf284e0838290d661b22483ecf648065e7ec440/dataflow-2.5.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.stephenc.jcip/jcip-annotations/1.0-1/ef31541dd28ae2cefdd17c7ebf352d93e9058c63/jcip-annotations-1.0-1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.3.2/d1a0c5032570e0f64be6b4d9c90cdeb103129029/error_prone_annotations-2.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_type_annotations/2.3.2/e79e88b9051888c8ea806f49c86fa7e3e3728180/error_prone_type_annotations-2.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-qual/2.5.3/4fe154d21bd734fe8c94ada37cdc41a9a6d61776/checker-qual-2.5.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jFormatString/3.0.0/d3995f9be450813bc2ccee8f0774c1a3033a0f30/jFormatString-3.0.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.kevinstern/software-and-algorithms/1.0/5e77666b72c6c5dd583c36148d17fc47f944dfb5/software-and-algorithms-1.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.vertx/vertx-core/3.6.2/988227077d06afc711b471680bd8af045a8aa784/vertx-core-3.6.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.5/3490508379d065fe3fcb80042b62f630f7588606/jackson-databind-2.9.5.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-engine/5.3.2/69350316a14c46d8f6c4c909e469ec9edf58c4f8/junit-jupiter-engine-5.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.bouncycastle/bcprov-jdk15on/1.60/bd47ad3bd14b8e82595c7adaa143501e60842a84/bcprov-jdk15on-1.60.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-params/5.3.2/606c97428ba94d06022cef6d0ae44e1eaff8ca3d/junit-jupiter-params-5.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.junit.jupiter/junit-jupiter-api/5.3.2/3602b523ffae9dabc04c329d73ab39ab04b3cbe2/junit-jupiter-api-5.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.logl/logl-logl/0.3.1/ec13fbeadbf5e922f2a97a953433136be782ce9/logl-logl-0.3.1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.logl/logl-vertx/0.3.1/47adf3a279954ef10492a265056915cc75d5682/logl-vertx-0.3.1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-jre/bd41a290787b5301e63929676d792c507bbc00ae/guava-27.0.1-jre.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.jnr/jnr-ffi/2.1.9/c465565992e8264012023c8b240e8175e235aa4e/jnr-ffi-2.1.9.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-engine/1.3.2/c54bc1d4654bd1ef15fccf512ce664184085969/junit-platform-engine-1.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.9.0/7c10d545325e3a6e72e06381afe469fd40eb701/jackson-annotations-2.9.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.7/4b7f0e0dc527fab032e9800ed231080fdc3ac015/jackson-core-2.9.7.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.logl/logl-api/0.3.1/4eee3a50fba1a4b8bbed52e769d429830e48b4e6/logl-api-0.3.1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.junit.platform/junit-platform-commons/1.3.2/378d1d1b162426ad031522f6d51e3bf28d1631a4/junit-platform-commons-1.3.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.apiguardian/apiguardian-api/1.0.0/3ef5276905e36f4d8055fe3cb0bdcc7503ffc85d/apiguardian-api-1.0.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.opentest4j/opentest4j/1.1.1/efd9f971e91074491ea55b19f67b13470cf4fcdd/opentest4j-1.1.1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-http2/4.1.30.Final/2da92f518409904954d3e8dcc42eb6a562a70302/netty-codec-http2-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-handler/4.1.30.Final/ecc076332ed103411347f4806a44ee32d9d9cb5f/netty-handler-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-handler-proxy/4.1.30.Final/1baa1568fa936caddca0fae96fdf127fd5cbad16/netty-handler-proxy-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-resolver-dns/4.1.30.Final/3f4bcf2e9fff1361ac9ad0bd27a10a1b31399294/netty-resolver-dns-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-http/4.1.30.Final/1384c630e8a0eeef33ad12a28791dce6e1d8767c/netty-codec-http-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-socks/4.1.30.Final/ea272e3bb281d3a91d27278f47e61b4de285cc27/netty-codec-socks-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec-dns/4.1.30.Final/7d28ce324f6cd5ae4ddd7f3e5027e2a7f126740b/netty-codec-dns-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-codec/4.1.30.Final/515c8f609aaca28a94f984d89a9667dd3359c1b1/netty-codec-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-transport/4.1.30.Final/3d27bb432a3b125167ac161b26415ad29ec17f02/netty-transport-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-buffer/4.1.30.Final/597adb653306470fb3ec1af3c0f3f30a37b1310a/netty-buffer-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-resolver/4.1.30.Final/5106fd687066ffd712e5295d32af4e2ac6482613/netty-resolver-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/io.netty/netty-common/4.1.30.Final/5dca0c34d8f38af51a2398614e81888f51cf811a/netty-common-4.1.30.Final.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0.1/1dcf1de382a0bf95a3d8b0849546c88bac1292c9/failureaccess-1.0.1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/b421526c5f297295adef1c886e5246c39d4ac629/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-qual/2.5.2/cea74543d5904a30861a61b4643a5f2bb372efc4/checker-qual-2.5.2.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.2.0/88e3c593e9b3586e1c6177f89267da6fc6986f0c/error_prone_annotations-2.2.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.jnr/jffi/1.2.17/19335d57d54d54680bdf5aa2fe92cd41f5cd29ee/jffi-1.2.17-native.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/976d8d30bebc251db406f2bdb3eb01962b5685b3/j2objc-annotations-1.1.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.codehaus.mojo/animal-sniffer-annotations/1.17/f97ce6decaea32b36101e37979f8b647f00681fb/animal-sniffer-annotations-1.17.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.jnr/jffi/1.2.17/6ffa5106be5a121bea9d0779cd757e804633e543/jffi-1.2.17.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/5.0.3/a7111830132c7f87d08fe48cb0ca07630f8cb91c/asm-commons-5.0.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/5.0.3/c7126aded0e8e13fed5f913559a0dd7b770a10f3/asm-analysis-5.0.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/5.0.3/1512e5571325854b05fb1efce1db75fcced54389/asm-util-5.0.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/5.0.3/287749b48ba7162fb67c93a026d690b29f410bed/asm-tree-5.0.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/5.0.3/dcc2193db20e19e1feca8b1240dbbc4e190824fa/asm-5.0.3.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.jnr/jnr-a64asm/1.0.0/a1cb8dbe71b5a6a0288043c3ba3ca64545be165/jnr-a64asm-1.0.0.jar:/home/happy0/.gradle/caches/modules-2/files-2.1/com.github.jnr/jnr-x86asm/1.0.2/6936bbd6c5b235665d87bd450f5e13b52d4b48/jnr-x86asm-1.0.2.jar:/home/happy0/projects/cava/crypto/out/production/classes:/home/happy0/projects/cava/junit/out/production/classes:/home/happy0/projects/cava/bytes/out/production/classes:/home/happy0/projects/cava/scuttlebutt/out/production/classes:/home/happy0/projects/cava/units/out/production/classes:/home/happy0/projects/cava/concurrent/out/production/classes:/home/happy0/projects/cava/io/out/production/classes:/home/happy0/projects/cava/scuttlebutt-handshake/out/production/classes com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit5 net.consensys.cava.scuttlebutt.rpc.PatchworkIntegrationTest,runWithPatchWork(io.vertx.core.Vertx)
Attempting RPC request...
Sending message?
Sending message?
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
Sending message?
We received a message?

Sending message?
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
Sending message?
2019-03-23 13:50:23.058+0000 DEBUG [localhost:8008] Failed to decrypt message header
net.consensys.cava.scuttlebutt.handshake.StreamException: Failed to decrypt message header
    at net.consensys.cava.scuttlebutt.handshake.SecureScuttlebuttStream.decryptMessage(SecureScuttlebuttStream.java:110)
    at net.consensys.cava.scuttlebutt.handshake.SecureScuttlebuttStream.decrypt(SecureScuttlebuttStream.java:85)
    at net.consensys.cava.scuttlebutt.handshake.SecureScuttlebuttStream.readFromServer(SecureScuttlebuttStream.java:53)
    at net.consensys.cava.scuttlebutt.handshake.vertx.SecureScuttlebuttVertxClient$NetSocketClientHandler.handle(SecureScuttlebuttVertxClient.java:91)
    at io.vertx.core.net.impl.NetSocketImpl$DataMessageHandler.handle(NetSocketImpl.java:392)
    at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:225)
    at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:123)
    at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:370)
    at io.vertx.core.net.impl.ConnectionBase.handleRead(ConnectionBase.java:386)
    at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
    at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:43)
    at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:188)
    at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:174)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)
Sending message?
Sending message?
Sending message?
Stream closed?

java.lang.IllegalStateException: Socket is closed

    at io.vertx.core.net.impl.NetSocketImpl.writeMessage(NetSocketImpl.java:114)
    at io.vertx.core.net.impl.NetSocketImpl.write(NetSocketImpl.java:159)
    at io.vertx.core.net.impl.NetSocketImpl.write(NetSocketImpl.java:137)
    at net.consensys.cava.scuttlebutt.handshake.vertx.SecureScuttlebuttVertxClient$NetSocketClientHandler.lambda$handle$2(SecureScuttlebuttVertxClient.java:84)
    at net.consensys.cava.scuttlebutt.rpc.PatchworkIntegrationTest$MyClientHandler.sendMessage(PatchworkIntegrationTest.java:85)
    at net.consensys.cava.scuttlebutt.rpc.PatchworkIntegrationTest.runWithPatchWork(PatchworkIntegrationTest.java:179)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Process finished with exit code 255

Sometime it succeeds:

Attempting RPC request...
Sending message?
Sending message?
Sending message?
Sending message?
We received a message?
   >����
Sending message?
Sending message?
We received a message?
{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
Sending message?
Sending message?
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
Sending message?
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
We received a message?
   >����{"id":"@jJPya8es0Cbioa+1l7lmxb8JXY8JNf1YF3qTdGC4YEo=.ed25519"}
atoulme commented 5 years ago

Some of the time, meaning we're having threading issues. This reminds me of what I do to for rlpx, when I had to synchronize getting messages and reading them, so the nonces (or digests) would not be consumed out of order.

So pretty much have to add synchronized blocks in the code. I can do that.

atoulme commented 5 years ago

Here is a fix: #200

Pretty sure most of the problems came from what happens if the message is properly consumed entirely and I didn't reset buffers then.

Happy0 commented 5 years ago

Thanks for looking into it, @atoulme =].

I still get to error sometimes after checking out that branch. I was wondering if this https://github.com/atoulme/cava/pull/2 might be a fix?

Rather than setting the buffer to Bytes.EMPTY, i think we should maybe be setting it to anything that remains in the buffer after we've processed the complete messages in the buffer?

    if (isClientToServer) {
      clientToServerBuffer = messageWithBuffer.slice(index);
    } else {
      serverToClientBuffer = messageWithBuffer.slice(index);
    }

This is tricky stuff! The test runs without error with the above, but I'm not sure if it's right.

atoulme commented 5 years ago

Yes, I just force pushed a change to that effect. Assuming build goes back green we are a go.

Happy0 commented 5 years ago

haha! the force push explains it. i looked at your original PR again and thought 'huh.. the code was already like that? and there's only one commit?')

Thanks @atoulme =]

atoulme commented 5 years ago

Force push is how I am never wrong, ever.

Happy0 commented 5 years ago

haha! mad with power, rewriting history

atoulme commented 5 years ago

Closing this issue.