mirror of
https://github.com/status-im/status-node-manager.git
synced 2025-02-19 16:04:20 +00:00
feat(waku noise): [wip] Add example for handshake pairing between 2 nodes
This commit is contained in:
parent
8e35d2c44c
commit
e8150ce1a3
214
libs/waku-utils/example/agentA.nim
Normal file
214
libs/waku-utils/example/agentA.nim
Normal file
@ -0,0 +1,214 @@
|
||||
import
|
||||
std/[tables,times,sequtils],
|
||||
stew/byteutils,
|
||||
stew/shims/net,
|
||||
chronicles,
|
||||
chronos,
|
||||
confutils,
|
||||
libp2p/crypto/crypto,
|
||||
eth/keys,
|
||||
eth/p2p/discoveryv5/enr,
|
||||
testutils/unittests
|
||||
|
||||
|
||||
import
|
||||
waku/common/logging,
|
||||
waku/node/peer_manager,
|
||||
waku/waku_core,
|
||||
waku/waku_node,
|
||||
waku/waku_enr,
|
||||
waku/waku_discv5,
|
||||
waku/common/protobuf,
|
||||
waku/utils/noise as waku_message_utils,
|
||||
waku/waku_noise/noise_types,
|
||||
waku/waku_noise/noise_utils,
|
||||
waku/waku_noise/noise_handshake_processing,
|
||||
waku/waku_core
|
||||
|
||||
import ../waku_handshake_utils
|
||||
|
||||
proc now*(): Timestamp =
|
||||
getNanosecondTime(getTime().toUnixFloat())
|
||||
|
||||
# An accesible bootstrap node. See wakuv2.prod fleets.status.im
|
||||
|
||||
|
||||
const bootstrapNode = "enr:-Nm4QOdTOKZJKTUUZ4O_W932CXIET-M9NamewDnL78P5u9D" &
|
||||
"OGnZlK0JFZ4k0inkfe6iY-0JAaJVovZXc575VV3njeiABgmlkgn" &
|
||||
"Y0gmlwhAjS3ueKbXVsdGlhZGRyc7g6ADg2MW5vZGUtMDEuYWMtY" &
|
||||
"24taG9uZ2tvbmctYy53YWt1djIucHJvZC5zdGF0dXNpbS5uZXQG" &
|
||||
"H0DeA4lzZWNwMjU2azGhAo0C-VvfgHiXrxZi3umDiooXMGY9FvY" &
|
||||
"j5_d1Q4EeS7eyg3RjcIJ2X4N1ZHCCIyiFd2FrdTIP"
|
||||
|
||||
# careful if running pub and sub in the same machine
|
||||
const wakuPort = 60000
|
||||
const discv5Port = 9000
|
||||
|
||||
|
||||
|
||||
proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} =
|
||||
var readyForFinalization = false
|
||||
|
||||
#########################
|
||||
# Content Topic information
|
||||
let contentTopicInfo = ContentTopicInfo(
|
||||
applicationName: "waku-noise-sessions",
|
||||
applicationVersion: "0.1",
|
||||
shardId: "10",)
|
||||
|
||||
################################
|
||||
# Alice static/ephemeral key initialization and commitment
|
||||
let aliceInfo = initAgentKeysAndCommitment(rng)
|
||||
let s = aliceInfo.commitment
|
||||
|
||||
let qr = readFile("qr.txt")
|
||||
let qrMessageNameTag = cast[seq[byte]](readFile("qrMessageNametag.txt"))
|
||||
echo qrMessageNameTag
|
||||
|
||||
# We set the contentTopic from the content topic parameters exchanged in the QR
|
||||
let contentTopic = initContentTopicFromQr(qr)
|
||||
|
||||
var aliceHS = initHS(aliceInfo, qr, true)
|
||||
|
||||
var
|
||||
sentTransportMessage: seq[byte]
|
||||
aliceStep: HandshakeStepResult
|
||||
wakuMsg: Result[WakuMessage, cstring]
|
||||
readPayloadV2: PayloadV2
|
||||
aliceMessageNametag: MessageNametag
|
||||
aliceHSResult: HandshakeResult
|
||||
|
||||
|
||||
# use notice to filter all waku messaging
|
||||
setupLogLevel(logging.LogLevel.NOTICE)
|
||||
notice "starting publisher", wakuPort=wakuPort, discv5Port=discv5Port
|
||||
let
|
||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get()
|
||||
ip = parseIpAddress("0.0.0.0")
|
||||
flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true)
|
||||
|
||||
var enrBuilder = EnrBuilder.init(nodeKey)
|
||||
|
||||
let recordRes = enrBuilder.build()
|
||||
let record =
|
||||
if recordRes.isErr():
|
||||
error "failed to create enr record", error=recordRes.error
|
||||
quit(QuitFailure)
|
||||
else: recordRes.get()
|
||||
|
||||
var builder = WakuNodeBuilder.init()
|
||||
builder.withNodeKey(nodeKey)
|
||||
builder.withRecord(record)
|
||||
builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
|
||||
let node = builder.build().tryGet()
|
||||
|
||||
var bootstrapNodeEnr: enr.Record
|
||||
discard bootstrapNodeEnr.fromURI(bootstrapNode)
|
||||
|
||||
let discv5Conf = WakuDiscoveryV5Config(
|
||||
discv5Config: none(DiscoveryConfig),
|
||||
address: ip,
|
||||
port: Port(discv5Port),
|
||||
privateKey: keys.PrivateKey(nodeKey.skkey),
|
||||
bootstrapRecords: @[bootstrapNodeEnr],
|
||||
autoupdateRecord: true,
|
||||
)
|
||||
|
||||
# assumes behind a firewall, so not care about being discoverable
|
||||
let wakuDiscv5 = WakuDiscoveryV5.new(
|
||||
node.rng,
|
||||
discv5Conf,
|
||||
some(node.enr),
|
||||
some(node.peerManager),
|
||||
node.topicSubscriptionQueue,
|
||||
)
|
||||
|
||||
await node.start()
|
||||
await node.mountRelay()
|
||||
node.peerManager.start()
|
||||
|
||||
(await wakuDiscv5.start()).isOkOr:
|
||||
error "failed to start discv5", error = error
|
||||
quit(1)
|
||||
|
||||
# wait for a minimum of peers to be connected, otherwise messages wont be gossiped
|
||||
while true:
|
||||
let numConnectedPeers = node.peerManager.peerStore[ConnectionBook].book.values().countIt(it == Connected)
|
||||
if numConnectedPeers >= 6:
|
||||
notice "publisher is ready", connectedPeers=numConnectedPeers, required=6
|
||||
break
|
||||
notice "waiting to be ready", connectedPeers=numConnectedPeers, required=6
|
||||
await sleepAsync(5000)
|
||||
|
||||
# Make sure it matches the publisher. Use default value
|
||||
# see spec: https://rfc.vac.dev/spec/23/
|
||||
let pubSubTopic = PubsubTopic("/waku/2/default-waku/proto")
|
||||
|
||||
###############################################
|
||||
# We prepare a Waku message from Alice's payload2
|
||||
echo "qrMessageNametag ", qrMessageNametag
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
wakuMsg = prepareHandShakeInitiatorMsg(rng, contentTopic, aliceInfo,
|
||||
qrMessageNameTag, aliceMessageNametag,
|
||||
aliceHS, aliceStep)
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
await publishHandShakeInitiatorMsg(node, pubSubTopic, contentTopic, wakuMsg.get())
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
# aliceMessageNametag = toMessageNametag(aliceHS)
|
||||
let step2Nametag = aliceMessageNametag
|
||||
echo "step2Nametag ", step2Nametag
|
||||
proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} =
|
||||
# let payloadStr = string.fromBytes(msg.payload)
|
||||
if msg.contentTopic == contentTopic:
|
||||
readPayloadV2 = decodePayloadV2(msg).get()
|
||||
if readPayloadV2.messageNametag == step2Nametag:
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
handleHandShakeMsg(rng, pubSubTopic, contentTopic,step = 2, readPayloadV2,
|
||||
aliceStep, aliceHS, aliceMessageNametag)
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
# await sleepAsync(5000)
|
||||
let handShakeMsgStep3 = prepareHandShakeMsg(rng, contentTopic, aliceInfo,
|
||||
aliceMessageNametag, aliceHS,
|
||||
aliceStep, step = 3)
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
await publishHandShakeMsg( node, pubSubTopic, contentTopic, handShakeMsgStep3.get(), 3)
|
||||
readyForFinalization = true
|
||||
echo "aliceMessageNametag ", aliceMessageNametag
|
||||
|
||||
node.subscribe((kind: PubsubSub, topic: pubsubTopic), some(handler))
|
||||
|
||||
while true:
|
||||
if readyForFinalization:
|
||||
notice "Finalizing handshake"
|
||||
aliceHSResult = finalizeHandshake(aliceHS)
|
||||
await sleepAsync(5000)
|
||||
break
|
||||
await sleepAsync(5000)
|
||||
|
||||
var
|
||||
payload2: PayloadV2
|
||||
realMessage: seq[byte]
|
||||
readMessage: seq[byte]
|
||||
|
||||
# Bob writes to Alice
|
||||
realMessage = @[(byte)42,42,42,42]
|
||||
let realMessageContentTopic = "/" & contentTopicInfo.applicationName & "/" & contentTopicInfo.applicationVersion & "/wakunoise/1/sessions_shard-" & contentTopicInfo.shardId & "/real" & "/proto"
|
||||
payload2 = writeMessage(aliceHSResult, realMessage, outboundMessageNametagBuffer = aliceHSResult.nametagsOutbound)
|
||||
echo aliceHSResult.h
|
||||
wakuMsg = encodePayloadV2( payload2, realMessageContentTopic)
|
||||
await node.publish(some(pubSubTopic), wakuMsg.get)
|
||||
notice "Sending real message", payload=payload2,
|
||||
pubsubTopic=pubsubTopic,
|
||||
contentTopic=realMessageContentTopic
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let rng = crypto.newRng()
|
||||
asyncSpawn setupAndPublish(rng)
|
||||
runForever()
|
203
libs/waku-utils/example/agentB.nim
Normal file
203
libs/waku-utils/example/agentB.nim
Normal file
@ -0,0 +1,203 @@
|
||||
import
|
||||
std/[tables, sequtils],
|
||||
stew/byteutils,
|
||||
stew/shims/net,
|
||||
chronicles,
|
||||
chronos,
|
||||
confutils,
|
||||
libp2p/crypto/crypto,
|
||||
eth/keys,
|
||||
eth/p2p/discoveryv5/enr
|
||||
|
||||
import
|
||||
waku/common/logging,
|
||||
waku/node/peer_manager,
|
||||
waku/waku_core,
|
||||
waku/waku_node,
|
||||
waku/waku_enr,
|
||||
waku/waku_discv5,
|
||||
waku/common/protobuf,
|
||||
waku/utils/noise as waku_message_utils,
|
||||
waku/waku_noise/noise_types,
|
||||
waku/waku_noise/noise_utils,
|
||||
waku/waku_noise/noise_handshake_processing,
|
||||
waku/waku_core
|
||||
|
||||
import ../waku_handshake_utils
|
||||
|
||||
# An accesible bootstrap node. See wakuv2.prod fleets.status.im
|
||||
const bootstrapNode = "enr:-Nm4QOdTOKZJKTUUZ4O_W932CXIET-M9NamewDnL78P5u9DOGnZl" &
|
||||
"K0JFZ4k0inkfe6iY-0JAaJVovZXc575VV3njeiABgmlkgnY0gmlwhAjS" &
|
||||
"3ueKbXVsdGlhZGRyc7g6ADg2MW5vZGUtMDEuYWMtY24taG9uZ2tvbmct" &
|
||||
"Yy53YWt1djIucHJvZC5zdGF0dXNpbS5uZXQGH0DeA4lzZWNwMjU2azGh" &
|
||||
"Ao0C-VvfgHiXrxZi3umDiooXMGY9FvYj5_d1Q4EeS7eyg3RjcIJ2X4N1" &
|
||||
"ZHCCIyiFd2FrdTIP"
|
||||
|
||||
# careful if running pub and sub in the same machine
|
||||
const wakuPort = 50000
|
||||
const discv5Port = 8000
|
||||
|
||||
|
||||
proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} =
|
||||
var readyForFinalization = false
|
||||
|
||||
################################
|
||||
# Bob static/ephemeral key initialization and commitment
|
||||
let bobInfo = initAgentKeysAndCommitment(rng)
|
||||
let r = bobInfo.commitment
|
||||
|
||||
#########################
|
||||
# Content Topic information
|
||||
let contentTopicInfo = ContentTopicInfo(
|
||||
applicationName: "waku-noise-sessions",
|
||||
applicationVersion: "0.1",
|
||||
shardId: "10",)
|
||||
|
||||
let (qr, qrMessageNametag) = initQr(rng, contentTopicInfo, bobInfo)
|
||||
writeFile("qr.txt", qr)
|
||||
writeFile("qrMessageNametag.txt", qrMessageNametag)
|
||||
echo qrMessageNametag
|
||||
|
||||
# We set the contentTopic from the content topic parameters exchanged in the QR
|
||||
let contentTopic = initContentTopicFromQr(qr)
|
||||
echo "contentTopic: ", contentTopic
|
||||
var bobHS = initHS(bobInfo, qr)
|
||||
|
||||
|
||||
var
|
||||
bobStep: HandshakeStepResult
|
||||
wakuMsg: Result[WakuMessage, cstring]
|
||||
readPayloadV2: PayloadV2
|
||||
bobMessageNametag: MessageNametag
|
||||
bobHSResult: HandshakeResult
|
||||
|
||||
|
||||
# use notice to filter all waku messaging
|
||||
setupLogLevel(logging.LogLevel.NOTICE)
|
||||
notice "starting subscriber", wakuPort=wakuPort, discv5Port=discv5Port
|
||||
let
|
||||
nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[]
|
||||
ip = parseIpAddress("0.0.0.0")
|
||||
flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true)
|
||||
|
||||
var enrBuilder = EnrBuilder.init(nodeKey)
|
||||
|
||||
let recordRes = enrBuilder.build()
|
||||
let record =
|
||||
if recordRes.isErr():
|
||||
error "failed to create enr record", error=recordRes.error
|
||||
quit(QuitFailure)
|
||||
else: recordRes.get()
|
||||
|
||||
var builder = WakuNodeBuilder.init()
|
||||
builder.withNodeKey(nodeKey)
|
||||
builder.withRecord(record)
|
||||
builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet()
|
||||
let node = builder.build().tryGet()
|
||||
|
||||
var bootstrapNodeEnr: enr.Record
|
||||
discard bootstrapNodeEnr.fromURI(bootstrapNode)
|
||||
|
||||
let discv5Conf = WakuDiscoveryV5Config(
|
||||
discv5Config: none(DiscoveryConfig),
|
||||
address: ip,
|
||||
port: Port(discv5Port),
|
||||
privateKey: keys.PrivateKey(nodeKey.skkey),
|
||||
bootstrapRecords: @[bootstrapNodeEnr],
|
||||
autoupdateRecord: true,
|
||||
)
|
||||
|
||||
# assumes behind a firewall, so not care about being discoverable
|
||||
let wakuDiscv5 = WakuDiscoveryV5.new(
|
||||
node.rng,
|
||||
discv5Conf,
|
||||
some(node.enr),
|
||||
some(node.peerManager),
|
||||
node.topicSubscriptionQueue,
|
||||
)
|
||||
|
||||
await node.start()
|
||||
await node.mountRelay()
|
||||
node.peerManager.start()
|
||||
|
||||
(await wakuDiscv5.start()).isOkOr:
|
||||
error "failed to start discv5", error = error
|
||||
quit(1)
|
||||
|
||||
# wait for a minimum of peers to be connected, otherwise messages wont be gossiped
|
||||
while true:
|
||||
let numConnectedPeers = node.peerManager.peerStore[ConnectionBook].book.values().countIt(it == Connected)
|
||||
if numConnectedPeers >= 6:
|
||||
notice "subscriber is ready", connectedPeers=numConnectedPeers, required=6
|
||||
break
|
||||
notice "waiting to be ready", connectedPeers=numConnectedPeers, required=6
|
||||
await sleepAsync(5000)
|
||||
|
||||
# Make sure it matches the publisher. Use default value
|
||||
# see spec: https://rfc.vac.dev/spec/23/
|
||||
let pubSubTopic = PubsubTopic("/waku/2/default-waku/proto")
|
||||
var step2Nameteag: MessageNametag
|
||||
echo "qrMessageNametag ", qrMessageNametag
|
||||
proc handler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} =
|
||||
# let payloadStr = string.fromBytes(msg.payload)
|
||||
if msg.contentTopic == contentTopic:
|
||||
readPayloadV2 = decodePayloadV2(msg).get()
|
||||
if readPayloadV2.messageNametag == qrMessageNametag:
|
||||
echo "bobMessageNametag ", bobMessageNametag
|
||||
handleHandShakeInitiatorMsg(rng, pubSubTopic, contentTopic, readPayloadV2,
|
||||
bobStep, bobHS, bobMessageNametag,
|
||||
qrMessageNametag)
|
||||
echo "bobMessageNametag ", bobMessageNametag
|
||||
step2Nameteag = bobMessageNametag
|
||||
wakuMsg = prepareHandShakeMsg(rng, contentTopic, bobInfo,
|
||||
bobMessageNametag, bobHS, bobStep,
|
||||
step = 2)
|
||||
echo "bobMessageNametag ", bobMessageNametag
|
||||
await publishHandShakeMsg(node, pubSubTopic, contentTopic,
|
||||
wakuMsg.get(), step = 2)
|
||||
|
||||
elif readPayloadV2.messageNametag != step2Nameteag:
|
||||
# bobMessageNametag = toMessageNametag(bobHS)
|
||||
handleHandShakeMsg(rng, pubSubTopic, contentTopic, step = 3, readPayloadV2,
|
||||
bobStep, bobHS, bobMessageNametag)
|
||||
# notice "step 3 message received", payload=readPayloadV2,
|
||||
# pubsubTopic=pubsubTopic,
|
||||
# contentTopic=msg.contentTopic,
|
||||
# timestamp=msg.timestamp
|
||||
# # Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
|
||||
# bobStep = stepHandshake(rng[], bobHS, readPayloadV2 = readPayloadV2, messageNametag = bobMessageNametag).get()
|
||||
readyForFinalization = true
|
||||
|
||||
node.subscribe((kind: PubsubSub, topic: pubsubTopic), some(handler))
|
||||
while true:
|
||||
if readyForFinalization:
|
||||
notice "Finalizing handshake"
|
||||
bobHSResult = finalizeHandshake(bobHS)
|
||||
|
||||
proc realMessageHandler(topic: PubsubTopic, msg: WakuMessage): Future[void] {.async, gcsafe.} =
|
||||
let realMessageContentTopic = "/" & contentTopicInfo.applicationName & "/" & contentTopicInfo.applicationVersion & "/wakunoise/1/sessions_shard-" & contentTopicInfo.shardId & "/real" & "/proto"
|
||||
|
||||
if msg.contentTopic == realMessageContentTopic:
|
||||
readPayloadV2 = decodePayloadV2(msg).get()
|
||||
notice "Received real message", payload=readPayloadV2,
|
||||
pubsubTopic=pubsubTopic,
|
||||
contentTopic=msg.contentTopic,
|
||||
timestamp=msg.timestamp
|
||||
let readMessage = readMessage(bobHSResult, readPayloadV2, inboundMessageNametagBuffer = bobHSResult.nametagsInbound).get()
|
||||
echo readMessage
|
||||
echo bobHSResult.h
|
||||
|
||||
|
||||
node.subscribe((kind: PubsubSub, topic: pubsubTopic), some(realMessageHandler))
|
||||
break
|
||||
await sleepAsync(5000)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let rng = crypto.newRng()
|
||||
asyncSpawn setupAndSubscribe(rng)
|
||||
runForever()
|
Loading…
x
Reference in New Issue
Block a user