diff --git a/eth_p2p/rlpx_protocols/shh_protocol.nim b/eth_p2p/rlpx_protocols/shh_protocol.nim index 331dd9b..87cae45 100644 --- a/eth_p2p/rlpx_protocols/shh_protocol.nim +++ b/eth_p2p/rlpx_protocols/shh_protocol.nim @@ -48,7 +48,7 @@ type src*: Option[PublicKey] ## If the message was signed, this is the public key ## of the source payload*: Bytes ## Application data / message contents - padding*: Option[Bytes] # XXX: to be added still + padding*: Option[Bytes] ## Message padding Envelope* = object ## What goes on the wire in the whisper protocol - a payload and some @@ -268,6 +268,7 @@ proc encode*(self: Payload): Option[Bytes] = let padLen = if self.padding.isSome(): self.padding.get().len + # is there a reason why 256 bytes are padded when the dataLen is 256? else: padMaxLen - (dataLen mod padMaxLen) # buffer space that we need to allocate @@ -397,6 +398,13 @@ proc decode*(data: openarray[byte], dst = none[PrivateKey](), return res.src = some(key) + if hasSignature: + if plain.len > pos + eth_keys.RawSignatureSize: + res.padding = some(plain[pos .. ^(eth_keys.RawSignatureSize+1)]) + else: + if plain.len > pos: + res.padding = some(plain[pos .. ^1]) + return some(res) # Envelopes -------------------------------------------------------------------- @@ -829,14 +837,14 @@ proc sendMessage*(node: EthereumNode, env: var Envelope): bool = return true -# XXX: add padding proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](), symKey = none[SymKey](), src = none[PrivateKey](), - ttl: uint32, topic: Topic, payload: Bytes, powTime = 1, + ttl: uint32, topic: Topic, payload: Bytes, + padding = none[Bytes](), powTime = 1, targetPeer = none[NodeId]()): bool = # NOTE: Allow a post without a key? Encryption is mandatory in v6? var payload = encode(Payload(payload: payload, src: src, dst: pubKey, - symKey: symKey)) + symKey: symKey, padding: padding)) if payload.isSome(): var env = Envelope(expiry:epochTime().uint32 + ttl + powTime.uint32, ttl: ttl, topic: topic, data: payload.get(), nonce: 0) diff --git a/tests/shh_basic_client.nim b/tests/shh_basic_client.nim index 024ed44..4ebb0f4 100644 --- a/tests/shh_basic_client.nim +++ b/tests/shh_basic_client.nim @@ -123,6 +123,8 @@ let encPublicKey = encPrivateKey.getPublicKey() let signPrivateKey = initPrivateKey("365bda0757d22212b04fada4b9222f8c3da59b49398fa04cf612481cd893b0a3") let signPublicKey = signPrivateKey.getPublicKey() var symKey: SymKey +# To test with geth: all 0's key is invalid in geth console +symKey[31] = 1 let topic = [byte 0x12, 0, 0, 0] if config.main: diff --git a/tests/tshh.nim b/tests/tshh.nim index 0b21216..5bdb2b1 100644 --- a/tests/tshh.nim +++ b/tests/tshh.nim @@ -19,8 +19,10 @@ suite "Whisper payload": let encoded = shh.encode(payload) let decoded = shh.decode(encoded.get()) - doAssert decoded.isSome() - doAssert payload.payload == decoded.get().payload + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.get().len == 251 # 256 -1 -1 -3 test "should roundtrip with symmetric encryption": var symKey: SymKey @@ -28,8 +30,10 @@ suite "Whisper payload": let encoded = shh.encode(payload) let decoded = shh.decode(encoded.get(), symKey = some(symKey)) - doAssert decoded.isSome() - doAssert payload.payload == decoded.get().payload + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.get().len == 251 # 256 -1 -1 -3 test "should roundtrip with signature": let privKey = eth_keys.newPrivateKey() @@ -38,9 +42,11 @@ suite "Whisper payload": let encoded = shh.encode(payload) let decoded = shh.decode(encoded.get()) - doAssert decoded.isSome() - doAssert payload.payload == decoded.get().payload - doAssert privKey.getPublicKey() == decoded.get().src.get() + check: + decoded.isSome() + payload.payload == decoded.get().payload + privKey.getPublicKey() == decoded.get().src.get() + decoded.get().padding.get().len == 186 # 256 -1 -1 -3 -65 test "should roundtrip with asymmetric encryption": let privKey = eth_keys.newPrivateKey() @@ -50,17 +56,122 @@ suite "Whisper payload": let encoded = shh.encode(payload) let decoded = shh.decode(encoded.get(), dst = some(privKey)) - doAssert decoded.isSome() - doAssert payload.payload == decoded.get().payload + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.get().len == 251 # 256 -1 -1 -3 - test "should roundtrip with asymmetric encryption": + test "should return specified bloom": # Geth test: https://github.com/ethersphere/go-ethereum/blob/d3441ebb563439bac0837d70591f92e2c6080303/whisper/whisperv6/whisper_test.go#L834 let top0 = [byte 0, 0, 255, 6] var x: Bloom x[0] = byte 1 x[32] = byte 1 x[^1] = byte 128 - doAssert @(top0.topicBloom) == @x + check @(top0.topicBloom) == @x + +suite "Whisper payload padding": + test "should do max padding": + let payload = Payload(payload: repeat(byte 1, 254)) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.isSome() + decoded.get().padding.get().len == 256 # as dataLen == 256 + + test "should do max padding with signature": + let privKey = eth_keys.newPrivateKey() + + let payload = Payload(src: some(privKey), payload: repeat(byte 1, 189)) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + privKey.getPublicKey() == decoded.get().src.get() + decoded.get().padding.isSome() + decoded.get().padding.get().len == 256 # as dataLen == 256 + + test "should do min padding": + let payload = Payload(payload: repeat(byte 1, 253)) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.isSome() + decoded.get().padding.get().len == 1 # as dataLen == 255 + + test "should do min padding with signature": + let privKey = eth_keys.newPrivateKey() + + let payload = Payload(src: some(privKey), payload: repeat(byte 1, 188)) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + privKey.getPublicKey() == decoded.get().src.get() + decoded.get().padding.isSome() + decoded.get().padding.get().len == 1 # as dataLen == 255 + + test "should roundtrip custom padding": + let payload = Payload(payload: repeat(byte 1, 10), + padding: some(repeat(byte 2, 100))) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.isSome() + payload.padding.get() == decoded.get().padding.get() + + test "should roundtrip custom 0 padding": + let padding: seq[byte] = @[] + let payload = Payload(payload: repeat(byte 1, 10), + padding: some(padding)) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + decoded.get().padding.isNone() + + test "should roundtrip custom padding with signature": + let privKey = eth_keys.newPrivateKey() + let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10), + padding: some(repeat(byte 2, 100))) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + privKey.getPublicKey() == decoded.get().src.get() + decoded.get().padding.isSome() + payload.padding.get() == decoded.get().padding.get() + + test "should roundtrip custom 0 padding with signature": + let padding: seq[byte] = @[] + let privKey = eth_keys.newPrivateKey() + let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10), + padding: some(padding)) + let encoded = shh.encode(payload) + + let decoded = shh.decode(encoded.get()) + check: + decoded.isSome() + payload.payload == decoded.get().payload + privKey.getPublicKey() == decoded.get().src.get() + decoded.get().padding.isNone() # example from https://github.com/paritytech/parity-ethereum/blob/93e1040d07e385d1219d00af71c46c720b0a1acf/whisper/src/message.rs#L439 let @@ -76,7 +187,7 @@ suite "Whisper envelope": # XXX checked with parity, should check with geth too - found a potential bug # in parity while playing with it: # https://github.com/paritytech/parity-ethereum/issues/9625 - doAssert $calcPowHash(env0) == + check $calcPowHash(env0) == "A13B48480AEB3123CD2358516E2E8EE9FCB0F4CB37E68CD09FDF7F9A7E14767C" suite "Whisper queue": @@ -86,22 +197,23 @@ suite "Whisper queue": let msg0 = initMessage(env0) let msg1 = initMessage(env1) - queue.add(msg0) - queue.add(msg1) + discard queue.add(msg0) + discard queue.add(msg1) - doAssert queue.items.len() == 1 - - doAssert queue.items[0].env.nonce == - (if msg0.pow > msg1.pow: msg0.env.nonce else: msg1.env.nonce) + check: + queue.items.len() == 1 + queue.items[0].env.nonce == + (if msg0.pow > msg1.pow: msg0.env.nonce else: msg1.env.nonce) test "should not throw out messages as long as there is capacity": var queue = initQueue(2) - queue.add(initMessage(env0)) - queue.add(initMessage(env1)) + check: + queue.add(initMessage(env0)) == true + queue.add(initMessage(env1)) == true - doAssert queue.items.len() == 2 + queue.items.len() == 2 test "check field order against expected rlp order": - doAssert rlp.encode(env0) == + check rlp.encode(env0) == rlp.encodeList(env0.expiry, env0.ttl, env0.topic, env0.data, env0.nonce)