mirror of https://github.com/status-im/nim-eth.git
use bearssl rng throughout (#265)
* use bearssl rng throughout * seeder can fail * imports and exports * modules, sigh * one more try * move var * even fewer thread vars * remove out-of-date genrated files
This commit is contained in:
parent
4f533eb5e6
commit
484fbcab1b
|
@ -15,3 +15,4 @@ build/
|
|||
*.la
|
||||
*.exe
|
||||
*.dll
|
||||
*.generated.nim
|
||||
|
|
53
eth/keys.nim
53
eth/keys.nim
|
@ -15,13 +15,13 @@
|
|||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
secp256k1,
|
||||
secp256k1, bearssl,
|
||||
nimcrypto/hash, nimcrypto/keccak,
|
||||
stew/[byteutils, objects, results], strformat
|
||||
|
||||
from nimcrypto/utils import burnMem
|
||||
|
||||
export secp256k1, results
|
||||
export secp256k1, results, bearssl
|
||||
|
||||
const
|
||||
KeyLength* = SkEcdhRawSecretSize - 1
|
||||
|
@ -46,12 +46,31 @@ type
|
|||
SharedSecret* = object
|
||||
data*: array[KeyLength, byte]
|
||||
|
||||
KeyPair* = object
|
||||
seckey*: PrivateKey
|
||||
pubkey*: PublicKey
|
||||
KeyPair* = distinct SkKeyPair
|
||||
|
||||
proc random*(T: type PrivateKey): SkResult[T] =
|
||||
SkSecretKey.random().mapConvert(T)
|
||||
template pubkey*(v: KeyPair): PublicKey = PublicKey(SkKeyPair(v).pubkey)
|
||||
template seckey*(v: KeyPair): PrivateKey = PrivateKey(SkKeyPair(v).seckey)
|
||||
|
||||
proc newRng*(): ref BrHmacDrbgContext =
|
||||
# You should only create one instance of the RNG per application / library
|
||||
# Ref is used so that it can be shared between components
|
||||
# TODO consider moving to bearssl
|
||||
var seeder = brPrngSeederSystem(nil)
|
||||
if seeder == nil:
|
||||
return nil
|
||||
|
||||
var rng = (ref BrHmacDrbgContext)()
|
||||
brHmacDrbgInit(addr rng[], addr sha256Vtable, nil, 0)
|
||||
if seeder(addr rng.vtable) == 0:
|
||||
return nil
|
||||
rng
|
||||
|
||||
proc random*(T: type PrivateKey, rng: var BrHmacDrbgContext): T =
|
||||
let rngPtr = unsafeAddr rng # doesn't escape
|
||||
proc callRng(data: var openArray[byte]) =
|
||||
brHmacDrbgGenerate(rngPtr[], data)
|
||||
|
||||
T(SkSecretKey.random(callRng))
|
||||
|
||||
func fromRaw*(T: type PrivateKey, data: openArray[byte]): SkResult[T] =
|
||||
SkSecretKey.fromRaw(data).mapConvert(T)
|
||||
|
@ -87,12 +106,16 @@ func toRaw*(pubkey: PublicKey): array[RawPublicKeySize, byte] =
|
|||
|
||||
func toRawCompressed*(pubkey: PublicKey): array[33, byte] {.borrow.}
|
||||
|
||||
proc random*(T: type KeyPair): SkResult[T] =
|
||||
let tmp = ? SkKeypair.random()
|
||||
ok(T(seckey: PrivateKey(tmp.seckey), pubkey: PublicKey(tmp.pubkey)))
|
||||
proc random*(T: type KeyPair, rng: var BrHmacDrbgContext): T =
|
||||
let seckey = SkSecretKey(PrivateKey.random(rng))
|
||||
KeyPair(SkKeyPair(
|
||||
seckey: seckey,
|
||||
pubkey: seckey.toPublicKey()
|
||||
))
|
||||
|
||||
func toKeyPair*(seckey: PrivateKey): KeyPair =
|
||||
KeyPair(seckey: seckey, pubkey: seckey.toPublicKey())
|
||||
KeyPair(SkKeyPair(
|
||||
seckey: SkSecretKey(seckey), pubkey: SkSecretKey(seckey).toPublicKey()))
|
||||
|
||||
func fromRaw*(T: type Signature, data: openArray[byte]): SkResult[T] =
|
||||
SkRecoverableSignature.fromRaw(data).mapConvert(T)
|
||||
|
@ -192,28 +215,28 @@ func sign*(seckey: PrivateKey, msg: SkMessage): Signature =
|
|||
|
||||
func sign*(seckey: PrivateKey, msg: openArray[byte]): Signature =
|
||||
let hash = keccak256.digest(msg)
|
||||
sign(seckey, hash)
|
||||
sign(seckey, SkMessage(hash.data))
|
||||
|
||||
func signNR*(seckey: PrivateKey, msg: SkMessage): SignatureNR =
|
||||
SignatureNR(sign(SkSecretKey(seckey), msg))
|
||||
|
||||
func signNR*(seckey: PrivateKey, msg: openArray[byte]): SignatureNR =
|
||||
let hash = keccak256.digest(msg)
|
||||
signNR(seckey, hash)
|
||||
signNR(seckey, SkMessage(hash.data))
|
||||
|
||||
func recover*(sig: Signature, msg: SkMessage): SkResult[PublicKey] =
|
||||
recover(SkRecoverableSignature(sig), msg).mapConvert(PublicKey)
|
||||
|
||||
func recover*(sig: Signature, msg: openArray[byte]): SkResult[PublicKey] =
|
||||
let hash = keccak256.digest(msg)
|
||||
recover(sig, hash)
|
||||
recover(sig, SkMessage(hash.data))
|
||||
|
||||
func verify*(sig: SignatureNR, msg: SkMessage, key: PublicKey): bool =
|
||||
verify(SkSignature(sig), msg, SkPublicKey(key))
|
||||
|
||||
func verify*(sig: SignatureNR, msg: openArray[byte], key: PublicKey): bool =
|
||||
let hash = keccak256.digest(msg)
|
||||
verify(sig, hash, key)
|
||||
verify(sig, SkMessage(hash.data), key)
|
||||
|
||||
func ecdhRaw*(seckey: PrivateKey, pubkey: PublicKey): SharedSecret =
|
||||
let tmp = ecdhRaw(SkSecretKey(seckey), SkPublicKey(pubkey))
|
||||
|
|
10
eth/p2p.nim
10
eth/p2p.nim
|
@ -9,7 +9,7 @@
|
|||
#
|
||||
|
||||
import
|
||||
tables, algorithm, random,
|
||||
tables, algorithm, random, bearssl,
|
||||
chronos, chronos/timer, chronicles,
|
||||
eth/keys, eth/common/eth_types,
|
||||
eth/p2p/[kademlia, discovery, enode, peer_pool, rlpx],
|
||||
|
@ -38,7 +38,12 @@ proc newEthereumNode*(keys: KeyPair,
|
|||
clientId = "nim-eth-p2p/0.2.0", # TODO: read this value from nimble somehow
|
||||
addAllCapabilities = true,
|
||||
useCompression: bool = false,
|
||||
minPeers = 10): EthereumNode =
|
||||
minPeers = 10,
|
||||
rng = newRng()): EthereumNode =
|
||||
|
||||
if rng == nil: # newRng could fail
|
||||
raise (ref CatchableError)(msg: "Cannot initialize RNG")
|
||||
|
||||
new result
|
||||
result.keys = keys
|
||||
result.networkId = networkId
|
||||
|
@ -47,6 +52,7 @@ proc newEthereumNode*(keys: KeyPair,
|
|||
result.capabilities.newSeq 0
|
||||
result.address = address
|
||||
result.connectionState = ConnectionState.None
|
||||
result.rng = rng
|
||||
|
||||
when useSnappy:
|
||||
result.protocolVersion = if useCompression: devp2pSnappyVersion
|
||||
|
|
108
eth/p2p/auth.nim
108
eth/p2p/auth.nim
|
@ -12,7 +12,7 @@
|
|||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import eth/[keys, rlp], nimcrypto
|
||||
import eth/[keys, rlp], nimcrypto/[rijndael, keccak, utils], bearssl
|
||||
import ecies
|
||||
import stew/[byteutils, endians2, objects, results]
|
||||
|
||||
|
@ -54,7 +54,6 @@ type
|
|||
Eip8 ## Flag indicates that EIP-8 handshake is used
|
||||
|
||||
AuthError* = enum
|
||||
RandomError = "auth: could not obtain random data"
|
||||
EcdhError = "auth: ECDH shared secret could not be calculated"
|
||||
BufferOverrun = "auth: buffer overrun"
|
||||
SignatureError = "auth: signature could not be obtained"
|
||||
|
@ -95,7 +94,8 @@ proc mapErrTo[T, E](r: Result[T, E], v: static AuthError): AuthResult[T] =
|
|||
r.mapErr(proc (e: E): AuthError = v)
|
||||
|
||||
proc tryInit*(
|
||||
T: type Handshake, host: KeyPair, flags: set[HandshakeFlag] = {Initiator},
|
||||
T: type Handshake, rng: var BrHmacDrbgContext, host: KeyPair,
|
||||
flags: set[HandshakeFlag] = {Initiator},
|
||||
version: uint8 = SupportedRlpxVersion): AuthResult[T] =
|
||||
## Create new `Handshake` object.
|
||||
|
||||
|
@ -103,16 +103,14 @@ proc tryInit*(
|
|||
initiatorNonce: Nonce
|
||||
responderNonce: Nonce
|
||||
expectedLength: int
|
||||
ephemeral = ? KeyPair.random().mapErrTo(RandomError)
|
||||
ephemeral = KeyPair.random(rng)
|
||||
|
||||
if Initiator in flags:
|
||||
expectedLength = AckMessageV4Length
|
||||
if randomBytes(initiatorNonce) != len(initiatorNonce):
|
||||
return err(RandomError)
|
||||
brHmacDrbgGenerate(rng, initiatorNonce)
|
||||
else:
|
||||
expectedLength = AuthMessageV4Length
|
||||
if randomBytes(responderNonce) != len(responderNonce):
|
||||
return err(RandomError)
|
||||
brHmacDrbgGenerate(rng, responderNonce)
|
||||
|
||||
return ok(T(
|
||||
version: version,
|
||||
|
@ -125,6 +123,7 @@ proc tryInit*(
|
|||
))
|
||||
|
||||
proc authMessagePreEIP8(h: var Handshake,
|
||||
rng: var BrHmacDrbgContext,
|
||||
pubkey: PublicKey,
|
||||
output: var openarray[byte],
|
||||
outlen: var int,
|
||||
|
@ -137,12 +136,11 @@ proc authMessagePreEIP8(h: var Handshake,
|
|||
let header = cast[ptr AuthMessageV4](addr buffer[0])
|
||||
|
||||
var secret = ecdhRaw(h.host.seckey, pubkey)
|
||||
let xornonce = secret.data xor h.initiatorNonce
|
||||
secret.data = secret.data xor h.initiatorNonce
|
||||
|
||||
let signature = sign(h.ephemeral.seckey, SkMessage(secret.data))
|
||||
secret.clear()
|
||||
|
||||
let signature = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
|
||||
|
||||
h.remoteHPubkey = pubkey
|
||||
header.signature = signature.toRaw()
|
||||
header.keyhash = keccak256.digest(h.ephemeral.pubkey.toRaw()).data
|
||||
|
@ -152,7 +150,7 @@ proc authMessagePreEIP8(h: var Handshake,
|
|||
if encrypt:
|
||||
if len(output) < AuthMessageV4Length:
|
||||
return err(BufferOverrun)
|
||||
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr:
|
||||
if eciesEncrypt(rng, buffer, output, h.remoteHPubkey).isErr:
|
||||
return err(EciesError)
|
||||
outlen = AuthMessageV4Length
|
||||
else:
|
||||
|
@ -164,6 +162,7 @@ proc authMessagePreEIP8(h: var Handshake,
|
|||
ok()
|
||||
|
||||
proc authMessageEIP8(h: var Handshake,
|
||||
rng: var BrHmacDrbgContext,
|
||||
pubkey: PublicKey,
|
||||
output: var openarray[byte],
|
||||
outlen: var int,
|
||||
|
@ -172,50 +171,49 @@ proc authMessageEIP8(h: var Handshake,
|
|||
## Create EIP8 authentication message.
|
||||
var
|
||||
buffer: array[PlainAuthMessageMaxEIP8, byte]
|
||||
padsize: byte
|
||||
padsize: array[1, byte]
|
||||
|
||||
doAssert(EIP8 in h.flags)
|
||||
outlen = 0
|
||||
var
|
||||
secret = ecdhRaw(h.host.seckey, pubkey)
|
||||
xornonce = secret.data xor h.initiatorNonce
|
||||
|
||||
var secret = ecdhRaw(h.host.seckey, pubkey)
|
||||
secret.data = secret.data xor h.initiatorNonce
|
||||
|
||||
let signature = sign(h.ephemeral.seckey, SkMessage(secret.data))
|
||||
secret.clear()
|
||||
|
||||
let signature = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
|
||||
|
||||
h.remoteHPubkey = pubkey
|
||||
var payload = rlp.encodeList(signature.toRaw(),
|
||||
h.host.pubkey.toRaw(),
|
||||
h.initiatorNonce,
|
||||
[byte(h.version)])
|
||||
doAssert(len(payload) == PlainAuthMessageEIP8Length)
|
||||
let pencsize = eciesEncryptedLength(len(payload))
|
||||
let
|
||||
pencsize = eciesEncryptedLength(len(payload))
|
||||
|
||||
while true:
|
||||
if randomBytes(addr padsize, 1) != 1:
|
||||
return err(RandomError)
|
||||
if int(padsize) > (AuthMessageV4Length - (pencsize + 2)):
|
||||
brHmacDrbgGenerate(rng, padsize)
|
||||
if int(padsize[0]) > (AuthMessageV4Length - (pencsize + 2)):
|
||||
break
|
||||
# It is possible to make packet size constant by uncommenting this line
|
||||
# padsize = 24
|
||||
let wosize = pencsize + int(padsize)
|
||||
let wosize = pencsize + int(padsize[0])
|
||||
let fullsize = wosize + 2
|
||||
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length,
|
||||
int(padsize))) != int(padsize):
|
||||
return err(RandomError)
|
||||
brHmacDrbgGenerate(
|
||||
rng, toa(buffer, PlainAuthMessageEIP8Length, int(padsize[0])))
|
||||
if encrypt:
|
||||
copyMem(addr buffer[0], addr payload[0], len(payload))
|
||||
if len(output) < fullsize:
|
||||
return err(BufferOverrun)
|
||||
let wosizeBE = uint16(wosize).toBytesBE()
|
||||
output[0..<2] = wosizeBE
|
||||
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
||||
if eciesEncrypt(rng, toa(buffer, 0, len(payload) + int(padsize[0])),
|
||||
toa(output, 2, wosize), pubkey,
|
||||
toa(output, 0, 2)).isErr:
|
||||
return err(EciesError)
|
||||
outlen = fullsize
|
||||
else:
|
||||
let plainsize = len(payload) + int(padsize)
|
||||
let plainsize = len(payload) + int(padsize[0])
|
||||
if len(output) < plainsize:
|
||||
return err(BufferOverrun)
|
||||
copyMem(addr output[0], addr buffer[0], plainsize)
|
||||
|
@ -224,6 +222,7 @@ proc authMessageEIP8(h: var Handshake,
|
|||
ok()
|
||||
|
||||
proc ackMessagePreEIP8(h: var Handshake,
|
||||
rng: var BrHmacDrbgContext,
|
||||
output: var openarray[byte],
|
||||
outlen: var int,
|
||||
flag: byte = 0,
|
||||
|
@ -238,7 +237,7 @@ proc ackMessagePreEIP8(h: var Handshake,
|
|||
if encrypt:
|
||||
if len(output) < AckMessageV4Length:
|
||||
return err(BufferOverrun)
|
||||
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr:
|
||||
if eciesEncrypt(rng, buffer, output, h.remoteHPubkey).isErr:
|
||||
return err(EciesError)
|
||||
outlen = AckMessageV4Length
|
||||
else:
|
||||
|
@ -250,6 +249,7 @@ proc ackMessagePreEIP8(h: var Handshake,
|
|||
ok()
|
||||
|
||||
proc ackMessageEIP8(h: var Handshake,
|
||||
rng: var BrHmacDrbgContext,
|
||||
output: var openarray[byte],
|
||||
outlen: var int,
|
||||
flag: byte = 0,
|
||||
|
@ -257,7 +257,7 @@ proc ackMessageEIP8(h: var Handshake,
|
|||
## Create EIP8 authentication ack message.
|
||||
var
|
||||
buffer: array[PlainAckMessageMaxEIP8, byte]
|
||||
padsize: byte
|
||||
padsize: array[1, byte]
|
||||
doAssert(EIP8 in h.flags)
|
||||
var payload = rlp.encodeList(h.ephemeral.pubkey.toRaw(),
|
||||
h.responderNonce,
|
||||
|
@ -266,30 +266,29 @@ proc ackMessageEIP8(h: var Handshake,
|
|||
outlen = 0
|
||||
let pencsize = eciesEncryptedLength(len(payload))
|
||||
while true:
|
||||
if randomBytes(addr padsize, 1) != 1:
|
||||
return err(RandomError)
|
||||
if int(padsize) > (AckMessageV4Length - (pencsize + 2)):
|
||||
brHmacDrbgGenerate(rng, padsize)
|
||||
if int(padsize[0]) > (AckMessageV4Length - (pencsize + 2)):
|
||||
break
|
||||
# It is possible to make packet size constant by uncommenting this line
|
||||
# padsize = 0
|
||||
let wosize = pencsize + int(padsize)
|
||||
let wosize = pencsize + int(padsize[0])
|
||||
let fullsize = wosize + 2
|
||||
if int(padsize) > 0:
|
||||
if randomBytes(toa(buffer, PlainAckMessageEIP8Length,
|
||||
int(padsize))) != int(padsize):
|
||||
return err(RandomError)
|
||||
if int(padsize[0]) > 0:
|
||||
brHmacDrbgGenerate(
|
||||
rng, toa(buffer, PlainAckMessageEIP8Length, int(padsize[0])))
|
||||
|
||||
copyMem(addr buffer[0], addr payload[0], len(payload))
|
||||
if encrypt:
|
||||
if len(output) < fullsize:
|
||||
return err(BufferOverrun)
|
||||
output[0..<2] = uint16(wosize).toBytesBE()
|
||||
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
||||
if eciesEncrypt(rng, toa(buffer, 0, len(payload) + int(padsize[0])),
|
||||
toa(output, 2, wosize), h.remoteHPubkey,
|
||||
toa(output, 0, 2)).isErr:
|
||||
return err(EciesError)
|
||||
outlen = fullsize
|
||||
else:
|
||||
let plainsize = len(payload) + int(padsize)
|
||||
let plainsize = len(payload) + int(padsize[0])
|
||||
if len(output) < plainsize:
|
||||
return err(BufferOverrun)
|
||||
copyMem(addr output[0], addr buffer[0], plainsize)
|
||||
|
@ -311,26 +310,28 @@ template ackSize*(h: Handshake, encrypt: bool = true): int =
|
|||
else:
|
||||
if encrypt: (AckMessageV4Length) else: (PlainAckMessageV4Length)
|
||||
|
||||
proc authMessage*(h: var Handshake, pubkey: PublicKey,
|
||||
proc authMessage*(h: var Handshake, rng: var BrHmacDrbgContext,
|
||||
pubkey: PublicKey,
|
||||
output: var openarray[byte],
|
||||
outlen: var int, flag: byte = 0,
|
||||
encrypt: bool = true): AuthResult[void] =
|
||||
## Create new AuthMessage for specified `pubkey` and store it inside
|
||||
## of `output`, size of generated AuthMessage will stored in `outlen`.
|
||||
if EIP8 in h.flags:
|
||||
authMessageEIP8(h, pubkey, output, outlen, flag, encrypt)
|
||||
authMessageEIP8(h, rng, pubkey, output, outlen, flag, encrypt)
|
||||
else:
|
||||
authMessagePreEIP8(h, pubkey, output, outlen, flag, encrypt)
|
||||
authMessagePreEIP8(h, rng, pubkey, output, outlen, flag, encrypt)
|
||||
|
||||
proc ackMessage*(h: var Handshake, output: var openarray[byte],
|
||||
proc ackMessage*(h: var Handshake, rng: var BrHmacDrbgContext,
|
||||
output: var openarray[byte],
|
||||
outlen: var int, flag: byte = 0,
|
||||
encrypt: bool = true): AuthResult[void] =
|
||||
## Create new AckMessage and store it inside of `output`, size of generated
|
||||
## AckMessage will stored in `outlen`.
|
||||
if EIP8 in h.flags:
|
||||
ackMessageEIP8(h, output, outlen, flag, encrypt)
|
||||
ackMessageEIP8(h, rng, output, outlen, flag, encrypt)
|
||||
else:
|
||||
ackMessagePreEIP8(h, output, outlen, flag, encrypt)
|
||||
ackMessagePreEIP8(h, rng, output, outlen, flag, encrypt)
|
||||
|
||||
proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void] =
|
||||
## Decodes V4 AuthMessage.
|
||||
|
@ -347,12 +348,12 @@ proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void]
|
|||
signature = ? Signature.fromRaw(header.signature).mapErrTo(SignatureError)
|
||||
|
||||
var secret = ecdhRaw(h.host.seckey, pubkey)
|
||||
let xornonce = secret.data xor header.nonce
|
||||
secret.data = secret.data xor header.nonce
|
||||
|
||||
var recovered = recover(signature, SkMessage(secret.data))
|
||||
secret.clear()
|
||||
|
||||
h.remoteEPubkey =
|
||||
? recover(signature, SkMessage(data: xornonce)).mapErrTo(SignatureError)
|
||||
h.remoteEPubkey = ? recovered.mapErrTo(SignatureError)
|
||||
h.initiatorNonce = header.nonce
|
||||
h.remoteHPubkey = pubkey
|
||||
|
||||
|
@ -392,13 +393,12 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthResult[voi
|
|||
nonce = toArray(KeyLength, nonceBr)
|
||||
|
||||
var secret = ecdhRaw(h.host.seckey, pubkey)
|
||||
secret.data = secret.data xor nonce
|
||||
|
||||
let xornonce = nonce xor secret.data
|
||||
let recovered = recover(signature, SkMessage(secret.data))
|
||||
secret.clear()
|
||||
|
||||
h.remoteEPubkey =
|
||||
? recover(signature, SkMessage(data: xornonce)).mapErrTo(SignatureError)
|
||||
|
||||
h.remoteEPubkey = ? recovered.mapErrTo(SignatureError)
|
||||
h.initiatorNonce = nonce
|
||||
h.remoteHPubkey = pubkey
|
||||
h.version = versionBr[0]
|
||||
|
@ -520,7 +520,7 @@ proc getSecrets*(
|
|||
mac1 = ctx0.finish()
|
||||
secret.macKey = mac1.data
|
||||
|
||||
burnMem(shsec)
|
||||
clear(shsec)
|
||||
|
||||
# egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import
|
||||
times,
|
||||
chronos, stint, nimcrypto, chronicles,
|
||||
chronos, stint, nimcrypto/keccak, chronicles, bearssl,
|
||||
eth/[keys, rlp],
|
||||
kademlia, enode,
|
||||
stew/[objects, results]
|
||||
|
@ -155,7 +155,7 @@ proc sendNeighbours*(d: DiscoveryProtocol, node: Node, neighbours: seq[Node]) =
|
|||
if nodes.len != 0: flush()
|
||||
|
||||
proc newDiscoveryProtocol*(privKey: PrivateKey, address: Address,
|
||||
bootstrapNodes: openarray[ENode]
|
||||
bootstrapNodes: openarray[ENode], rng = newRng()
|
||||
): DiscoveryProtocol =
|
||||
result.new()
|
||||
result.privKey = privKey
|
||||
|
@ -163,7 +163,7 @@ proc newDiscoveryProtocol*(privKey: PrivateKey, address: Address,
|
|||
result.bootstrapNodes = newSeqOfCap[Node](bootstrapNodes.len)
|
||||
for n in bootstrapNodes: result.bootstrapNodes.add(newNode(n))
|
||||
result.thisNode = newNode(privKey.toPublicKey(), address)
|
||||
result.kademlia = newKademliaProtocol(result.thisNode, result)
|
||||
result.kademlia = newKademliaProtocol(result.thisNode, result, rng = rng)
|
||||
|
||||
proc recvPing(d: DiscoveryProtocol, node: Node,
|
||||
msgHash: MDigest[256]) {.inline.} =
|
||||
|
|
|
@ -28,7 +28,7 @@ proc makeKey(id: NodeId, address: Address): array[keySize, byte] =
|
|||
of IpAddressFamily.IpV4:
|
||||
result[pos ..< pos+sizeof(address.ip.address_v4)] = address.ip.address_v4
|
||||
of IpAddressFamily.IpV6:
|
||||
result[pos..< pos+sizeof(address.ip.address_v6)] = address.ip.address_v6
|
||||
result[pos ..< pos+sizeof(address.ip.address_v6)] = address.ip.address_v6
|
||||
pos.inc(sizeof(address.ip.address_v6))
|
||||
result[pos ..< pos+sizeof(address.port)] = toBytes(address.port.uint16)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import
|
||||
std/[tables, options], nimcrypto, stint, chronicles, stew/results,
|
||||
types, node, enr, hkdf, eth/[rlp, keys]
|
||||
types, node, enr, hkdf, eth/[rlp, keys], bearssl
|
||||
|
||||
export keys
|
||||
|
||||
|
@ -64,7 +64,7 @@ proc idNonceHash(nonce, ephkey: openarray[byte]): MDigest[256] =
|
|||
|
||||
proc signIDNonce*(privKey: PrivateKey, idNonce, ephKey: openarray[byte]):
|
||||
SignatureNR =
|
||||
signNR(privKey, idNonceHash(idNonce, ephKey))
|
||||
signNR(privKey, SkMessage(idNonceHash(idNonce, ephKey).data))
|
||||
|
||||
proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey,
|
||||
idNonce: openarray[byte]): HandshakeSecrets =
|
||||
|
@ -89,11 +89,12 @@ proc encryptGCM*(key, nonce, pt, authData: openarray[byte]): seq[byte] =
|
|||
ectx.getTag(result.toOpenArray(pt.len, result.high))
|
||||
ectx.clear()
|
||||
|
||||
proc encodeAuthHeader*(c: Codec,
|
||||
proc encodeAuthHeader*(rng: var BrHmacDrbgContext,
|
||||
c: Codec,
|
||||
toId: NodeID,
|
||||
nonce: array[gcmNonceSize, byte],
|
||||
challenge: Whoareyou):
|
||||
EncodeResult[(seq[byte], HandshakeSecrets)] =
|
||||
(seq[byte], HandshakeSecrets) =
|
||||
var resp = AuthResponse(version: 5)
|
||||
let ln = c.localNode
|
||||
|
||||
|
@ -101,7 +102,7 @@ proc encodeAuthHeader*(c: Codec,
|
|||
if challenge.recordSeq < ln.record.seqNum:
|
||||
resp.record = ln.record
|
||||
|
||||
let ephKeys = ? KeyPair.random()
|
||||
let ephKeys = KeyPair.random(rng)
|
||||
let signature = signIDNonce(c.privKey, challenge.idNonce,
|
||||
ephKeys.pubkey.toRaw)
|
||||
resp.signature = signature.toRaw
|
||||
|
@ -117,7 +118,7 @@ proc encodeAuthHeader*(c: Codec,
|
|||
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce,
|
||||
scheme: authSchemeName, ephemeralKey: ephKeys.pubkey.toRaw,
|
||||
response: respEnc)
|
||||
ok((rlp.encode(header), secrets))
|
||||
(rlp.encode(header), secrets)
|
||||
|
||||
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] =
|
||||
for i in 0 .. a.high:
|
||||
|
@ -130,15 +131,16 @@ proc packetTag(destNode, srcNode: NodeID): PacketTag =
|
|||
destidHash = sha256.digest(destId)
|
||||
result = srcId xor destidHash.data
|
||||
|
||||
proc encodePacket*(c: Codec,
|
||||
proc encodePacket*(
|
||||
rng: var BrHmacDrbgContext,
|
||||
c: Codec,
|
||||
toId: NodeID,
|
||||
toAddr: Address,
|
||||
message: openarray[byte],
|
||||
challenge: Whoareyou):
|
||||
EncodeResult[(seq[byte], array[gcmNonceSize, byte])] =
|
||||
(seq[byte], array[gcmNonceSize, byte]) =
|
||||
var nonce: array[gcmNonceSize, byte]
|
||||
if randomBytes(nonce) != nonce.len:
|
||||
return err("Could not randomize bytes")
|
||||
brHmacDrbgGenerate(rng, nonce)
|
||||
|
||||
var headEnc: seq[byte]
|
||||
|
||||
|
@ -153,7 +155,7 @@ proc encodePacket*(c: Codec,
|
|||
discard c.db.loadKeys(toId, toAddr, readKey, writeKey)
|
||||
else:
|
||||
var secrets: HandshakeSecrets
|
||||
(headEnc, secrets) = ? c.encodeAuthHeader(toId, nonce, challenge)
|
||||
(headEnc, secrets) = encodeAuthHeader(rng, c, toId, nonce, challenge)
|
||||
|
||||
writeKey = secrets.writeKey
|
||||
# TODO: is it safe to ignore the error here?
|
||||
|
@ -165,7 +167,7 @@ proc encodePacket*(c: Codec,
|
|||
packet.add(tag)
|
||||
packet.add(headEnc)
|
||||
packet.add(encryptGCM(writeKey, nonce, message, tag))
|
||||
ok((packet, nonce))
|
||||
(packet, nonce)
|
||||
|
||||
proc decryptGCM*(key: AesKey, nonce, ct, authData: openarray[byte]):
|
||||
Option[seq[byte]] =
|
||||
|
@ -260,7 +262,7 @@ proc decodeAuthResp*(c: Codec, fromId: NodeId, head: AuthHeader,
|
|||
# Verify the id-nonce-sig
|
||||
let sig = ? SignatureNR.fromRaw(authResp.signature).mapErrTo(HandshakeError)
|
||||
let h = idNonceHash(head.idNonce, head.ephemeralKey)
|
||||
if verify(sig, h, newNode.pubkey):
|
||||
if verify(sig, SkMessage(h.data), newNode.pubkey):
|
||||
ok(secrets)
|
||||
else:
|
||||
err(HandshakeError)
|
||||
|
@ -332,12 +334,12 @@ proc decodePacket*(c: var Codec,
|
|||
|
||||
decodeMessage(message.get())
|
||||
|
||||
proc newRequestId*(): Result[RequestId, cstring] =
|
||||
var id: RequestId
|
||||
if randomBytes(addr id, sizeof(id)) != sizeof(id):
|
||||
err("Could not randomize bytes")
|
||||
else:
|
||||
ok(id)
|
||||
proc init*(T: type RequestId, rng: var BrHmacDrbgContext): T =
|
||||
var buf: array[sizeof(T), byte]
|
||||
brHmacDrbgGenerate(rng, buf)
|
||||
var id: T
|
||||
copyMem(addr id, addr buf[0], sizeof(id))
|
||||
id
|
||||
|
||||
proc numFields(T: typedesc): int =
|
||||
for k, v in fieldPairs(default(T)): inc result
|
||||
|
|
|
@ -232,7 +232,7 @@ proc verifySignatureV4(r: Record, sigData: openarray[byte], content: seq[byte]):
|
|||
let sig = SignatureNR.fromRaw(sigData)
|
||||
if sig.isOk:
|
||||
var h = keccak256.digest(content)
|
||||
return verify(sig[], h, publicKey.get)
|
||||
return verify(sig[], SkMessage(h.data), publicKey.get)
|
||||
|
||||
proc verifySignature(r: Record): bool {.raises: [RlpError, Defect].} =
|
||||
var rlp = rlpFromBytes(r.raw)
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
## This might be a concern for mobile devices.
|
||||
|
||||
import
|
||||
std/[tables, sets, options, math, random],
|
||||
std/[tables, sets, options, math, random], bearssl,
|
||||
stew/shims/net as stewNet, json_serialization/std/net,
|
||||
stew/[byteutils, endians2], chronicles, chronos, stint,
|
||||
eth/[rlp, keys, async_utils], types, encoding, node, routing_table, enr
|
||||
|
@ -120,6 +120,7 @@ type
|
|||
lookupLoop: Future[void]
|
||||
revalidateLoop: Future[void]
|
||||
bootstrapRecords*: seq[Record]
|
||||
rng*: ref BrHmacDrbgContext
|
||||
|
||||
PendingRequest = object
|
||||
node: Node
|
||||
|
@ -222,9 +223,7 @@ proc sendWhoareyou(d: Protocol, address: Address, toNode: NodeId,
|
|||
authTag: AuthTag): DiscResult[void] {.raises: [Exception, Defect].} =
|
||||
trace "sending who are you", to = $toNode, toAddress = $address
|
||||
let challenge = Whoareyou(authTag: authTag, recordSeq: 0)
|
||||
|
||||
if randomBytes(challenge.idNonce) != challenge.idNonce.len:
|
||||
return err("Could not randomize bytes")
|
||||
brHmacDrbgGenerate(d.rng[], challenge.idNonce)
|
||||
|
||||
# If there is already a handshake going on for this nodeid then we drop this
|
||||
# new one. Handshake will get cleaned up after `handshakeTimeout`.
|
||||
|
@ -250,17 +249,18 @@ proc sendWhoareyou(d: Protocol, address: Address, toNode: NodeId,
|
|||
err("NodeId already has ongoing handshake")
|
||||
|
||||
proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address, reqId: RequestId,
|
||||
nodes: openarray[Node]): DiscResult[void] =
|
||||
nodes: openarray[Node]) =
|
||||
proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address,
|
||||
message: NodesMessage, reqId: RequestId): DiscResult[void] {.nimcall.} =
|
||||
let (data, _) = ? d.codec.encodePacket(toId, toAddr,
|
||||
message: NodesMessage, reqId: RequestId) {.nimcall.} =
|
||||
let (data, _) = encodePacket(
|
||||
d.rng[], d.codec, toId, toAddr,
|
||||
encodeMessage(message, reqId), challenge = nil)
|
||||
d.send(toAddr, data)
|
||||
ok()
|
||||
|
||||
if nodes.len == 0:
|
||||
# In case of 0 nodes, a reply is still needed
|
||||
return d.sendNodes(toId, toAddr, NodesMessage(total: 1, enrs: @[]), reqId)
|
||||
d.sendNodes(toId, toAddr, NodesMessage(total: 1, enrs: @[]), reqId)
|
||||
return
|
||||
|
||||
var message: NodesMessage
|
||||
# TODO: Do the total calculation based on the max UDP packet size we want to
|
||||
|
@ -271,19 +271,14 @@ proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address, reqId: RequestId,
|
|||
for i in 0 ..< nodes.len:
|
||||
message.enrs.add(nodes[i].record)
|
||||
if message.enrs.len == maxNodesPerMessage:
|
||||
let res = d.sendNodes(toId, toAddr, message, reqId)
|
||||
if res.isErr: # TODO: is there something nicer for this?
|
||||
return res
|
||||
d.sendNodes(toId, toAddr, message, reqId)
|
||||
message.enrs.setLen(0)
|
||||
|
||||
if message.enrs.len != 0:
|
||||
let res = d.sendNodes(toId, toAddr, message, reqId)
|
||||
if res.isErr: # TODO: is there something nicer for this?
|
||||
return res
|
||||
ok()
|
||||
d.sendNodes(toId, toAddr, message, reqId)
|
||||
|
||||
proc handlePing(d: Protocol, fromId: NodeId, fromAddr: Address,
|
||||
ping: PingMessage, reqId: RequestId): DiscResult[void] =
|
||||
ping: PingMessage, reqId: RequestId) =
|
||||
let a = fromAddr
|
||||
var pong: PongMessage
|
||||
pong.enrSeq = ping.enrSeq
|
||||
|
@ -292,14 +287,13 @@ proc handlePing(d: Protocol, fromId: NodeId, fromAddr: Address,
|
|||
of IpAddressFamily.IPv6: @(a.ip.address_v6)
|
||||
pong.port = a.port.uint16
|
||||
|
||||
let (data, _) = ? d.codec.encodePacket(fromId, fromAddr,
|
||||
let (data, _) = encodePacket(d.rng[], d.codec, fromId, fromAddr,
|
||||
encodeMessage(pong, reqId), challenge = nil)
|
||||
|
||||
d.send(fromAddr, data)
|
||||
ok()
|
||||
|
||||
proc handleFindNode(d: Protocol, fromId: NodeId, fromAddr: Address,
|
||||
fn: FindNodeMessage, reqId: RequestId): DiscResult[void] =
|
||||
fn: FindNodeMessage, reqId: RequestId) =
|
||||
if fn.distance == 0:
|
||||
d.sendNodes(fromId, fromAddr, reqId, [d.localNode])
|
||||
else:
|
||||
|
@ -333,14 +327,8 @@ proc receive*(d: Protocol, a: Address, packet: openArray[byte]) {.gcsafe,
|
|||
let toNode = pr.node
|
||||
whoareyou.pubKey = toNode.pubkey # TODO: Yeah, rather ugly this.
|
||||
doAssert(toNode.address.isSome())
|
||||
let encoded = d.codec.encodePacket(toNode.id, toNode.address.get(),
|
||||
let (data, _) = encodePacket(d.rng[], d.codec, toNode.id, toNode.address.get(),
|
||||
pr.message, challenge = whoareyou)
|
||||
# TODO: Perhaps just expect here? Or raise Defect in `encodePacket`?
|
||||
# if this occurs there is an issue with the system anyhow?
|
||||
if encoded.isErr:
|
||||
warn "Not enough randomness to encode packet"
|
||||
return
|
||||
let (data, _) = encoded[]
|
||||
d.send(toNode, data)
|
||||
else:
|
||||
debug "Timed out or unrequested WhoAreYou packet"
|
||||
|
@ -366,11 +354,9 @@ proc receive*(d: Protocol, a: Address, packet: openArray[byte]) {.gcsafe,
|
|||
|
||||
case message.kind
|
||||
of ping:
|
||||
if d.handlePing(sender, a, message.ping, message.reqId).isErr:
|
||||
debug "Sending Pong message failed"
|
||||
d.handlePing(sender, a, message.ping, message.reqId)
|
||||
of findNode:
|
||||
if d.handleFindNode(sender, a, message.findNode, message.reqId).isErr:
|
||||
debug "Sending Nodes message failed"
|
||||
d.handleFindNode(sender, a, message.findNode, message.reqId)
|
||||
else:
|
||||
var waiter: Future[Option[Message]]
|
||||
if d.awaitedMessages.take((sender, message.reqId), waiter):
|
||||
|
@ -518,24 +504,22 @@ proc waitNodes(d: Protocol, fromNode: Node, reqId: RequestId):
|
|||
return err("Nodes message not received in time")
|
||||
|
||||
proc sendMessage*[T: SomeMessage](d: Protocol, toNode: Node, m: T):
|
||||
DiscResult[RequestId] {.raises: [Exception, Defect].} =
|
||||
RequestId {.raises: [Exception, Defect].} =
|
||||
doAssert(toNode.address.isSome())
|
||||
let
|
||||
reqId = ? newRequestId()
|
||||
reqId = RequestId.init(d.rng[])
|
||||
message = encodeMessage(m, reqId)
|
||||
(data, nonce) = ? d.codec.encodePacket(toNode.id, toNode.address.get(),
|
||||
(data, nonce) = encodePacket(d.rng[], d.codec, toNode.id, toNode.address.get(),
|
||||
message, challenge = nil)
|
||||
d.registerRequest(toNode, message, nonce)
|
||||
d.send(toNode, data)
|
||||
return ok(reqId)
|
||||
return reqId
|
||||
|
||||
proc ping*(d: Protocol, toNode: Node):
|
||||
Future[DiscResult[PongMessage]] {.async, raises: [Exception, Defect].} =
|
||||
let reqId = d.sendMessage(toNode,
|
||||
PingMessage(enrSeq: d.localNode.record.seqNum))
|
||||
if reqId.isErr:
|
||||
return err(reqId.error)
|
||||
let resp = await d.waitMessage(toNode, reqId[])
|
||||
let resp = await d.waitMessage(toNode, reqId)
|
||||
|
||||
if resp.isSome() and resp.get().kind == pong:
|
||||
d.routingTable.setJustSeen(toNode)
|
||||
|
@ -547,9 +531,7 @@ proc ping*(d: Protocol, toNode: Node):
|
|||
proc findNode*(d: Protocol, toNode: Node, distance: uint32):
|
||||
Future[DiscResult[seq[Node]]] {.async, raises: [Exception, Defect].} =
|
||||
let reqId = d.sendMessage(toNode, FindNodeMessage(distance: distance))
|
||||
if reqId.isErr:
|
||||
return err(reqId.error)
|
||||
let nodes = await d.waitNodes(toNode, reqId[])
|
||||
let nodes = await d.waitNodes(toNode, reqId)
|
||||
|
||||
if nodes.isOk:
|
||||
var res = newSeq[Node]()
|
||||
|
@ -632,15 +614,16 @@ proc lookup*(d: Protocol, target: NodeId): Future[seq[Node]]
|
|||
if result.len < BUCKET_SIZE:
|
||||
result.add(n)
|
||||
|
||||
proc lookupRandom*(d: Protocol): Future[DiscResult[seq[Node]]]
|
||||
proc lookupRandom*(d: Protocol): Future[seq[Node]]
|
||||
{.async, raises:[Exception, Defect].} =
|
||||
## Perform a lookup for a random target, return the closest n nodes to the
|
||||
## target. Maximum value for n is `BUCKET_SIZE`.
|
||||
var id: NodeId
|
||||
if randomBytes(addr id, sizeof(id)) != sizeof(id):
|
||||
return err("Could not randomize bytes")
|
||||
var buf: array[sizeof(id), byte]
|
||||
brHmacDrbgGenerate(d.rng[], buf)
|
||||
copyMem(addr id, addr buf[0], sizeof(id))
|
||||
|
||||
return ok(await d.lookup(id))
|
||||
return await d.lookup(id)
|
||||
|
||||
proc resolve*(d: Protocol, id: NodeId): Future[Option[Node]]
|
||||
{.async, raises: [Exception, Defect].} =
|
||||
|
@ -700,11 +683,8 @@ proc lookupLoop(d: Protocol) {.async, raises: [Exception, Defect].} =
|
|||
trace "Discovered nodes in self lookup", nodes = $selfLookup
|
||||
while true:
|
||||
let randomLookup = await d.lookupRandom()
|
||||
if randomLookup.isOK:
|
||||
trace "Discovered nodes in random lookup", nodes = $randomLookup[]
|
||||
trace "Discovered nodes in random lookup", nodes = $randomLookup
|
||||
trace "Total nodes in routing table", total = d.routingTable.len()
|
||||
else:
|
||||
trace "random lookup failed", err = randomLookup.error
|
||||
await sleepAsync(lookupInterval)
|
||||
except CancelledError:
|
||||
trace "lookupLoop canceled"
|
||||
|
@ -713,7 +693,7 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
|
|||
externalIp: Option[ValidIpAddress], tcpPort, udpPort: Port,
|
||||
localEnrFields: openarray[FieldPair] = [],
|
||||
bootstrapRecords: openarray[Record] = [],
|
||||
bindIp = IPv4_any()):
|
||||
bindIp = IPv4_any(), rng = newRng()):
|
||||
Protocol {.raises: [Defect].} =
|
||||
# TODO: Tried adding bindPort = udpPort as parameter but that gave
|
||||
# "Error: internal error: environment misses: udpPort" in nim-beacon-chain.
|
||||
|
@ -725,6 +705,9 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
|
|||
localEnrFields).expect("Properly intialized private key")
|
||||
node = newNode(enrRec).expect("Properly initialized node")
|
||||
|
||||
# TODO Consider whether this should be a Defect
|
||||
doAssert rng != nil, "RNG initialization failed"
|
||||
|
||||
result = Protocol(
|
||||
privateKey: privKey,
|
||||
db: db,
|
||||
|
@ -733,7 +716,8 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
|
|||
whoareyouMagic: whoareyouMagic(node.id),
|
||||
idHash: sha256.digest(node.id.toByteArrayBE).data,
|
||||
codec: Codec(localNode: node, privKey: privKey, db: db),
|
||||
bootstrapRecords: @bootstrapRecords)
|
||||
bootstrapRecords: @bootstrapRecords,
|
||||
rng: rng)
|
||||
|
||||
result.routingTable.init(node, 5)
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sysrand, sha2, utils]
|
||||
import bearssl
|
||||
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sha2, utils]
|
||||
import stew/results
|
||||
|
||||
export results
|
||||
|
@ -23,7 +24,6 @@ const
|
|||
type
|
||||
EciesError* = enum
|
||||
BufferOverrun = "ecies: output buffer size is too small"
|
||||
RandomError = "ecies: could not obtain random data"
|
||||
EcdhError = "ecies: ECDH shared secret could not be calculated"
|
||||
WrongHeader = "ecies: header is incorrect"
|
||||
IncorrectKey = "ecies: recovered public key is invalid"
|
||||
|
@ -92,8 +92,8 @@ proc kdf*(data: openarray[byte]): array[KeyLength, byte] {.noInit.} =
|
|||
ctx.clear() # clean ctx
|
||||
copyMem(addr result[0], addr storage[0], KeyLength)
|
||||
|
||||
proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
|
||||
pubkey: PublicKey,
|
||||
proc eciesEncrypt*(rng: var BrHmacDrbgContext, input: openarray[byte],
|
||||
output: var openarray[byte], pubkey: PublicKey,
|
||||
sharedmac: openarray[byte] = emptyMac): EciesResult[void] =
|
||||
## Encrypt data with ECIES method using given public key `pubkey`.
|
||||
## ``input`` - input data
|
||||
|
@ -110,11 +110,11 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
|
|||
|
||||
if len(output) < eciesEncryptedLength(len(input)):
|
||||
return err(BufferOverrun)
|
||||
if randomBytes(iv) != aes128.sizeBlock:
|
||||
return err(RandomError)
|
||||
|
||||
brHmacDrbgGenerate(rng, iv)
|
||||
|
||||
var
|
||||
ephemeral = ? KeyPair.random().mapErrTo(RandomError)
|
||||
ephemeral = KeyPair.random(rng)
|
||||
secret = ecdhRaw(ephemeral.seckey, pubkey)
|
||||
material = kdf(secret.data)
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#
|
||||
|
||||
import
|
||||
tables, hashes, times, algorithm, sets, sequtils, random,
|
||||
chronos, eth/keys, chronicles, stint, nimcrypto,
|
||||
tables, hashes, times, algorithm, sets, sequtils, bearssl, random,
|
||||
chronos, eth/keys, chronicles, stint, nimcrypto/keccak,
|
||||
enode
|
||||
|
||||
export sets # TODO: This should not be needed, but compilation fails otherwise
|
||||
|
@ -26,6 +26,7 @@ type
|
|||
pongFutures: Table[seq[byte], Future[bool]]
|
||||
pingFutures: Table[Node, Future[bool]]
|
||||
neighboursCallbacks: Table[Node, proc(n: seq[Node]) {.gcsafe.}]
|
||||
rng: ref BrHmacDrbgContext
|
||||
|
||||
NodeId* = UInt256
|
||||
|
||||
|
@ -231,12 +232,15 @@ proc neighbours(r: RoutingTable, id: NodeId, k: int = BUCKET_SIZE): seq[Node] =
|
|||
proc len(r: RoutingTable): int =
|
||||
for b in r.buckets: result += b.len
|
||||
|
||||
proc newKademliaProtocol*[Wire](thisNode: Node,
|
||||
wire: Wire): KademliaProtocol[Wire] =
|
||||
proc newKademliaProtocol*[Wire](
|
||||
thisNode: Node, wire: Wire, rng = newRng()): KademliaProtocol[Wire] =
|
||||
if rng == nil: raiseAssert "Need an RNG" # doAssert gives compile error on mac
|
||||
|
||||
result.new()
|
||||
result.thisNode = thisNode
|
||||
result.wire = wire
|
||||
result.routing.init(thisNode)
|
||||
result.rng = rng
|
||||
|
||||
proc bond(k: KademliaProtocol, n: Node): Future[bool] {.async, gcsafe.}
|
||||
|
||||
|
@ -408,7 +412,10 @@ proc lookup*(k: KademliaProtocol, nodeId: NodeId): Future[seq[Node]] {.async.} =
|
|||
|
||||
proc lookupRandom*(k: KademliaProtocol): Future[seq[Node]] =
|
||||
var id: NodeId
|
||||
discard randomBytes(addr id, id.sizeof)
|
||||
var buf: array[sizeof(id), byte]
|
||||
brHmacDrbgGenerate(k.rng[], buf)
|
||||
copyMem(addr id, addr buf[0], sizeof(id))
|
||||
|
||||
k.lookup(id)
|
||||
|
||||
proc resolve*(k: KademliaProtocol, id: NodeId): Future[Node] {.async.} =
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import
|
||||
deques, tables,
|
||||
deques, tables, bearssl,
|
||||
eth/[rlp, keys], chronos, eth/common/eth_types,
|
||||
../enode, ../kademlia, ../discovery, ../rlpxcrypt
|
||||
|
||||
|
@ -24,6 +24,7 @@ type
|
|||
discovery*: DiscoveryProtocol
|
||||
when useSnappy:
|
||||
protocolVersion*: uint
|
||||
rng*: ref BrHmacDrbgContext
|
||||
|
||||
Peer* = ref object
|
||||
remote*: Node
|
||||
|
|
|
@ -461,7 +461,7 @@ proc waitSingleMsg(peer: Peer, MsgType: type): Future[MsgType] {.async.} =
|
|||
result = checkedRlpRead(peer, nextMsgData, MsgType)
|
||||
logReceivedMsg(peer, result)
|
||||
return
|
||||
except RlpError:
|
||||
except rlp.RlpError:
|
||||
await peer.disconnectAndRaise(BreachOfProtocol,
|
||||
"Invalid RLPx message body")
|
||||
|
||||
|
@ -969,11 +969,12 @@ proc rlpxConnect*(node: EthereumNode, remote: Node): Future[Peer] {.async.} =
|
|||
try:
|
||||
result.transport = await connect(ta)
|
||||
var handshake = Handshake.tryInit(
|
||||
node.keys, {Initiator, EIP8}, node.baseProtocolVersion).tryGet()
|
||||
node.rng[], node.keys, {Initiator, EIP8}, node.baseProtocolVersion).tryGet()
|
||||
|
||||
var authMsg: array[AuthMessageMaxEIP8, byte]
|
||||
var authMsgLen = 0
|
||||
authMessage(handshake, remote.node.pubkey, authMsg, authMsgLen).tryGet()
|
||||
authMessage(
|
||||
handshake, node.rng[], remote.node.pubkey, authMsg, authMsgLen).tryGet()
|
||||
var res = await result.transport.write(addr authMsg[0], authMsgLen)
|
||||
if res != authMsgLen:
|
||||
raisePeerDisconnected("Unexpected disconnect while authenticating",
|
||||
|
@ -1055,7 +1056,8 @@ proc rlpxAccept*(node: EthereumNode,
|
|||
result.transport = transport
|
||||
result.network = node
|
||||
|
||||
var handshake = HandShake.tryInit(node.keys, {auth.Responder}).tryGet
|
||||
var handshake =
|
||||
HandShake.tryInit(node.rng[], node.keys, {auth.Responder}).tryGet
|
||||
|
||||
var ok = false
|
||||
try:
|
||||
|
@ -1078,7 +1080,7 @@ proc rlpxAccept*(node: EthereumNode,
|
|||
|
||||
var ackMsg: array[AckMessageMaxEIP8, byte]
|
||||
var ackMsgLen: int
|
||||
handshake.ackMessage(ackMsg, ackMsgLen).tryGet()
|
||||
handshake.ackMessage(node.rng[], ackMsg, ackMsgLen).tryGet()
|
||||
var res = await transport.write(addr ackMsg[0], ackMsgLen)
|
||||
if res != ackMsgLen:
|
||||
raisePeerDisconnected("Unexpected disconnect while authenticating",
|
||||
|
|
|
@ -1,196 +0,0 @@
|
|||
|
||||
## Generated at line 781
|
||||
type
|
||||
DevP2P* = object
|
||||
type
|
||||
helloObj* = object
|
||||
version*: uint
|
||||
clientId*: string
|
||||
capabilities*: seq[Capability]
|
||||
listenPort*: uint
|
||||
nodeId*: array[RawPublicKeySize, byte]
|
||||
|
||||
template hello*(PROTO: type DevP2P): type =
|
||||
helloObj
|
||||
|
||||
template msgProtocol*(MSG: type helloObj): type =
|
||||
DevP2P
|
||||
|
||||
template RecType*(MSG: type helloObj): untyped =
|
||||
helloObj
|
||||
|
||||
template msgId*(MSG: type helloObj): int =
|
||||
0
|
||||
|
||||
type
|
||||
sendDisconnectMsgObj* = object
|
||||
reason*: DisconnectionReasonList
|
||||
|
||||
template sendDisconnectMsg*(PROTO: type DevP2P): type =
|
||||
sendDisconnectMsgObj
|
||||
|
||||
template msgProtocol*(MSG: type sendDisconnectMsgObj): type =
|
||||
DevP2P
|
||||
|
||||
template RecType*(MSG: type sendDisconnectMsgObj): untyped =
|
||||
sendDisconnectMsgObj
|
||||
|
||||
template msgId*(MSG: type sendDisconnectMsgObj): int =
|
||||
1
|
||||
|
||||
type
|
||||
pingObj* = object
|
||||
emptyList*: EmptyList
|
||||
|
||||
template ping*(PROTO: type DevP2P): type =
|
||||
pingObj
|
||||
|
||||
template msgProtocol*(MSG: type pingObj): type =
|
||||
DevP2P
|
||||
|
||||
template RecType*(MSG: type pingObj): untyped =
|
||||
pingObj
|
||||
|
||||
template msgId*(MSG: type pingObj): int =
|
||||
2
|
||||
|
||||
type
|
||||
pongObj* = object
|
||||
emptyList*: EmptyList
|
||||
|
||||
template pong*(PROTO: type DevP2P): type =
|
||||
pongObj
|
||||
|
||||
template msgProtocol*(MSG: type pongObj): type =
|
||||
DevP2P
|
||||
|
||||
template RecType*(MSG: type pongObj): untyped =
|
||||
pongObj
|
||||
|
||||
template msgId*(MSG: type pongObj): int =
|
||||
3
|
||||
|
||||
var DevP2PProtocolObj = initProtocol("p2p", 5, nil, nil)
|
||||
var DevP2PProtocol = addr DevP2PProtocolObj
|
||||
template protocolInfo*(P`gensym75730262: type DevP2P): auto =
|
||||
DevP2PProtocol
|
||||
|
||||
proc hello*(peerOrResponder: Peer; version: uint; clientId: string;
|
||||
capabilities: seq[Capability]; listenPort: uint;
|
||||
nodeId: array[RawPublicKeySize, byte]): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 0
|
||||
let perPeerMsgId = 0
|
||||
append(writer, perPeerMsgId)
|
||||
startList(writer, 5)
|
||||
append(writer, version)
|
||||
append(writer, clientId)
|
||||
append(writer, capabilities)
|
||||
append(writer, listenPort)
|
||||
append(writer, nodeId)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc sendDisconnectMsg*(peerOrResponder: Peer; reason: DisconnectionReasonList): Future[
|
||||
void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 1
|
||||
let perPeerMsgId = 1
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, reason)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc ping*(peerOrResponder: Peer; emptyList: EmptyList): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 2
|
||||
let perPeerMsgId = 2
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, emptyList)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc pong*(peerOrResponder: Peer; emptyList: EmptyList): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 3
|
||||
let perPeerMsgId = 3
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, emptyList)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc sendDisconnectMsgUserHandler(peer: Peer; reason: DisconnectionReasonList) {.
|
||||
gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = DevP2P
|
||||
const
|
||||
perProtocolMsgId = 1
|
||||
trace "disconnect message received", reason = reason.value, peer
|
||||
await peer.disconnect(reason.value, false)
|
||||
|
||||
proc pingUserHandler(peer: Peer; emptyList: EmptyList) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = DevP2P
|
||||
const
|
||||
perProtocolMsgId = 2
|
||||
discard peer.pong(EmptyList())
|
||||
|
||||
proc pongUserHandler(peer: Peer; emptyList: EmptyList) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = DevP2P
|
||||
const
|
||||
perProtocolMsgId = 3
|
||||
discard
|
||||
|
||||
proc helloThunk(peer: Peer; _`gensym75730215: int; data`gensym75730216: Rlp) {.async,
|
||||
gcsafe.} =
|
||||
var rlp = data`gensym75730216
|
||||
var msg {.noinit.}: helloObj
|
||||
tryEnterList(rlp)
|
||||
msg.version = checkedRlpRead(peer, rlp, uint)
|
||||
msg.clientId = checkedRlpRead(peer, rlp, string)
|
||||
msg.capabilities = checkedRlpRead(peer, rlp, seq[Capability])
|
||||
msg.listenPort = checkedRlpRead(peer, rlp, uint)
|
||||
msg.nodeId = checkedRlpRead(peer, rlp, array[RawPublicKeySize, byte])
|
||||
|
||||
proc sendDisconnectMsgThunk(peer: Peer; _`gensym75730250: int;
|
||||
data`gensym75730251: Rlp) {.async, gcsafe.} =
|
||||
var rlp = data`gensym75730251
|
||||
var msg {.noinit.}: sendDisconnectMsgObj
|
||||
msg.reason = checkedRlpRead(peer, rlp, DisconnectionReasonList)
|
||||
await(sendDisconnectMsgUserHandler(peer, msg.reason))
|
||||
|
||||
proc pingThunk(peer: Peer; _`gensym75730252: int; data`gensym75730253: Rlp) {.async,
|
||||
gcsafe.} =
|
||||
var rlp = data`gensym75730253
|
||||
var msg {.noinit.}: pingObj
|
||||
msg.emptyList = checkedRlpRead(peer, rlp, EmptyList)
|
||||
await(pingUserHandler(peer, msg.emptyList))
|
||||
|
||||
proc pongThunk(peer: Peer; _`gensym75730254: int; data`gensym75730255: Rlp) {.async,
|
||||
gcsafe.} =
|
||||
var rlp = data`gensym75730255
|
||||
var msg {.noinit.}: pongObj
|
||||
msg.emptyList = checkedRlpRead(peer, rlp, EmptyList)
|
||||
await(pongUserHandler(peer, msg.emptyList))
|
||||
|
||||
registerMsg(DevP2PProtocol, 0, "hello", helloThunk, messagePrinter[helloObj],
|
||||
requestResolver[helloObj], nextMsgResolver[helloObj])
|
||||
registerMsg(DevP2PProtocol, 1, "sendDisconnectMsg", sendDisconnectMsgThunk,
|
||||
messagePrinter[sendDisconnectMsgObj],
|
||||
requestResolver[sendDisconnectMsgObj],
|
||||
nextMsgResolver[sendDisconnectMsgObj])
|
||||
registerMsg(DevP2PProtocol, 2, "ping", pingThunk, messagePrinter[pingObj],
|
||||
requestResolver[pingObj], nextMsgResolver[pingObj])
|
||||
registerMsg(DevP2PProtocol, 3, "pong", pongThunk, messagePrinter[pongObj],
|
||||
requestResolver[pongObj], nextMsgResolver[pongObj])
|
||||
setEventHandlers(DevP2PProtocol, nil, nil)
|
||||
registerProtocol(DevP2PProtocol)
|
|
@ -9,9 +9,9 @@
|
|||
#
|
||||
|
||||
import
|
||||
algorithm, bitops, math, options, tables, times, chronicles, hashes, strutils,
|
||||
stew/[byteutils, endians2], metrics,
|
||||
nimcrypto/[bcmode, hash, keccak, rijndael, sysrand],
|
||||
algorithm, bitops, math, options, tables, times, chronicles, hashes,
|
||||
stew/[byteutils, endians2], metrics, bearssl,
|
||||
nimcrypto/[bcmode, hash, keccak, rijndael],
|
||||
eth/[keys, rlp, p2p], eth/p2p/ecies
|
||||
|
||||
logScope:
|
||||
|
@ -158,12 +158,10 @@ proc topicBloom*(topic: Topic): Bloom =
|
|||
doAssert idx <= 511
|
||||
result[idx div 8] = result[idx div 8] or byte(1 shl (idx and 7'u16))
|
||||
|
||||
proc generateRandomID*(): string =
|
||||
proc generateRandomID*(rng: var BrHmacDrbgContext): string =
|
||||
var bytes: array[256 div 8, byte]
|
||||
while true: # XXX: error instead of looping?
|
||||
if randomBytes(bytes) == 256 div 8:
|
||||
result = toHex(bytes)
|
||||
break
|
||||
brHmacDrbgGenerate(rng, bytes)
|
||||
toHex(bytes)
|
||||
|
||||
proc `or`(a, b: Bloom): Bloom =
|
||||
for i in 0..<a.len:
|
||||
|
@ -231,7 +229,7 @@ proc decryptAesGcm(cipher: openarray[byte], key: SymKey): Option[seq[byte]] =
|
|||
# simply because that makes it closer to EIP 627 - see also:
|
||||
# https://github.com/paritytech/parity-ethereum/issues/9652
|
||||
|
||||
proc encode*(self: Payload): Option[seq[byte]] =
|
||||
proc encode*(rng: var BrHmacDrbgContext, self: Payload): Option[seq[byte]] =
|
||||
## Encode a payload according so as to make it suitable to put in an Envelope
|
||||
## The format follows EIP 627 - https://eips.ethereum.org/EIPS/eip-627
|
||||
|
||||
|
@ -284,9 +282,7 @@ proc encode*(self: Payload): Option[seq[byte]] =
|
|||
plain.add self.padding.get()
|
||||
else:
|
||||
var padding = newSeq[byte](padLen)
|
||||
if randomBytes(padding) != padLen:
|
||||
notice "Generation of random padding failed"
|
||||
return
|
||||
brHmacDrbgGenerate(rng, padding)
|
||||
|
||||
plain.add padding
|
||||
|
||||
|
@ -297,7 +293,7 @@ proc encode*(self: Payload): Option[seq[byte]] =
|
|||
|
||||
if self.dst.isSome(): # Asymmetric key present - encryption requested
|
||||
var res = newSeq[byte](eciesEncryptedLength(plain.len))
|
||||
let err = eciesEncrypt(plain, res, self.dst.get())
|
||||
let err = eciesEncrypt(rng, plain, res, self.dst.get())
|
||||
if err.isErr:
|
||||
notice "Encryption failed", err = err.error
|
||||
return
|
||||
|
@ -305,9 +301,7 @@ proc encode*(self: Payload): Option[seq[byte]] =
|
|||
|
||||
if self.symKey.isSome(): # Symmetric key present - encryption requested
|
||||
var iv: array[gcmIVLen, byte]
|
||||
if randomBytes(iv) != gcmIVLen:
|
||||
notice "Generation of random IV failed"
|
||||
return
|
||||
brHmacDrbgGenerate(rng, iv)
|
||||
|
||||
return some(encryptAesGcm(plain, self.symKey.get(), iv))
|
||||
|
||||
|
@ -582,11 +576,12 @@ proc initFilter*(src = none[PublicKey](), privateKey = none[PrivateKey](),
|
|||
Filter(src: src, privateKey: privateKey, symKey: symKey, topics: topics,
|
||||
powReq: powReq, allowP2P: allowP2P, bloom: toBloom(topics))
|
||||
|
||||
proc subscribeFilter*(filters: var Filters, filter: Filter,
|
||||
handler:FilterMsgHandler = nil): string =
|
||||
proc subscribeFilter*(
|
||||
rng: var BrHmacDrbgContext, filters: var Filters, filter: Filter,
|
||||
handler: FilterMsgHandler = nil): string =
|
||||
# NOTE: Should we allow a filter without a key? Encryption is mandatory in v6?
|
||||
# Check if asymmetric _and_ symmetric key? Now asymmetric just has precedence.
|
||||
let id = generateRandomID()
|
||||
let id = generateRandomID(rng)
|
||||
var filter = filter
|
||||
if handler.isNil():
|
||||
filter.queue = newSeqOfCap[ReceivedMessage](defaultFilterQueueCapacity)
|
||||
|
|
|
@ -343,8 +343,8 @@ proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](),
|
|||
##
|
||||
## NOTE: This call allows a post without encryption. If encryption is
|
||||
## mandatory it should be enforced a layer up
|
||||
let payload = encode(Payload(payload: payload, src: src, dst: pubKey,
|
||||
symKey: symKey, padding: padding))
|
||||
let payload = encode(node.rng[], Payload(
|
||||
payload: payload, src: src, dst: pubKey, symKey: symKey, padding: padding))
|
||||
if payload.isSome():
|
||||
var env = Envelope(expiry:epochTime().uint32 + ttl,
|
||||
ttl: ttl, topic: topic, data: payload.get(), nonce: 0)
|
||||
|
@ -387,7 +387,8 @@ proc subscribeFilter*(node: EthereumNode, filter: Filter,
|
|||
##
|
||||
## NOTE: This call allows for a filter without decryption. If encryption is
|
||||
## mandatory it should be enforced a layer up.
|
||||
return node.protocolState(Whisper).filters.subscribeFilter(filter, handler)
|
||||
return subscribeFilter(
|
||||
node.rng[], node.protocolState(Whisper).filters, filter, handler)
|
||||
|
||||
proc unsubscribeFilter*(node: EthereumNode, filterId: string): bool =
|
||||
## Remove a previously subscribed filter.
|
||||
|
|
|
@ -1,662 +0,0 @@
|
|||
|
||||
## Generated at line 119
|
||||
type
|
||||
Whisper* = object
|
||||
template State*(PROTO: type Whisper): type =
|
||||
ref[WhisperPeer:ObjectType]
|
||||
|
||||
template NetworkState*(PROTO: type Whisper): type =
|
||||
ref[WhisperNetwork:ObjectType]
|
||||
|
||||
type
|
||||
statusObj* = object
|
||||
protocolVersion*: uint
|
||||
powConverted*: uint64
|
||||
bloom*: seq[byte]
|
||||
isLightNode*: bool
|
||||
|
||||
template status*(PROTO: type Whisper): type =
|
||||
statusObj
|
||||
|
||||
template msgProtocol*(MSG: type statusObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type statusObj): untyped =
|
||||
statusObj
|
||||
|
||||
template msgId*(MSG: type statusObj): int =
|
||||
0
|
||||
|
||||
type
|
||||
messagesObj* = object
|
||||
envelopes*: seq[Envelope]
|
||||
|
||||
template messages*(PROTO: type Whisper): type =
|
||||
messagesObj
|
||||
|
||||
template msgProtocol*(MSG: type messagesObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type messagesObj): untyped =
|
||||
messagesObj
|
||||
|
||||
template msgId*(MSG: type messagesObj): int =
|
||||
1
|
||||
|
||||
type
|
||||
powRequirementObj* = object
|
||||
value*: uint64
|
||||
|
||||
template powRequirement*(PROTO: type Whisper): type =
|
||||
powRequirementObj
|
||||
|
||||
template msgProtocol*(MSG: type powRequirementObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type powRequirementObj): untyped =
|
||||
powRequirementObj
|
||||
|
||||
template msgId*(MSG: type powRequirementObj): int =
|
||||
2
|
||||
|
||||
type
|
||||
bloomFilterExchangeObj* = object
|
||||
bloom*: seq[byte]
|
||||
|
||||
template bloomFilterExchange*(PROTO: type Whisper): type =
|
||||
bloomFilterExchangeObj
|
||||
|
||||
template msgProtocol*(MSG: type bloomFilterExchangeObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type bloomFilterExchangeObj): untyped =
|
||||
bloomFilterExchangeObj
|
||||
|
||||
template msgId*(MSG: type bloomFilterExchangeObj): int =
|
||||
3
|
||||
|
||||
type
|
||||
p2pRequestObj* = object
|
||||
envelope*: Envelope
|
||||
|
||||
template p2pRequest*(PROTO: type Whisper): type =
|
||||
p2pRequestObj
|
||||
|
||||
template msgProtocol*(MSG: type p2pRequestObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type p2pRequestObj): untyped =
|
||||
p2pRequestObj
|
||||
|
||||
template msgId*(MSG: type p2pRequestObj): int =
|
||||
126
|
||||
|
||||
type
|
||||
p2pMessageObj* = object
|
||||
envelope*: Envelope
|
||||
|
||||
template p2pMessage*(PROTO: type Whisper): type =
|
||||
p2pMessageObj
|
||||
|
||||
template msgProtocol*(MSG: type p2pMessageObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type p2pMessageObj): untyped =
|
||||
p2pMessageObj
|
||||
|
||||
template msgId*(MSG: type p2pMessageObj): int =
|
||||
127
|
||||
|
||||
type
|
||||
batchAcknowledgedObj* = object
|
||||
|
||||
template batchAcknowledged*(PROTO: type Whisper): type =
|
||||
batchAcknowledgedObj
|
||||
|
||||
template msgProtocol*(MSG: type batchAcknowledgedObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type batchAcknowledgedObj): untyped =
|
||||
batchAcknowledgedObj
|
||||
|
||||
template msgId*(MSG: type batchAcknowledgedObj): int =
|
||||
11
|
||||
|
||||
type
|
||||
messageResponseObj* = object
|
||||
|
||||
template messageResponse*(PROTO: type Whisper): type =
|
||||
messageResponseObj
|
||||
|
||||
template msgProtocol*(MSG: type messageResponseObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type messageResponseObj): untyped =
|
||||
messageResponseObj
|
||||
|
||||
template msgId*(MSG: type messageResponseObj): int =
|
||||
12
|
||||
|
||||
type
|
||||
p2pSyncResponseObj* = object
|
||||
|
||||
template p2pSyncResponse*(PROTO: type Whisper): type =
|
||||
p2pSyncResponseObj
|
||||
|
||||
template msgProtocol*(MSG: type p2pSyncResponseObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type p2pSyncResponseObj): untyped =
|
||||
p2pSyncResponseObj
|
||||
|
||||
template msgId*(MSG: type p2pSyncResponseObj): int =
|
||||
124
|
||||
|
||||
type
|
||||
p2pSyncRequestObj* = object
|
||||
|
||||
template p2pSyncRequest*(PROTO: type Whisper): type =
|
||||
p2pSyncRequestObj
|
||||
|
||||
template msgProtocol*(MSG: type p2pSyncRequestObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type p2pSyncRequestObj): untyped =
|
||||
p2pSyncRequestObj
|
||||
|
||||
template msgId*(MSG: type p2pSyncRequestObj): int =
|
||||
123
|
||||
|
||||
type
|
||||
p2pRequestCompleteObj* = object
|
||||
|
||||
template p2pRequestComplete*(PROTO: type Whisper): type =
|
||||
p2pRequestCompleteObj
|
||||
|
||||
template msgProtocol*(MSG: type p2pRequestCompleteObj): type =
|
||||
Whisper
|
||||
|
||||
template RecType*(MSG: type p2pRequestCompleteObj): untyped =
|
||||
p2pRequestCompleteObj
|
||||
|
||||
template msgId*(MSG: type p2pRequestCompleteObj): int =
|
||||
125
|
||||
|
||||
var WhisperProtocolObj = initProtocol("shh", 6, createPeerState[Peer,
|
||||
ref[WhisperPeer:ObjectType]], createNetworkState[EthereumNode,
|
||||
ref[WhisperNetwork:ObjectType]])
|
||||
var WhisperProtocol = addr WhisperProtocolObj
|
||||
template protocolInfo*(P`gensym85175079: type Whisper): auto =
|
||||
WhisperProtocol
|
||||
|
||||
proc statusRawSender(peerOrResponder: Peer; protocolVersion: uint;
|
||||
powConverted: uint64; bloom: seq[byte]; isLightNode: bool;
|
||||
timeout: Duration = milliseconds(10000'i64)): Future[void] {.
|
||||
gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 0
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 0)
|
||||
append(writer, perPeerMsgId)
|
||||
startList(writer, 4)
|
||||
append(writer, protocolVersion)
|
||||
append(writer, powConverted)
|
||||
append(writer, bloom)
|
||||
append(writer, isLightNode)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
template status*(peerOrResponder: Peer; protocolVersion: uint; powConverted: uint64;
|
||||
bloom: seq[byte]; isLightNode: bool;
|
||||
timeout: Duration = milliseconds(10000'i64)): Future[statusObj] =
|
||||
let peer = peerOrResponder
|
||||
let sendingFuture`gensym85175056 = statusRawSender(peer, protocolVersion,
|
||||
powConverted, bloom, isLightNode)
|
||||
handshakeImpl(peer, sendingFuture`gensym85175056, nextMsg(peer, statusObj),
|
||||
timeout)
|
||||
|
||||
proc messages*(peerOrResponder: Peer; envelopes: openarray[Envelope]): Future[void] {.
|
||||
gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 1
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 1)
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, envelopes)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc powRequirement*(peerOrResponder: Peer; value: uint64): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 2
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 2)
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, value)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc bloomFilterExchange*(peerOrResponder: Peer; bloom: openArray[byte]): Future[void] {.
|
||||
gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 3
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 3)
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, bloom)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc p2pRequest*(peerOrResponder: Peer; envelope: Envelope): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 126
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 126)
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, envelope)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc p2pMessage*(peerOrResponder: Peer; envelope: Envelope): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 127
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 127)
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, envelope)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc batchAcknowledged*(peerOrResponder: Peer): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 11
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 11)
|
||||
append(writer, perPeerMsgId)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc messageResponse*(peerOrResponder: Peer): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 12
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 12)
|
||||
append(writer, perPeerMsgId)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc p2pSyncResponse*(peerOrResponder: ResponderWithId[p2pSyncResponseObj]): Future[
|
||||
void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 124
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 124)
|
||||
append(writer, perPeerMsgId)
|
||||
append(writer, peerOrResponder.reqId)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
template send*(r`gensym85175073: ResponderWithId[p2pSyncResponseObj];
|
||||
args`gensym85175074: varargs[untyped]): auto =
|
||||
p2pSyncResponse(r`gensym85175073, args`gensym85175074)
|
||||
|
||||
proc p2pSyncRequest*(peerOrResponder: Peer;
|
||||
timeout: Duration = milliseconds(10000'i64)): Future[
|
||||
Option[p2pSyncResponseObj]] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 123
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 123)
|
||||
append(writer, perPeerMsgId)
|
||||
initFuture result
|
||||
let reqId = registerRequest(peer, timeout, result, perPeerMsgId + 1)
|
||||
append(writer, reqId)
|
||||
let msgBytes = finish(writer)
|
||||
linkSendFailureToReqFuture(sendMsg(peer, msgBytes), result)
|
||||
|
||||
proc p2pRequestComplete*(peerOrResponder: Peer): Future[void] {.gcsafe.} =
|
||||
let peer = getPeer(peerOrResponder)
|
||||
var writer = initRlpWriter()
|
||||
const
|
||||
perProtocolMsgId = 125
|
||||
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 125)
|
||||
append(writer, perPeerMsgId)
|
||||
let msgBytes = finish(writer)
|
||||
return sendMsg(peer, msgBytes)
|
||||
|
||||
proc messagesUserHandler(peer: Peer; envelopes: seq[Envelope]) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 1
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
if not peer.state.initialized:
|
||||
warn "Handshake not completed yet, discarding messages"
|
||||
return
|
||||
for envelope in envelopes:
|
||||
if not envelope.valid():
|
||||
warn "Expired or future timed envelope", peer
|
||||
continue
|
||||
let msg = initMessage(envelope)
|
||||
if not msg.allowed(peer.networkState.config):
|
||||
continue
|
||||
if peer.state.received.containsOrIncl(msg.hash):
|
||||
dropped_duplicate_envelopes.inc()
|
||||
trace "Peer sending duplicate messages", peer, hash = $msg.hash
|
||||
continue
|
||||
if peer.networkState.queue[].add(msg):
|
||||
peer.networkState.filters.notify(msg)
|
||||
|
||||
proc powRequirementUserHandler(peer: Peer; value: uint64) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 2
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
if not peer.state.initialized:
|
||||
warn "Handshake not completed yet, discarding powRequirement"
|
||||
return
|
||||
peer.state.powRequirement = cast[float64](value)
|
||||
|
||||
proc bloomFilterExchangeUserHandler(peer: Peer; bloom: seq[byte]) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 3
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
if not peer.state.initialized:
|
||||
warn "Handshake not completed yet, discarding bloomFilterExchange"
|
||||
return
|
||||
if bloom.len == bloomSize:
|
||||
peer.state.bloom.bytesCopy(bloom)
|
||||
|
||||
proc p2pRequestUserHandler(peer: Peer; envelope: Envelope) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 126
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
discard
|
||||
|
||||
proc p2pMessageUserHandler(peer: Peer; envelope: Envelope) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 127
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
if peer.state.trusted:
|
||||
let msg = Message(env: envelope, isP2P: true)
|
||||
peer.networkState.filters.notify(msg)
|
||||
|
||||
proc batchAcknowledgedUserHandler(peer: Peer) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 11
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
discard
|
||||
|
||||
proc messageResponseUserHandler(peer: Peer) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 12
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
discard
|
||||
|
||||
proc p2pSyncResponseUserHandler(peer: Peer; reqId: int) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 124
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
discard
|
||||
|
||||
proc p2pSyncRequestUserHandler(peer: Peer; reqId: int) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 123
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
var response = init(ResponderWithId[p2pSyncResponseObj], peer, reqId)
|
||||
discard
|
||||
|
||||
proc p2pRequestCompleteUserHandler(peer: Peer) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
const
|
||||
perProtocolMsgId = 125
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
discard
|
||||
|
||||
proc statusThunk(peer: Peer; _`gensym85175033: int; data`gensym85175034: Rlp) {.async,
|
||||
gcsafe.} =
|
||||
var rlp = data`gensym85175034
|
||||
var msg {.noinit.}: statusObj
|
||||
tryEnterList(rlp)
|
||||
msg.protocolVersion = checkedRlpRead(peer, rlp, uint)
|
||||
msg.powConverted = checkedRlpRead(peer, rlp, uint64)
|
||||
msg.bloom = checkedRlpRead(peer, rlp, seq[byte])
|
||||
msg.isLightNode = checkedRlpRead(peer, rlp, bool)
|
||||
|
||||
proc messagesThunk(peer: Peer; _`gensym85175057: int; data`gensym85175058: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175058
|
||||
var msg {.noinit.}: messagesObj
|
||||
msg.envelopes = checkedRlpRead(peer, rlp, openarray[Envelope])
|
||||
await(messagesUserHandler(peer, msg.envelopes))
|
||||
|
||||
proc powRequirementThunk(peer: Peer; _`gensym85175059: int; data`gensym85175060: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175060
|
||||
var msg {.noinit.}: powRequirementObj
|
||||
msg.value = checkedRlpRead(peer, rlp, uint64)
|
||||
await(powRequirementUserHandler(peer, msg.value))
|
||||
|
||||
proc bloomFilterExchangeThunk(peer: Peer; _`gensym85175061: int;
|
||||
data`gensym85175062: Rlp) {.async, gcsafe.} =
|
||||
var rlp = data`gensym85175062
|
||||
var msg {.noinit.}: bloomFilterExchangeObj
|
||||
msg.bloom = checkedRlpRead(peer, rlp, openArray[byte])
|
||||
await(bloomFilterExchangeUserHandler(peer, msg.bloom))
|
||||
|
||||
proc p2pRequestThunk(peer: Peer; _`gensym85175063: int; data`gensym85175064: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175064
|
||||
var msg {.noinit.}: p2pRequestObj
|
||||
msg.envelope = checkedRlpRead(peer, rlp, Envelope)
|
||||
await(p2pRequestUserHandler(peer, msg.envelope))
|
||||
|
||||
proc p2pMessageThunk(peer: Peer; _`gensym85175065: int; data`gensym85175066: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175066
|
||||
var msg {.noinit.}: p2pMessageObj
|
||||
msg.envelope = checkedRlpRead(peer, rlp, Envelope)
|
||||
await(p2pMessageUserHandler(peer, msg.envelope))
|
||||
|
||||
proc batchAcknowledgedThunk(peer: Peer; _`gensym85175067: int;
|
||||
data`gensym85175068: Rlp) {.async, gcsafe.} =
|
||||
var rlp = data`gensym85175068
|
||||
var msg {.noinit.}: batchAcknowledgedObj
|
||||
await(batchAcknowledgedUserHandler(peer))
|
||||
|
||||
proc messageResponseThunk(peer: Peer; _`gensym85175069: int; data`gensym85175070: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175070
|
||||
var msg {.noinit.}: messageResponseObj
|
||||
await(messageResponseUserHandler(peer))
|
||||
|
||||
proc p2pSyncResponseThunk(peer: Peer; _`gensym85175071: int; data`gensym85175072: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175072
|
||||
var msg {.noinit.}: p2pSyncResponseObj
|
||||
let reqId = read(rlp, int)
|
||||
await(p2pSyncResponseUserHandler(peer, reqId))
|
||||
resolveResponseFuture(peer, perPeerMsgId(peer, p2pSyncResponseObj), addr(msg),
|
||||
reqId)
|
||||
|
||||
proc p2pSyncRequestThunk(peer: Peer; _`gensym85175075: int; data`gensym85175076: Rlp) {.
|
||||
async, gcsafe.} =
|
||||
var rlp = data`gensym85175076
|
||||
var msg {.noinit.}: p2pSyncRequestObj
|
||||
let reqId = read(rlp, int)
|
||||
await(p2pSyncRequestUserHandler(peer, reqId))
|
||||
|
||||
proc p2pRequestCompleteThunk(peer: Peer; _`gensym85175077: int;
|
||||
data`gensym85175078: Rlp) {.async, gcsafe.} =
|
||||
var rlp = data`gensym85175078
|
||||
var msg {.noinit.}: p2pRequestCompleteObj
|
||||
await(p2pRequestCompleteUserHandler(peer))
|
||||
|
||||
registerMsg(WhisperProtocol, 0, "status", statusThunk, messagePrinter[statusObj],
|
||||
requestResolver[statusObj], nextMsgResolver[statusObj])
|
||||
registerMsg(WhisperProtocol, 1, "messages", messagesThunk,
|
||||
messagePrinter[messagesObj], requestResolver[messagesObj],
|
||||
nextMsgResolver[messagesObj])
|
||||
registerMsg(WhisperProtocol, 2, "powRequirement", powRequirementThunk,
|
||||
messagePrinter[powRequirementObj],
|
||||
requestResolver[powRequirementObj],
|
||||
nextMsgResolver[powRequirementObj])
|
||||
registerMsg(WhisperProtocol, 3, "bloomFilterExchange", bloomFilterExchangeThunk,
|
||||
messagePrinter[bloomFilterExchangeObj],
|
||||
requestResolver[bloomFilterExchangeObj],
|
||||
nextMsgResolver[bloomFilterExchangeObj])
|
||||
registerMsg(WhisperProtocol, 126, "p2pRequest", p2pRequestThunk,
|
||||
messagePrinter[p2pRequestObj], requestResolver[p2pRequestObj],
|
||||
nextMsgResolver[p2pRequestObj])
|
||||
registerMsg(WhisperProtocol, 127, "p2pMessage", p2pMessageThunk,
|
||||
messagePrinter[p2pMessageObj], requestResolver[p2pMessageObj],
|
||||
nextMsgResolver[p2pMessageObj])
|
||||
registerMsg(WhisperProtocol, 11, "batchAcknowledged", batchAcknowledgedThunk,
|
||||
messagePrinter[batchAcknowledgedObj],
|
||||
requestResolver[batchAcknowledgedObj],
|
||||
nextMsgResolver[batchAcknowledgedObj])
|
||||
registerMsg(WhisperProtocol, 12, "messageResponse", messageResponseThunk,
|
||||
messagePrinter[messageResponseObj],
|
||||
requestResolver[messageResponseObj],
|
||||
nextMsgResolver[messageResponseObj])
|
||||
registerMsg(WhisperProtocol, 124, "p2pSyncResponse", p2pSyncResponseThunk,
|
||||
messagePrinter[p2pSyncResponseObj],
|
||||
requestResolver[p2pSyncResponseObj],
|
||||
nextMsgResolver[p2pSyncResponseObj])
|
||||
registerMsg(WhisperProtocol, 123, "p2pSyncRequest", p2pSyncRequestThunk,
|
||||
messagePrinter[p2pSyncRequestObj],
|
||||
requestResolver[p2pSyncRequestObj],
|
||||
nextMsgResolver[p2pSyncRequestObj])
|
||||
registerMsg(WhisperProtocol, 125, "p2pRequestComplete", p2pRequestCompleteThunk,
|
||||
messagePrinter[p2pRequestCompleteObj],
|
||||
requestResolver[p2pRequestCompleteObj],
|
||||
nextMsgResolver[p2pRequestCompleteObj])
|
||||
proc WhisperPeerConnected(peer: Peer) {.gcsafe, async.} =
|
||||
type
|
||||
CurrentProtocol = Whisper
|
||||
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
|
||||
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
|
||||
|
||||
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
|
||||
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
|
||||
WhisperProtocol))
|
||||
|
||||
trace "onPeerConnected Whisper"
|
||||
let
|
||||
whisperNet = peer.networkState
|
||||
whisperPeer = peer.state
|
||||
let m = await peer.status(whisperVersion,
|
||||
cast[uint64](whisperNet.config.powRequirement),
|
||||
@(whisperNet.config.bloom),
|
||||
whisperNet.config.isLightNode,
|
||||
timeout = chronos.milliseconds(5000))
|
||||
if m.protocolVersion == whisperVersion:
|
||||
debug "Whisper peer", peer, whisperVersion
|
||||
else:
|
||||
raise newException(UselessPeerError, "Incompatible Whisper version")
|
||||
whisperPeer.powRequirement = cast[float64](m.powConverted)
|
||||
if m.bloom.len > 0:
|
||||
if m.bloom.len != bloomSize:
|
||||
raise newException(UselessPeerError, "Bloomfilter size mismatch")
|
||||
else:
|
||||
whisperPeer.bloom.bytesCopy(m.bloom)
|
||||
else:
|
||||
whisperPeer.bloom = fullBloom()
|
||||
whisperPeer.isLightNode = m.isLightNode
|
||||
if whisperPeer.isLightNode and whisperNet.config.isLightNode:
|
||||
raise newException(UselessPeerError, "Two light nodes connected")
|
||||
whisperPeer.received.init()
|
||||
whisperPeer.trusted = false
|
||||
whisperPeer.initialized = true
|
||||
if not whisperNet.config.isLightNode:
|
||||
traceAsyncErrors peer.run()
|
||||
debug "Whisper peer initialized", peer
|
||||
|
||||
setEventHandlers(WhisperProtocol, WhisperPeerConnected, nil)
|
||||
registerProtocol(WhisperProtocol)
|
|
@ -9,12 +9,13 @@ var
|
|||
node2: EthereumNode
|
||||
peer: Peer
|
||||
|
||||
let rng = newRng()
|
||||
# This is not a good example of a fuzzing test and it would be much better
|
||||
# to mock more to get rid of anything sockets, async, etc.
|
||||
# However, it can and has provided reasonably quick results anyhow.
|
||||
init:
|
||||
node1 = setupTestNode(eth, Whisper)
|
||||
node2 = setupTestNode(eth, Whisper)
|
||||
node1 = setupTestNode(rng, eth, Whisper)
|
||||
node2 = setupTestNode(rng, eth, Whisper)
|
||||
|
||||
node2.startListening()
|
||||
peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
|
||||
|
|
|
@ -14,6 +14,8 @@ import eth/keys, eth/keyfile/[keyfile], json, os, unittest
|
|||
# Test vectors copied from
|
||||
# https://github.com/ethereum/tests/blob/develop/KeyStoreTests/basic_tests.json
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
var TestVectors = [
|
||||
%*{
|
||||
"keyfile": {
|
||||
|
@ -114,7 +116,7 @@ suite "KeyFile test suite":
|
|||
check:
|
||||
seckey.error == KeyFileError.IncorrectMac
|
||||
test "Create/Save/Load test":
|
||||
var seckey0 = PrivateKey.random()[]
|
||||
var seckey0 = PrivateKey.random(rng[])
|
||||
let jobject = createKeyFileJson(seckey0, "randompassword")[]
|
||||
|
||||
check:
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
{.used.}
|
||||
|
||||
import unittest
|
||||
import eth/keys
|
||||
import eth/keys, bearssl
|
||||
import nimcrypto/hash, nimcrypto/keccak, nimcrypto/utils
|
||||
from strutils import toLowerAscii
|
||||
|
||||
|
@ -25,6 +25,7 @@ proc compare(x: openarray[byte], y: openarray[byte]): bool =
|
|||
break
|
||||
|
||||
let message = "message".toBytes()
|
||||
let rng = newRng()
|
||||
|
||||
const
|
||||
pkbytes = "58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d"
|
||||
|
@ -80,7 +81,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||
var s = PrivateKey.fromHex(pkbytes)[]
|
||||
var mhash = keccak256.digest(message)
|
||||
var signature = s.sign(message)
|
||||
var p = recover(signature, mhash)[]
|
||||
var p = recover(signature, SkMessage(mhash.data))[]
|
||||
check:
|
||||
s.toPublicKey() == p
|
||||
|
||||
|
@ -130,7 +131,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||
|
||||
test "EIP-55 100 addresses":
|
||||
for i in 1..100:
|
||||
var kp = KeyPair.random()[]
|
||||
var kp = KeyPair.random(rng[])
|
||||
var chaddress = kp.pubkey.toChecksumAddress()
|
||||
var noaddress = kp.pubkey.toAddress()
|
||||
if noaddress != chaddress:
|
||||
|
@ -206,9 +207,9 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||
|
||||
var s = PrivateKey.fromRaw(keccak256.digest("sec").data)[]
|
||||
var m = keccak256.digest("msg")
|
||||
var sig = sign(s, m)
|
||||
var sig = sign(s, SkMessage(m.data))
|
||||
var sersig = sig.toRaw()
|
||||
var key = recover(sig, m)[]
|
||||
var key = recover(sig, SkMessage(m.data))[]
|
||||
var serkey = key.toRaw()
|
||||
check:
|
||||
compare(sersig, check1) == true
|
||||
|
@ -217,18 +218,19 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||
test "ECDSA/100 signatures":
|
||||
# signature test
|
||||
for i in 1..100:
|
||||
var m = PrivateKey.random()[].toRaw
|
||||
var s = PrivateKey.random()[]
|
||||
var m: array[32, byte]
|
||||
brHmacDrbgGenerate(rng[], m)
|
||||
var s = PrivateKey.random(rng[])
|
||||
var key = s.toPublicKey()
|
||||
let sig = sign(s, m)
|
||||
let rkey = recover(sig, m)[]
|
||||
let sig = sign(s, SkMessage(m))
|
||||
let rkey = recover(sig, SkMessage(m))[]
|
||||
check:
|
||||
key == rkey
|
||||
|
||||
test "KEYS/100 create/recovery keys":
|
||||
# key create/recovery test
|
||||
for i in 1..100:
|
||||
var s = PrivateKey.random()[]
|
||||
var s = PrivateKey.random(rng[])
|
||||
var key = s.toPublicKey()
|
||||
let rkey = PublicKey.fromRaw(key.toRaw())[]
|
||||
check:
|
||||
|
@ -237,9 +239,9 @@ suite "ECC/ECDSA/ECDHE tests suite":
|
|||
test "ECDHE/100 shared secrets":
|
||||
# ECDHE shared secret test
|
||||
for i in 1..100:
|
||||
var aliceSecret = PrivateKey.random()[]
|
||||
var aliceSecret = PrivateKey.random(rng[])
|
||||
var alicePublic = aliceSecret.toPublicKey()
|
||||
var bobSecret = PrivateKey.random()[]
|
||||
var bobSecret = PrivateKey.random(rng[])
|
||||
var bobPublic = bobSecret.toPublicKey()
|
||||
var secret1 = ecdhRaw(aliceSecret, bobPublic)
|
||||
var secret2 = ecdhRaw(bobSecret, alicePublic)
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import
|
||||
testutils/unittests, stew/shims/net, nimcrypto,
|
||||
testutils/unittests, stew/shims/net, bearssl,
|
||||
eth/[keys, rlp, trie/db],
|
||||
eth/p2p/discoveryv5/[discovery_db, enr, node, types, routing_table, encoding],
|
||||
eth/p2p/discoveryv5/protocol as discv5_protocol
|
||||
|
||||
|
||||
proc localAddress*(port: int): Address =
|
||||
Address(ip: ValidIpAddress.init("127.0.0.1"), port: Port(port))
|
||||
|
||||
proc initDiscoveryNode*(privKey: PrivateKey, address: Address,
|
||||
proc initDiscoveryNode*(rng: ref BrHmacDrbgContext, privKey: PrivateKey, address: Address,
|
||||
bootstrapRecords: openarray[Record] = [],
|
||||
localEnrFields: openarray[FieldPair] = []):
|
||||
discv5_protocol.Protocol =
|
||||
|
@ -17,7 +16,7 @@ proc initDiscoveryNode*(privKey: PrivateKey, address: Address,
|
|||
some(address.ip),
|
||||
address.port, address.port,
|
||||
bootstrapRecords = bootstrapRecords,
|
||||
localEnrFields = localEnrFields)
|
||||
localEnrFields = localEnrFields, rng = rng)
|
||||
|
||||
result.open()
|
||||
|
||||
|
@ -26,33 +25,34 @@ proc nodeIdInNodes*(id: NodeId, nodes: openarray[Node]): bool =
|
|||
if id == n.id: return true
|
||||
|
||||
# Creating a random packet with specific nodeid each time
|
||||
proc randomPacket*(tag: PacketTag): seq[byte] =
|
||||
proc randomPacket*(rng: var BrHmacDrbgContext, tag: PacketTag): seq[byte] =
|
||||
var
|
||||
authTag: AuthTag
|
||||
msg: array[44, byte]
|
||||
|
||||
check randomBytes(authTag) == authTag.len
|
||||
check randomBytes(msg) == msg.len
|
||||
brHmacDrbgGenerate(rng, authTag)
|
||||
brHmacDrbgGenerate(rng, msg)
|
||||
result.add(tag)
|
||||
result.add(rlp.encode(authTag))
|
||||
result.add(msg)
|
||||
|
||||
proc generateNode*(privKey = PrivateKey.random()[], port: int = 20302,
|
||||
proc generateNode*(privKey: PrivateKey, port: int = 20302,
|
||||
localEnrFields: openarray[FieldPair] = []): Node =
|
||||
let port = Port(port)
|
||||
let enr = enr.Record.init(1, privKey, some(ValidIpAddress.init("127.0.0.1")),
|
||||
port, port, localEnrFields).expect("Properly intialized private key")
|
||||
result = newNode(enr).expect("Properly initialized node")
|
||||
|
||||
proc nodeAtDistance*(n: Node, d: uint32): Node =
|
||||
proc nodeAtDistance*(n: Node, rng: var BrHmacDrbgContext, d: uint32): Node =
|
||||
while true:
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng))
|
||||
if logDist(n.id, node.id) == d:
|
||||
return node
|
||||
|
||||
proc nodesAtDistance*(n: Node, d: uint32, amount: int): seq[Node] =
|
||||
proc nodesAtDistance*(
|
||||
n: Node, rng: var BrHmacDrbgContext, d: uint32, amount: int): seq[Node] =
|
||||
for i in 0..<amount:
|
||||
result.add(nodeAtDistance(n, d))
|
||||
result.add(nodeAtDistance(n, rng, d))
|
||||
|
||||
proc addSeenNode*(d: discv5_protocol.Protocol, n: Node): bool =
|
||||
# Add it as a seen node, warning: for testing convenience only!
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import
|
||||
unittest, chronos, nimcrypto, strutils,
|
||||
unittest, chronos, nimcrypto, strutils, bearssl,
|
||||
eth/[keys, p2p], eth/p2p/[discovery, enode]
|
||||
|
||||
var nextPort = 30303
|
||||
|
@ -15,17 +15,13 @@ proc startDiscoveryNode*(privKey: PrivateKey, address: Address,
|
|||
result.open()
|
||||
await result.bootstrap()
|
||||
|
||||
proc setupBootNode*(): Future[ENode] {.async.} =
|
||||
let
|
||||
bootNodeKey = KeyPair.random()[]
|
||||
bootNodeAddr = localAddress(30301)
|
||||
bootNode = await startDiscoveryNode(bootNodeKey.seckey, bootNodeAddr, @[])
|
||||
result = ENode(pubkey: bootNodeKey.pubkey, address: bootNodeAddr)
|
||||
|
||||
proc setupTestNode*(capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode =
|
||||
let keys1 = KeyPair.random()[]
|
||||
proc setupTestNode*(
|
||||
rng: ref BrHmacDrbgContext,
|
||||
capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode {.gcsafe.} =
|
||||
# Don't create new RNG every time in production code!
|
||||
let keys1 = KeyPair.random(rng[])
|
||||
result = newEthereumNode(keys1, localAddress(nextPort), 1, nil,
|
||||
addAllCapabilities = false)
|
||||
addAllCapabilities = false, rng = rng)
|
||||
nextPort.inc
|
||||
for capability in capabilities:
|
||||
result.addCapability capability
|
||||
|
|
|
@ -199,6 +199,8 @@ const eip8data = [
|
|||
"0c7ec6340062cc46f5e9f1e3cf86f8c8c403c5a0964f5df0ebd34a75ddc86db5")
|
||||
]
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
proc testValue(s: string): string =
|
||||
for item in data:
|
||||
if item[0] == s:
|
||||
|
@ -212,26 +214,21 @@ proc testE8Value(s: string): string =
|
|||
break
|
||||
|
||||
suite "Ethereum P2P handshake test suite":
|
||||
|
||||
block:
|
||||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||
if Initiator in flags:
|
||||
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
|
||||
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey())
|
||||
result = Handshake.tryInit(kp, flags)[]
|
||||
result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
|
||||
|
||||
let epki = testValue("initiator_ephemeral_private_key")
|
||||
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
|
||||
result.ephemeral = PrivateKey.fromHex(epki)[].toKeyPair()
|
||||
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||
elif Responder in flags:
|
||||
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
|
||||
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey())
|
||||
result = Handshake.tryInit(kp, flags)[]
|
||||
result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
|
||||
let epkr = testValue("receiver_ephemeral_private_key")
|
||||
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
|
||||
result.ephemeral = PrivateKey.fromHex(epkr)[].toKeyPair()
|
||||
let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
|
||||
result.responderNonce[0..^1] = nonce[0..^1]
|
||||
|
||||
|
@ -241,7 +238,7 @@ suite "Ethereum P2P handshake test suite":
|
|||
var m0 = newSeq[byte](initiator.authSize(false))
|
||||
var k0 = 0
|
||||
initiator.authMessage(
|
||||
responder.host.pubkey, m0, k0, 0, false).expect("auth success")
|
||||
rng[], responder.host.pubkey, m0, k0, 0, false).expect("auth success")
|
||||
var expect1 = fromHex(stripSpaces(testValue("auth_plaintext")))
|
||||
var expect2 = fromHex(stripSpaces(pyevmAuth))
|
||||
check:
|
||||
|
@ -257,7 +254,7 @@ suite "Ethereum P2P handshake test suite":
|
|||
let remoteHPubkey0 = initiator.host.pubkey
|
||||
|
||||
initiator.authMessage(
|
||||
responder.host.pubkey, m0, k0).expect("auth success")
|
||||
rng[], responder.host.pubkey, m0, k0).expect("auth success")
|
||||
responder.decodeAuthMessage(m0).expect("decode success")
|
||||
check:
|
||||
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||
|
@ -273,9 +270,9 @@ suite "Ethereum P2P handshake test suite":
|
|||
var k1 = 0
|
||||
var expect0 = fromHex(stripSpaces(testValue("authresp_plaintext")))
|
||||
initiator.authMessage(
|
||||
responder.host.pubkey, m0, k0).expect("auth success")
|
||||
rng[], responder.host.pubkey, m0, k0).expect("auth success")
|
||||
responder.decodeAuthMessage(m0).expect("decode success")
|
||||
responder.ackMessage(m1, k1, 0, false).expect("ack success")
|
||||
responder.ackMessage(rng[], m1, k1, 0, false).expect("ack success")
|
||||
check:
|
||||
m1 == expect0
|
||||
responder.initiatorNonce == initiator.initiatorNonce
|
||||
|
@ -289,9 +286,9 @@ suite "Ethereum P2P handshake test suite":
|
|||
var k1 = 0
|
||||
|
||||
initiator.authMessage(
|
||||
responder.host.pubkey, m0, k0).expect("auth success")
|
||||
rng[], responder.host.pubkey, m0, k0).expect("auth success")
|
||||
responder.decodeAuthMessage(m0).expect("decode success")
|
||||
responder.ackMessage(m1, k1).expect("ack success")
|
||||
responder.ackMessage(rng[], m1, k1).expect("ack success")
|
||||
initiator.decodeAckMessage(m1).expect("decode success")
|
||||
let remoteEPubkey0 = responder.ephemeral.pubkey
|
||||
let remoteHPubkey0 = responder.host.pubkey
|
||||
|
@ -333,23 +330,18 @@ suite "Ethereum P2P handshake test suite":
|
|||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||
if Initiator in flags:
|
||||
let pk = PrivateKey.fromHex(testE8Value("initiator_private_key"))[]
|
||||
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey())
|
||||
result = Handshake.tryInit(kp, flags)[]
|
||||
result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
|
||||
|
||||
result.host.pubkey = result.host.seckey.toPublicKey()
|
||||
let esec = testE8Value("initiator_ephemeral_private_key")
|
||||
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
|
||||
result.ephemeral = PrivateKey.fromHex(esec)[].toKeyPair()
|
||||
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
|
||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||
elif Responder in flags:
|
||||
let pk = PrivateKey.fromHex(testE8Value("receiver_private_key"))[]
|
||||
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey())
|
||||
result = Handshake.tryInit(kp, flags)[]
|
||||
result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
|
||||
|
||||
let esec = testE8Value("receiver_ephemeral_private_key")
|
||||
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
|
||||
result.ephemeral = PrivateKey.fromHex(esec)[].toKeyPair()
|
||||
let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce")))
|
||||
result.responderNonce[0..^1] = nonce[0..^1]
|
||||
|
||||
|
@ -436,12 +428,12 @@ suite "Ethereum P2P handshake test suite":
|
|||
var k0 = 0
|
||||
var k1 = 0
|
||||
initiator.authMessage(
|
||||
responder.host.pubkey, m0, k0).expect("auth success")
|
||||
rng[], responder.host.pubkey, m0, k0).expect("auth success")
|
||||
m0.setLen(k0)
|
||||
responder.decodeAuthMessage(m0).expect("decode success")
|
||||
check (EIP8 in responder.flags) == true
|
||||
var m1 = newSeq[byte](responder.ackSize())
|
||||
responder.ackMessage(m1, k1).expect("ack success")
|
||||
responder.ackMessage(rng[], m1, k1).expect("ack success")
|
||||
m1.setLen(k1)
|
||||
initiator.decodeAckMessage(m1).expect("decode success")
|
||||
var csecInitiator = initiator.getSecrets(m0, m1).expect("secrets")
|
||||
|
@ -458,11 +450,11 @@ suite "Ethereum P2P handshake test suite":
|
|||
var k0 = 0
|
||||
var k1 = 0
|
||||
initiator.authMessage(
|
||||
responder.host.pubkey, m0, k0).expect("auth success")
|
||||
rng[], responder.host.pubkey, m0, k0).expect("auth success")
|
||||
m0.setLen(k0)
|
||||
responder.decodeAuthMessage(m0).expect("auth success")
|
||||
var m1 = newSeq[byte](responder.ackSize())
|
||||
responder.ackMessage(m1, k1).expect("ack success")
|
||||
responder.ackMessage(rng[], m1, k1).expect("ack success")
|
||||
m1.setLen(k1)
|
||||
initiator.decodeAckMessage(m1).expect("ack success")
|
||||
|
||||
|
|
|
@ -80,6 +80,8 @@ const data = [
|
|||
e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8""")
|
||||
]
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
proc testValue(s: string): string =
|
||||
for item in data:
|
||||
if item[0] == s:
|
||||
|
@ -90,20 +92,16 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
|||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||
if Initiator in flags:
|
||||
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
|
||||
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey())
|
||||
result = Handshake.tryInit(kp, flags)[]
|
||||
result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
|
||||
let epki = testValue("initiator_ephemeral_private_key")
|
||||
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
|
||||
result.ephemeral = PrivateKey.fromHex(epki)[].toKeyPair()
|
||||
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||
elif Responder in flags:
|
||||
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
|
||||
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey())
|
||||
result = Handshake.tryInit(kp, flags)[]
|
||||
result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
|
||||
let epkr = testValue("receiver_ephemeral_private_key")
|
||||
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
|
||||
result.ephemeral = PrivateKey.fromHex(epkr)[].toKeyPair()
|
||||
let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
|
||||
result.responderNonce[0..^1] = nonce[0..^1]
|
||||
|
||||
|
@ -174,12 +172,12 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
|||
var m0 = newSeq[byte](initiator.authSize())
|
||||
var k0 = 0
|
||||
var k1 = 0
|
||||
check initiator.authMessage(responder.host.pubkey,
|
||||
check initiator.authMessage(rng[], responder.host.pubkey,
|
||||
m0, k0).isOk
|
||||
m0.setLen(k0)
|
||||
check responder.decodeAuthMessage(m0).isOk
|
||||
var m1 = newSeq[byte](responder.ackSize())
|
||||
check responder.ackMessage(m1, k1).isOk
|
||||
check responder.ackMessage(rng[], m1, k1).isOk
|
||||
m1.setLen(k1)
|
||||
check initiator.decodeAckMessage(m1).isOk
|
||||
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
import
|
||||
chronos, chronicles, tables, stint, nimcrypto, testutils/unittests,
|
||||
stew/shims/net, eth/keys,
|
||||
chronos, chronicles, tables, stint, testutils/unittests,
|
||||
stew/shims/net, eth/keys, bearssl,
|
||||
eth/p2p/discoveryv5/[enr, node, types, routing_table, encoding],
|
||||
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||
./discv5_test_helper
|
||||
|
||||
procSuite "Discovery v5 Tests":
|
||||
let rng = newRng()
|
||||
|
||||
asyncTest "GetNode":
|
||||
# TODO: This could be tested in just a routing table only context
|
||||
let
|
||||
node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302))
|
||||
targetNode = generateNode()
|
||||
node = initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
targetNode = generateNode(PrivateKey.random(rng[]))
|
||||
|
||||
check node.addNode(targetNode)
|
||||
|
||||
for i in 0..<1000:
|
||||
discard node.addNode(generateNode())
|
||||
discard node.addNode(generateNode(PrivateKey.random(rng[])))
|
||||
|
||||
let n = node.getNode(targetNode.id)
|
||||
check n.isSome()
|
||||
|
@ -25,10 +27,13 @@ procSuite "Discovery v5 Tests":
|
|||
|
||||
asyncTest "Node deletion":
|
||||
let
|
||||
bootnode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301))
|
||||
node1 = initDiscoveryNode(PrivateKey.random()[], localAddress(20302),
|
||||
bootnode = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20301))
|
||||
node1 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302),
|
||||
@[bootnode.localNode.record])
|
||||
node2 = initDiscoveryNode(PrivateKey.random()[], localAddress(20303),
|
||||
node2 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20303),
|
||||
@[bootnode.localNode.record])
|
||||
pong1 = await discv5_protocol.ping(node1, bootnode.localNode)
|
||||
pong2 = await discv5_protocol.ping(node1, node2.localNode)
|
||||
|
@ -51,13 +56,14 @@ procSuite "Discovery v5 Tests":
|
|||
|
||||
|
||||
asyncTest "Handshake cleanup":
|
||||
let node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302))
|
||||
let node = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
var tag: PacketTag
|
||||
let a = localAddress(20303)
|
||||
|
||||
for i in 0 ..< 5:
|
||||
check randomBytes(tag) == tag.len
|
||||
node.receive(a, randomPacket(tag))
|
||||
brHmacDrbgGenerate(rng[], tag)
|
||||
node.receive(a, randomPacket(rng[], tag))
|
||||
|
||||
# Checking different nodeIds but same address
|
||||
check node.codec.handshakes.len == 5
|
||||
|
@ -70,24 +76,26 @@ procSuite "Discovery v5 Tests":
|
|||
await node.closeWait()
|
||||
|
||||
asyncTest "Handshake different address":
|
||||
let node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302))
|
||||
let node = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
var tag: PacketTag
|
||||
|
||||
for i in 0 ..< 5:
|
||||
let a = localAddress(20303 + i)
|
||||
node.receive(a, randomPacket(tag))
|
||||
node.receive(a, randomPacket(rng[], tag))
|
||||
|
||||
check node.codec.handshakes.len == 5
|
||||
|
||||
await node.closeWait()
|
||||
|
||||
asyncTest "Handshake duplicates":
|
||||
let node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302))
|
||||
let node = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
var tag: PacketTag
|
||||
let a = localAddress(20303)
|
||||
|
||||
for i in 0 ..< 5:
|
||||
node.receive(a, randomPacket(tag))
|
||||
node.receive(a, randomPacket(rng[], tag))
|
||||
|
||||
# Checking handshake duplicates
|
||||
check node.codec.handshakes.len == 1
|
||||
|
@ -177,11 +185,11 @@ procSuite "Discovery v5 Tests":
|
|||
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
|
||||
testNodeKey = PrivateKey.fromHex(
|
||||
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a618")[]
|
||||
mainNode = initDiscoveryNode(mainNodeKey, localAddress(20301))
|
||||
testNode = initDiscoveryNode(testNodeKey, localAddress(20302))
|
||||
mainNode = initDiscoveryNode(rng, mainNodeKey, localAddress(20301))
|
||||
testNode = initDiscoveryNode(rng, testNodeKey, localAddress(20302))
|
||||
# logarithmic distance between mainNode and testNode is 256
|
||||
|
||||
let nodes = nodesAtDistance(mainNode.localNode, dist, 10)
|
||||
let nodes = nodesAtDistance(mainNode.localNode, rng[], dist, 10)
|
||||
for n in nodes:
|
||||
discard mainNode.addSeenNode(n) # for testing only!
|
||||
|
||||
|
@ -218,7 +226,7 @@ procSuite "Discovery v5 Tests":
|
|||
check discovered.isOk
|
||||
check discovered[].len == 0
|
||||
|
||||
let moreNodes = nodesAtDistance(mainNode.localNode, dist, 10)
|
||||
let moreNodes = nodesAtDistance(mainNode.localNode, rng[], dist, 10)
|
||||
for n in moreNodes:
|
||||
discard mainNode.addSeenNode(n) # for testing only!
|
||||
|
||||
|
@ -233,11 +241,12 @@ procSuite "Discovery v5 Tests":
|
|||
|
||||
asyncTest "FindNode with test table":
|
||||
|
||||
let mainNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301))
|
||||
let mainNode =
|
||||
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
|
||||
|
||||
# Generate 1000 random nodes and add to our main node's routing table
|
||||
for i in 0..<1000:
|
||||
discard mainNode.addSeenNode(generateNode()) # for testing only!
|
||||
discard mainNode.addSeenNode(generateNode(PrivateKey.random(rng[]))) # for testing only!
|
||||
|
||||
let
|
||||
neighbours = mainNode.neighbours(mainNode.localNode.id)
|
||||
|
@ -247,7 +256,8 @@ procSuite "Discovery v5 Tests":
|
|||
debug "Closest neighbour", closestDistance, id=closest.id.toHex()
|
||||
|
||||
let
|
||||
testNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20302),
|
||||
testNode = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302),
|
||||
@[mainNode.localNode.record])
|
||||
discovered = await discv5_protocol.findNode(testNode, mainNode.localNode,
|
||||
closestDistance)
|
||||
|
@ -262,13 +272,14 @@ procSuite "Discovery v5 Tests":
|
|||
const
|
||||
nodeCount = 17
|
||||
|
||||
let bootNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301))
|
||||
let bootNode =
|
||||
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
|
||||
bootNode.start()
|
||||
|
||||
var nodes = newSeqOfCap[discv5_protocol.Protocol](nodeCount)
|
||||
nodes.add(bootNode)
|
||||
for i in 1 ..< nodeCount:
|
||||
nodes.add(initDiscoveryNode(PrivateKey.random()[], localAddress(20301 + i),
|
||||
nodes.add(initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301 + i),
|
||||
@[bootNode.localNode.record]))
|
||||
|
||||
# Make sure all nodes have "seen" each other by forcing pings
|
||||
|
@ -292,11 +303,13 @@ procSuite "Discovery v5 Tests":
|
|||
|
||||
asyncTest "Resolve target":
|
||||
let
|
||||
mainNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301))
|
||||
lookupNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20302))
|
||||
targetKey = PrivateKey.random()[]
|
||||
mainNode =
|
||||
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
|
||||
lookupNode =
|
||||
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
targetKey = PrivateKey.random(rng[])
|
||||
targetAddress = localAddress(20303)
|
||||
targetNode = initDiscoveryNode(targetKey, targetAddress)
|
||||
targetNode = initDiscoveryNode(rng, targetKey, targetAddress)
|
||||
targetId = targetNode.localNode.id
|
||||
|
||||
var targetSeqNum = targetNode.localNode.record.seqNum
|
||||
|
@ -362,12 +375,12 @@ procSuite "Discovery v5 Tests":
|
|||
|
||||
asyncTest "Random nodes with enr field filter":
|
||||
let
|
||||
lookupNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301))
|
||||
lookupNode = initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
|
||||
targetFieldPair = toFieldPair("test", @[byte 1,2,3,4])
|
||||
targetNode = generateNode(localEnrFields = [targetFieldPair])
|
||||
targetNode = generateNode(PrivateKey.random(rng[]), localEnrFields = [targetFieldPair])
|
||||
otherFieldPair = toFieldPair("test", @[byte 1,2,3,4,5])
|
||||
otherNode = generateNode(localEnrFields = [otherFieldPair])
|
||||
anotherNode = generateNode()
|
||||
otherNode = generateNode(PrivateKey.random(rng[]), localEnrFields = [otherFieldPair])
|
||||
anotherNode = generateNode(PrivateKey.random(rng[]))
|
||||
|
||||
check:
|
||||
lookupNode.addNode(targetNode)
|
||||
|
|
|
@ -5,6 +5,8 @@ import
|
|||
# According to test vectors:
|
||||
# https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
suite "Discovery v5 Packet Encodings":
|
||||
# TODO: These tests are currently not completely representative for the code
|
||||
# and thus will not necessarily notice failures. Refactor/restructure code
|
||||
|
@ -230,19 +232,19 @@ suite "Discovery v5 Additional":
|
|||
|
||||
test "AuthHeader encode/decode":
|
||||
let
|
||||
privKey = PrivateKey.random()[]
|
||||
privKey = PrivateKey.random(rng[])
|
||||
enrRec = enr.Record.init(1, privKey, none(ValidIpAddress), Port(9000),
|
||||
Port(9000)).expect("Properly intialized private key")
|
||||
node = newNode(enrRec).expect("Properly initialized record")
|
||||
nonce = hexToByteArray[authTagSize]("0x27b5af763c446acd2749fe8e")
|
||||
pubKey = PrivateKey.random()[].toPublicKey()
|
||||
pubKey = PrivateKey.random(rng[]).toPublicKey()
|
||||
nodeId = pubKey.toNodeId()
|
||||
idNonce = hexToByteArray[idNonceSize](
|
||||
"0xa77e3aa0c144ae7c0a3af73692b7d6e5b7a2fdc0eda16e8d5e6cb0d08e88dd04")
|
||||
whoareyou = Whoareyou(idNonce: idNonce, recordSeq: 0, pubKey: pubKey)
|
||||
c = Codec(localNode: node, privKey: privKey)
|
||||
|
||||
let (auth, _) = c.encodeAuthHeader(nodeId, nonce, whoareyou)[]
|
||||
let (auth, _) = encodeAuthHeader(rng[], c, nodeId, nonce, whoareyou)
|
||||
var rlp = rlpFromBytes(auth)
|
||||
let authHeader = rlp.read(AuthHeader)
|
||||
var newNode: Node
|
||||
|
|
|
@ -23,6 +23,8 @@ proc compare[A, B](x: openarray[A], y: openarray[B], s: int = 0): bool =
|
|||
template offsetOf(a, b): int =
|
||||
cast[int](cast[uint](unsafeAddr b) - cast[uint](unsafeAddr a))
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
suite "ECIES test suite":
|
||||
test "ECIES structures alignment":
|
||||
var header: EciesHeader
|
||||
|
@ -69,17 +71,17 @@ suite "ECIES test suite":
|
|||
var encr = newSeq[byte](eciesEncryptedLength(len(m)))
|
||||
var decr = newSeq[byte](len(m))
|
||||
var shmac = [0x13'u8, 0x13'u8]
|
||||
var s = PrivateKey.random()[]
|
||||
var s = PrivateKey.random(rng[])
|
||||
var p = s.toPublicKey()
|
||||
|
||||
eciesEncrypt(plain, encr, p).expect("encryption should succeed")
|
||||
eciesEncrypt(rng[], plain, encr, p).expect("encryption should succeed")
|
||||
eciesDecrypt(encr, decr, s).expect("decryption should succeed")
|
||||
|
||||
check:
|
||||
# Without additional mac data
|
||||
equalMem(addr m[0], addr decr[0], len(m))
|
||||
# With additional mac data
|
||||
eciesEncrypt(plain, encr, p, shmac).expect("encryption should succeed")
|
||||
eciesEncrypt(rng[], plain, encr, p, shmac).expect("encryption should succeed")
|
||||
eciesDecrypt(encr, decr, s, shmac).expect("decryption should succeed")
|
||||
|
||||
check:
|
||||
|
|
|
@ -3,6 +3,8 @@ import
|
|||
nimcrypto/utils, stew/shims/net,
|
||||
eth/p2p/enode, eth/p2p/discoveryv5/enr, eth/keys
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
suite "ENR":
|
||||
test "Serialization":
|
||||
var pk = PrivateKey.fromHex(
|
||||
|
@ -33,7 +35,7 @@ suite "ENR":
|
|||
|
||||
test "Create from ENode address":
|
||||
let
|
||||
keys = KeyPair.random()[]
|
||||
keys = KeyPair.random(rng[])
|
||||
ip = ValidIpAddress.init("10.20.30.40")
|
||||
enr = Record.init(100, keys.seckey, some(ip), Port(9000), Port(9000), @[])[]
|
||||
typedEnr = get enr.toTypedRecord()
|
||||
|
@ -53,7 +55,7 @@ suite "ENR":
|
|||
|
||||
test "ENR without address":
|
||||
let
|
||||
keys = KeyPair.random()[]
|
||||
keys = KeyPair.random(rng[])
|
||||
enr = Record.init(100, keys.seckey, none(ValidIpAddress), Port(9000), Port(9000))[]
|
||||
typedEnr = get enr.toTypedRecord()
|
||||
|
||||
|
|
|
@ -52,11 +52,13 @@ p2pProtocol hah(version = 1,
|
|||
onPeerDisconnected do (peer: Peer, reason: DisconnectionReason) {.gcsafe.}:
|
||||
peer.networkState.count -= 1
|
||||
|
||||
|
||||
suite "Testing protocol handlers":
|
||||
asyncTest "Failing disconnection handler":
|
||||
let bootENode = await setupBootNode()
|
||||
var node1 = setupTestNode(abc, xyz)
|
||||
var node2 = setupTestNode(abc, xyz)
|
||||
let rng = newRng()
|
||||
|
||||
var node1 = setupTestNode(rng, abc, xyz)
|
||||
var node2 = setupTestNode(rng, abc, xyz)
|
||||
|
||||
node2.startListening()
|
||||
let peer = await node1.rlpxConnect(newNode(node2.toENode()))
|
||||
|
@ -71,8 +73,10 @@ suite "Testing protocol handlers":
|
|||
node1.protocolState(xyz).count == 0
|
||||
|
||||
asyncTest "Failing connection handler":
|
||||
var node1 = setupTestNode(hah)
|
||||
var node2 = setupTestNode(hah)
|
||||
let rng = newRng()
|
||||
|
||||
var node1 = setupTestNode(rng, hah)
|
||||
var node2 = setupTestNode(rng, hah)
|
||||
node2.startListening()
|
||||
let peer = await node1.rlpxConnect(newNode(node2.toENode()))
|
||||
check:
|
||||
|
|
|
@ -3,17 +3,14 @@ import
|
|||
eth/p2p, eth/p2p/rlpx_protocols/[whisper_protocol, eth_protocol],
|
||||
../p2p/p2p_test_helper
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
var
|
||||
node1: EthereumNode
|
||||
node2: EthereumNode
|
||||
peer: Peer
|
||||
|
||||
|
||||
node1 = setupTestNode(eth, Whisper)
|
||||
node2 = setupTestNode(eth, Whisper)
|
||||
node1 = setupTestNode(rng, eth, Whisper)
|
||||
node2 = setupTestNode(rng, eth, Whisper)
|
||||
|
||||
node2.startListening()
|
||||
peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
|
||||
var peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
|
||||
|
||||
proc testThunk(payload: openArray[byte]) =
|
||||
var (msgId, msgData) = recvMsgMock(payload)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import
|
||||
unittest,
|
||||
unittest, bearssl,
|
||||
eth/keys,
|
||||
eth/p2p/discoveryv5/[routing_table, node],
|
||||
./discv5_test_helper
|
||||
|
||||
suite "Routing Table Tests":
|
||||
let rng = newRng()
|
||||
|
||||
test "Bucket splitting in range branch b=1":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
|
@ -13,11 +16,11 @@ suite "Routing Table Tests":
|
|||
|
||||
for j in 0..5'u32:
|
||||
for i in 0..<BUCKET_SIZE:
|
||||
check table.addNode(node.nodeAtDistance(256-j)) == nil
|
||||
check table.addNode(node.nodeAtDistance(256-j)) != nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256-j)) == nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256-j)) != nil
|
||||
|
||||
test "Bucket splitting off range branch b=1":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
|
@ -25,19 +28,19 @@ suite "Routing Table Tests":
|
|||
|
||||
# Add 16 nodes, distance 256
|
||||
for i in 0..<BUCKET_SIZE:
|
||||
check table.addNode(node.nodeAtDistance(256)) == nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256)) == nil
|
||||
|
||||
# This should split the bucket in the distance 256 branch, and the distance
|
||||
# <=255 branch. But not add the node, as distance 256 bucket is already full
|
||||
# and b=1 will not allow it to spit any further
|
||||
check table.addNode(node.nodeAtDistance(256)) != nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256)) != nil
|
||||
|
||||
# This add should be allowed as it is on the branch where the own node's id
|
||||
# id belongs to.
|
||||
check table.addNode(node.nodeAtDistance(255)) == nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 255)) == nil
|
||||
|
||||
test "Bucket splitting off range branch b=2":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 2, allow not in range branch to split once (2 buckets).
|
||||
|
@ -45,46 +48,46 @@ suite "Routing Table Tests":
|
|||
|
||||
# Add 16 nodes, distance 256 from `node`, but all with 2 bits shared prefix
|
||||
# among themselves.
|
||||
let firstNode = node.nodeAtDistance(256)
|
||||
let firstNode = node.nodeAtDistance(rng[], 256)
|
||||
check table.addNode(firstNode) == nil
|
||||
for n in 1..<BUCKET_SIZE:
|
||||
check table.addNode(firstNode.nodeAtDistance(254)) == nil
|
||||
check table.addNode(firstNode.nodeAtDistance(rng[], 254)) == nil
|
||||
|
||||
# Add 16 more nodes with only 1 bit shared prefix with previous 16. This
|
||||
# should cause the initial bucket to split and and fill the second bucket
|
||||
# with the 16 new entries.
|
||||
for n in 0..<BUCKET_SIZE:
|
||||
check table.addNode(firstNode.nodeAtDistance(255)) == nil
|
||||
check table.addNode(firstNode.nodeAtDistance(rng[], 255)) == nil
|
||||
|
||||
# Adding another should fail as both buckets will be full and not be
|
||||
# allowed to split another time.
|
||||
check table.addNode(node.nodeAtDistance(256)) != nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256)) != nil
|
||||
# And also when targetting one of the two specific buckets.
|
||||
check table.addNode(firstNode.nodeAtDistance(255)) != nil
|
||||
check table.addNode(firstNode.nodeAtDistance(254)) != nil
|
||||
check table.addNode(firstNode.nodeAtDistance(rng[], 255)) != nil
|
||||
check table.addNode(firstNode.nodeAtDistance(rng[], 254)) != nil
|
||||
# This add should be allowed as it is on the branch where the own node's id
|
||||
# id belongs to.
|
||||
check table.addNode(node.nodeAtDistance(255)) == nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 255)) == nil
|
||||
|
||||
test "Replacement cache":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
table.init(node, 1)
|
||||
|
||||
# create a full bucket
|
||||
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE)
|
||||
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
|
||||
for n in bucketNodes:
|
||||
check table.addNode(n) == nil
|
||||
|
||||
# create a full replacement cache
|
||||
let replacementNodes = node.nodesAtDistance(256, REPLACEMENT_CACHE_SIZE)
|
||||
let replacementNodes = node.nodesAtDistance(rng[], 256, REPLACEMENT_CACHE_SIZE)
|
||||
for n in replacementNodes:
|
||||
check table.addNode(n) != nil
|
||||
|
||||
# Add one more node to replacement (would drop first one)
|
||||
let lastNode = node.nodeAtDistance(256)
|
||||
let lastNode = node.nodeAtDistance(rng[], 256)
|
||||
check table.addNode(lastNode) != nil
|
||||
|
||||
# This should replace the last node in the bucket, with the last one of
|
||||
|
@ -101,7 +104,7 @@ suite "Routing Table Tests":
|
|||
check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone()
|
||||
|
||||
test "Empty bucket":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
|
@ -110,29 +113,29 @@ suite "Routing Table Tests":
|
|||
check table.nodeToRevalidate().isNil()
|
||||
|
||||
# try to replace not existing node
|
||||
table.replaceNode(generateNode())
|
||||
table.replaceNode(generateNode(PrivateKey.random(rng[])))
|
||||
check table.len == 0
|
||||
|
||||
let addedNode = generateNode()
|
||||
let addedNode = generateNode(PrivateKey.random(rng[]))
|
||||
check table.addNode(addedNode) == nil
|
||||
check table.len == 1
|
||||
|
||||
# try to replace not existing node
|
||||
table.replaceNode(generateNode())
|
||||
table.replaceNode(generateNode(PrivateKey.random(rng[])))
|
||||
check table.len == 1
|
||||
|
||||
table.replaceNode(addedNode)
|
||||
check table.len == 0
|
||||
|
||||
test "Empty replacement cache":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
table.init(node, 1)
|
||||
|
||||
# create a full bucket TODO: no need to store bucketNodes
|
||||
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE)
|
||||
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
|
||||
for n in bucketNodes:
|
||||
check table.addNode(n) == nil
|
||||
|
||||
|
@ -141,21 +144,21 @@ suite "Routing Table Tests":
|
|||
check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone()
|
||||
|
||||
test "Double add":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
table.init(node, 1)
|
||||
|
||||
let doubleNode = node.nodeAtDistance(256)
|
||||
let doubleNode = node.nodeAtDistance(rng[], 256)
|
||||
# Try to add the node twice
|
||||
check table.addNode(doubleNode) == nil
|
||||
check table.addNode(doubleNode) == nil
|
||||
|
||||
for n in 0..<BUCKET_SIZE-1:
|
||||
check table.addNode(node.nodeAtDistance(256)) == nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256)) == nil
|
||||
|
||||
check table.addNode(node.nodeAtDistance(256)) != nil
|
||||
check table.addNode(node.nodeAtDistance(rng[], 256)) != nil
|
||||
# Check when adding again once the bucket is full
|
||||
check table.addNode(doubleNode) == nil
|
||||
|
||||
|
@ -171,19 +174,19 @@ suite "Routing Table Tests":
|
|||
table.len == 1
|
||||
|
||||
test "Double replacement add":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
table.init(node, 1)
|
||||
|
||||
# create a full bucket
|
||||
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE)
|
||||
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
|
||||
for n in bucketNodes:
|
||||
check table.addNode(n) == nil
|
||||
|
||||
# create a full replacement cache
|
||||
let replacementNodes = node.nodesAtDistance(256, REPLACEMENT_CACHE_SIZE)
|
||||
let replacementNodes = node.nodesAtDistance(rng[], 256, REPLACEMENT_CACHE_SIZE)
|
||||
for n in replacementNodes:
|
||||
check table.addNode(n) != nil
|
||||
|
||||
|
@ -201,14 +204,14 @@ suite "Routing Table Tests":
|
|||
check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone()
|
||||
|
||||
test "Just seen":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
table.init(node, 1)
|
||||
|
||||
# create a full bucket
|
||||
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE)
|
||||
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
|
||||
for n in bucketNodes:
|
||||
check table.addNode(n) == nil
|
||||
|
||||
|
@ -221,19 +224,19 @@ suite "Routing Table Tests":
|
|||
check (table.getNode(n.id)).isNone()
|
||||
|
||||
test "Just seen replacement":
|
||||
let node = generateNode()
|
||||
let node = generateNode(PrivateKey.random(rng[]))
|
||||
var table: RoutingTable
|
||||
|
||||
# bitsPerHop = 1 -> Split only the branch in range of own id
|
||||
table.init(node, 1)
|
||||
|
||||
# create a full bucket
|
||||
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE)
|
||||
let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
|
||||
for n in bucketNodes:
|
||||
check table.addNode(n) == nil
|
||||
|
||||
# create a full replacement cache
|
||||
let replacementNodes = node.nodesAtDistance(256, REPLACEMENT_CACHE_SIZE)
|
||||
let replacementNodes = node.nodesAtDistance(rng[], 256, REPLACEMENT_CACHE_SIZE)
|
||||
for n in replacementNodes:
|
||||
check table.addNode(n) != nil
|
||||
|
||||
|
|
|
@ -11,10 +11,12 @@ import
|
|||
sequtils, options, unittest, tables, nimcrypto/hash,
|
||||
eth/[keys, rlp], eth/p2p/rlpx_protocols/whisper/whisper_types as whisper
|
||||
|
||||
let rng = newRng()
|
||||
|
||||
suite "Whisper payload":
|
||||
test "should roundtrip without keys":
|
||||
let payload = Payload(payload: @[byte 0, 1, 2])
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -26,7 +28,7 @@ suite "Whisper payload":
|
|||
test "should roundtrip with symmetric encryption":
|
||||
var symKey: SymKey
|
||||
let payload = Payload(symKey: some(symKey), payload: @[byte 0, 1, 2])
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get(), symKey = some(symKey))
|
||||
check:
|
||||
|
@ -36,10 +38,10 @@ suite "Whisper payload":
|
|||
decoded.get().padding.get().len == 251 # 256 -1 -1 -3
|
||||
|
||||
test "should roundtrip with signature":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
|
||||
let payload = Payload(src: some(privKey), payload: @[byte 0, 1, 2])
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -49,11 +51,11 @@ suite "Whisper payload":
|
|||
decoded.get().padding.get().len == 186 # 256 -1 -1 -3 -65
|
||||
|
||||
test "should roundtrip with asymmetric encryption":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
|
||||
let payload = Payload(dst: some(privKey.toPublicKey()),
|
||||
payload: @[byte 0, 1, 2])
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get(), dst = some(privKey))
|
||||
check:
|
||||
|
@ -74,7 +76,7 @@ suite "Whisper payload":
|
|||
suite "Whisper payload padding":
|
||||
test "should do max padding":
|
||||
let payload = Payload(payload: repeat(byte 1, 254))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -84,10 +86,10 @@ suite "Whisper payload padding":
|
|||
decoded.get().padding.get().len == 256 # as dataLen == 256
|
||||
|
||||
test "should do max padding with signature":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
|
||||
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 189))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -99,7 +101,7 @@ suite "Whisper payload padding":
|
|||
|
||||
test "should do min padding":
|
||||
let payload = Payload(payload: repeat(byte 1, 253))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -109,10 +111,10 @@ suite "Whisper payload padding":
|
|||
decoded.get().padding.get().len == 1 # as dataLen == 255
|
||||
|
||||
test "should do min padding with signature":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
|
||||
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 188))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -125,7 +127,7 @@ suite "Whisper payload padding":
|
|||
test "should roundtrip custom padding":
|
||||
let payload = Payload(payload: repeat(byte 1, 10),
|
||||
padding: some(repeat(byte 2, 100)))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -138,7 +140,7 @@ suite "Whisper payload padding":
|
|||
let padding: seq[byte] = @[]
|
||||
let payload = Payload(payload: repeat(byte 1, 10),
|
||||
padding: some(padding))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -147,10 +149,10 @@ suite "Whisper payload padding":
|
|||
decoded.get().padding.isNone()
|
||||
|
||||
test "should roundtrip custom padding with signature":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10),
|
||||
padding: some(repeat(byte 2, 100)))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -162,10 +164,10 @@ suite "Whisper payload padding":
|
|||
|
||||
test "should roundtrip custom 0 padding with signature":
|
||||
let padding: seq[byte] = @[]
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10),
|
||||
padding: some(padding))
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
|
||||
let decoded = whisper.decode(encoded.get())
|
||||
check:
|
||||
|
@ -276,7 +278,7 @@ proc prepFilterTestMsg(pubKey = none[PublicKey](), symKey = none[SymKey](),
|
|||
padding = none[seq[byte]]()): Message =
|
||||
let payload = Payload(dst: pubKey, symKey: symKey, src: src,
|
||||
payload: @[byte 0, 1, 2], padding: padding)
|
||||
let encoded = whisper.encode(payload)
|
||||
let encoded = whisper.encode(rng[], payload)
|
||||
let env = Envelope(expiry: 1, ttl: 1, topic: topic, data: encoded.get(),
|
||||
nonce: 0)
|
||||
result = initMessage(env)
|
||||
|
@ -289,7 +291,7 @@ suite "Whisper filter":
|
|||
|
||||
var filters = initTable[string, Filter]()
|
||||
let filter = initFilter(symKey = some(symKey), topics = @[topic])
|
||||
let filterId = filters.subscribeFilter(filter)
|
||||
let filterId = subscribeFilter(rng[], filters, filter)
|
||||
|
||||
notify(filters, msg)
|
||||
|
||||
|
@ -300,14 +302,14 @@ suite "Whisper filter":
|
|||
messages[0].dst.isNone()
|
||||
|
||||
test "should notify filter on message with asymmetric encryption":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
let topic = [byte 0, 0, 0, 0]
|
||||
let msg = prepFilterTestMsg(pubKey = some(privKey.toPublicKey()),
|
||||
topic = topic)
|
||||
|
||||
var filters = initTable[string, Filter]()
|
||||
let filter = initFilter(privateKey = some(privKey), topics = @[topic])
|
||||
let filterId = filters.subscribeFilter(filter)
|
||||
let filterId = subscribeFilter(rng[], filters, filter)
|
||||
|
||||
notify(filters, msg)
|
||||
|
||||
|
@ -318,14 +320,14 @@ suite "Whisper filter":
|
|||
messages[0].dst.isSome()
|
||||
|
||||
test "should notify filter on message with signature":
|
||||
let privKey = PrivateKey.random()[]
|
||||
let privKey = PrivateKey.random(rng[])
|
||||
let topic = [byte 0, 0, 0, 0]
|
||||
let msg = prepFilterTestMsg(src = some(privKey), topic = topic)
|
||||
|
||||
var filters = initTable[string, Filter]()
|
||||
let filter = initFilter(src = some(privKey.toPublicKey()),
|
||||
topics = @[topic])
|
||||
let filterId = filters.subscribeFilter(filter)
|
||||
let filterId = subscribeFilter(rng[], filters, filter)
|
||||
|
||||
notify(filters, msg)
|
||||
|
||||
|
@ -346,9 +348,9 @@ suite "Whisper filter":
|
|||
|
||||
var filters = initTable[string, Filter]()
|
||||
let
|
||||
filterId1 = filters.subscribeFilter(
|
||||
filterId1 = subscribeFilter(rng[], filters,
|
||||
initFilter(topics = @[topic], powReq = 0.014492753623188406))
|
||||
filterId2 = filters.subscribeFilter(
|
||||
filterId2 = subscribeFilter(rng[], filters,
|
||||
initFilter(topics = @[topic], powReq = 0.014492753623188407))
|
||||
|
||||
notify(filters, msg)
|
||||
|
@ -366,8 +368,8 @@ suite "Whisper filter":
|
|||
|
||||
var filters = initTable[string, Filter]()
|
||||
let
|
||||
filterId1 = filters.subscribeFilter(initFilter(topics = @[topic1]))
|
||||
filterId2 = filters.subscribeFilter(initFilter(topics = @[topic2]))
|
||||
filterId1 = subscribeFilter(rng[], filters, initFilter(topics = @[topic1]))
|
||||
filterId2 = subscribeFilter(rng[], filters, initFilter(topics = @[topic2]))
|
||||
|
||||
notify(filters, msg)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# MIT license (LICENSE-MIT)
|
||||
|
||||
import
|
||||
sequtils, options, tables, chronos, testutils/unittests,
|
||||
sequtils, options, tables, chronos, testutils/unittests, bearssl,
|
||||
eth/[keys, p2p], eth/p2p/rlpx_protocols/whisper_protocol, eth/p2p/peer_pool,
|
||||
./p2p_test_helper
|
||||
|
||||
|
@ -20,8 +20,9 @@ let safeTTL = 5'u32
|
|||
let waitInterval = messageInterval + 150.milliseconds
|
||||
|
||||
procSuite "Whisper connections":
|
||||
var node1 = setupTestNode(Whisper)
|
||||
var node2 = setupTestNode(Whisper)
|
||||
let rng = newRng()
|
||||
var node1 = setupTestNode(rng, Whisper)
|
||||
var node2 = setupTestNode(rng, Whisper)
|
||||
node2.startListening()
|
||||
waitFor node1.peerPool.connectToNode(newNode(node2.toENode()))
|
||||
asyncTest "Two peers connected":
|
||||
|
@ -29,8 +30,8 @@ procSuite "Whisper connections":
|
|||
node1.peerPool.connectedNodes.len() == 1
|
||||
|
||||
asyncTest "Filters with encryption and signing":
|
||||
let encryptKeyPair = KeyPair.random()[]
|
||||
let signKeyPair = KeyPair.random()[]
|
||||
let encryptKeyPair = KeyPair.random(rng[])
|
||||
let signKeyPair = KeyPair.random(rng[])
|
||||
var symKey: SymKey
|
||||
let topic = [byte 0x12, 0, 0, 0]
|
||||
var filters: seq[string] = @[]
|
||||
|
@ -294,7 +295,7 @@ procSuite "Whisper connections":
|
|||
node1.unsubscribeFilter(filter) == true
|
||||
|
||||
asyncTest "Light node posting":
|
||||
var ln1 = setupTestNode(Whisper)
|
||||
var ln1 = setupTestNode(rng, Whisper)
|
||||
ln1.setLightNode(true)
|
||||
|
||||
await ln1.peerPool.connectToNode(newNode(node2.toENode()))
|
||||
|
@ -313,8 +314,8 @@ procSuite "Whisper connections":
|
|||
ln1.protocolState(Whisper).queue.items.len == 0
|
||||
|
||||
asyncTest "Connect two light nodes":
|
||||
var ln1 = setupTestNode(Whisper)
|
||||
var ln2 = setupTestNode(Whisper)
|
||||
var ln1 = setupTestNode(rng, Whisper)
|
||||
var ln2 = setupTestNode(rng, Whisper)
|
||||
|
||||
ln1.setLightNode(true)
|
||||
ln2.setLightNode(true)
|
||||
|
|
Loading…
Reference in New Issue