From 50562bfe14c37d38f3da286ba8fbb558d8c56411 Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 31 Oct 2024 12:20:07 +0100 Subject: [PATCH 1/7] cherry-pick eligibility PoC from an experimental branch --- tests/incentivization/test_all.nim | 4 +- tests/incentivization/test_poc.nim | 99 ++++++++++++++++++++++++ tests/incentivization/test_rpc_codec.nim | 60 +++++++++++--- waku/incentivization/client.nim | 55 +++++++++++++ waku/incentivization/common.nim | 53 +++++++++++++ waku/incentivization/eligibility.nim | 26 +++++++ waku/incentivization/protocol.nim | 60 ++++++++++++++ waku/incentivization/rpc.nim | 10 +++ waku/incentivization/rpc_codec.nim | 46 +++++++++++ waku/incentivization/txid_proof.nim | 28 +++++++ 10 files changed, 430 insertions(+), 11 deletions(-) create mode 100644 tests/incentivization/test_poc.nim create mode 100644 waku/incentivization/client.nim create mode 100644 waku/incentivization/common.nim create mode 100644 waku/incentivization/eligibility.nim create mode 100644 waku/incentivization/protocol.nim create mode 100644 waku/incentivization/txid_proof.nim diff --git a/tests/incentivization/test_all.nim b/tests/incentivization/test_all.nim index 3efdc7d6e7..d63dcf502a 100644 --- a/tests/incentivization/test_all.nim +++ b/tests/incentivization/test_all.nim @@ -1 +1,3 @@ -import ./test_rpc_codec +import + ./test_rpc_codec, + ./test_poc diff --git a/tests/incentivization/test_poc.nim b/tests/incentivization/test_poc.nim new file mode 100644 index 0000000000..0b9b3f6f77 --- /dev/null +++ b/tests/incentivization/test_poc.nim @@ -0,0 +1,99 @@ +{.used.} + +import + std/[options, strscans], + testutils/unittests, + chronicles, + chronos, + libp2p/crypto/crypto, + web3 + +import + ../../../waku/[ + node/peer_manager, + waku_core, + ], + ../testlib/[assertions, wakucore, testasync, futures, testutils], + ../../../waku/incentivization/[ + rpc, + rpc_codec, + common, + client, + protocol, + txid_proof + ] + + +# a random confirmed txis (Sepolia) +const TxHashExisting* = TxHash.fromHex( + "0xc1be5f442d3688a8d3e4b5980a73f15e4351358e0f16e2fdd99c2517c9cf6270" + ) +const TxHashNonExisting* = TxHash.fromHex( + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) + +const EthClient = "https://sepolia.infura.io/v3/470c2e9a16f24057aee6660081729fb9" + +proc newTestDummyProtocolNode*( + switch: Switch, + handler: DummyHandler, + ethClient: string + ): Future[DummyProtocol] {.async.} = + let + peerManager = PeerManager.new(switch) + dummyProtocol = DummyProtocol.new(peerManager, handler, ethClient) + + await dummyProtocol.start() + switch.mount(dummyProtocol) + + return dummyProtocol + + +suite "Waku Incentivization PoC Dummy Protocol": + + var + serverSwitch {.threadvar.}: Switch + serverRemotePeerInfo {.threadvar.}: RemotePeerInfo + handlerFuture {.threadvar.}: Future[DummyRequest] + handler {.threadvar.}: DummyHandler + server {.threadvar.}: DummyProtocol + clientSwitch {.threadvar.}: Switch + client {.threadvar.}: WakuDummyClient + clientPeerId {.threadvar.}: PeerId + + asyncSetup: + + # setting up a server + serverSwitch = newTestSwitch() + handlerFuture = newFuture[DummyRequest]() + handler = proc( + peer: PeerId, dummyRequest: DummyRequest + ): Future[DummyResult[void]] {.async.} = + handlerFuture.complete(dummyRequest) + return ok() + server = await newTestDummyProtocolNode(serverSwitch, handler, EthClient) + + # setting up a client + clientSwitch = newTestSwitch() + let peerManager = PeerManager.new(clientSwitch) + client = WakuDummyClient.new(peerManager) + + await allFutures(serverSwitch.start(), clientSwitch.start()) + + clientPeerId = clientSwitch.peerInfo.peerId + serverRemotePeerInfo = serverSwitch.peerInfo.toRemotePeerInfo() + + asyncTeardown: + await allFutures(clientSwitch.stop(), serverSwitch.stop()) + + asyncTest "incentivization PoC: dummy protocol with a valid txid eligibility proof": + let request = genDummyRequestWithTxIdEligibilityProof(@(TxHashExisting.bytes())) + let response = await client.sendRequest(request, serverRemotePeerInfo) + check: + response.isOk() + + asyncTest "incentivization PoC: dummy protocol client with an invalid txid eligibility proof": + let request = genDummyRequestWithTxIdEligibilityProof(@(TxHashNonExisting.bytes())) + let response = await client.sendRequest(request, serverRemotePeerInfo) + check: + response.isErr() diff --git a/tests/incentivization/test_rpc_codec.nim b/tests/incentivization/test_rpc_codec.nim index 803796dbd5..8210b0b90f 100644 --- a/tests/incentivization/test_rpc_codec.nim +++ b/tests/incentivization/test_rpc_codec.nim @@ -1,12 +1,23 @@ import std/options, - std/strscans, testutils/unittests, - chronicles, chronos, - libp2p/crypto/crypto + libp2p/crypto/crypto, + web3 -import waku/incentivization/rpc, waku/incentivization/rpc_codec +import + ../../../waku/incentivization/[ + rpc, + rpc_codec, + common, + txid_proof, + eligibility + ] + +let txHash = TxHash.fromHex( + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) +let txHashAsBytes = @(txHash.bytes()) suite "Waku Incentivization Eligibility Codec": asyncTest "encode eligibility proof": @@ -17,9 +28,38 @@ suite "Waku Incentivization Eligibility Codec": check: epRpc == decoded - asyncTest "encode eligibility status": - let esRpc = EligibilityStatus(statusCode: uint32(200), statusDesc: some("OK")) - let encoded = encode(esRpc) - let decoded = EligibilityStatus.decode(encoded.buffer).get() - check: - esRpc == decoded + asyncTest "encode eligibility proof": + let txHash = TxHash.fromHex( + "0x0000000000000000000000000000000000000000000000000000000000000000") + let txHashAsBytes = @(txHash.bytes()) + let eligibilityProof = EligibilityProof(proofOfPayment: some(txHashAsBytes)) + let encoded = encode(eligibilityProof) + let decoded = EligibilityProof.decode(encoded.buffer).get() + check: + eligibilityProof == decoded + + asyncTest "encode eligibility status": + let eligibilityStatus = genEligibilityStatus(true) + let encoded = encode(eligibilityStatus) + let decoded = EligibilityStatus.decode(encoded.buffer).get() + check: + eligibilityStatus == decoded + + asyncTest "encode dummy request": + let txHash = TxHash.fromHex( + "0x0000000000000000000000000000000000000000000000000000000000000000") + let txHashAsBytes = @(txHash.bytes()) + let dummyRequest = genDummyRequestWithTxIdEligibilityProof(txHashAsBytes) + let encoded = encode(dummyRequest) + let decoded = DummyRequest.decode(encoded.buffer).get() + check: + dummyRequest == decoded + + asyncTest "encode dummy response": + var dummyResponse = genDummyResponseWithEligibilityStatus(true) + let encoded = encode(dummyResponse) + let decoded = DummyResponse.decode(encoded.buffer).get() + check: + dummyResponse == decoded + + diff --git a/waku/incentivization/client.nim b/waku/incentivization/client.nim new file mode 100644 index 0000000000..4fe8170ff1 --- /dev/null +++ b/waku/incentivization/client.nim @@ -0,0 +1,55 @@ +import std/options, chronicles, chronos, libp2p/protocols/protocol +import + ../node/peer_manager, ../waku_core, ./common, ./rpc_codec, ./rpc + +logScope: + topics = "waku incentivization PoC client" + +type WakuDummyClient* = ref object + peerManager*: PeerManager + +proc new*( + T: type WakuDummyClient, peerManager: PeerManager): T = + WakuDummyClient(peerManager: peerManager) + +proc sendDummyRequest( + dummyClient: WakuDummyClient, dummyRequest: DummyRequest, peer: PeerId | RemotePeerInfo +): Future[DummyResult[void]] {.async, gcsafe.} = + let connOpt = await dummyClient.peerManager.dialPeer(peer, DummyCodec) + if connOpt.isNone(): + return err("dialFailure") + let connection = connOpt.get() + await connection.writeLP(dummyRequest.encode().buffer) + + var buffer: seq[byte] + try: + buffer = await connection.readLp(DefaultMaxRpcSize.int) + except LPStreamRemoteClosedError: + return err("Exception reading: " & getCurrentExceptionMsg()) + + let decodeRespRes = DummyResponse.decode(buffer) + if decodeRespRes.isErr(): + return err("decodeRpcFailure") + + let dummyResponse = decodeRespRes.get() + + let requestId = dummyResponse.requestId + let eligibilityStatus = dummyResponse.eligibilityStatus + let statusCode = eligibilityStatus.statusCode + # status description is optional + var statusDesc = "" + let statusDescRes = eligibilityStatus.statusDesc + if statusDescRes.isSome(): + statusDesc = statusDescRes.get() + + if statusCode == 200: + return ok() + else: + return err(statusDesc) + +proc sendRequest*( + dummyClient: WakuDummyClient, + dummyRequest: DummyRequest, + peer: PeerId | RemotePeerInfo, +): Future[DummyResult[void]] {.async, gcsafe.} = + return await dummyClient.sendDummyRequest(dummyRequest, peer) diff --git a/waku/incentivization/common.nim b/waku/incentivization/common.nim new file mode 100644 index 0000000000..2112cc4fbb --- /dev/null +++ b/waku/incentivization/common.nim @@ -0,0 +1,53 @@ +import + std/options, + std/strscans, + std/sequtils, + testutils/unittests, + chronicles, + chronos, + libp2p/crypto/crypto + +import stew/results, libp2p/peerid + +import + ../../../waku/incentivization/rpc + +const DummyCodec* = "/vac/waku/dummy/0.0.1" + +type DummyResult*[T] = Result[T, string] + +type DummyHandler* = proc( + peer: PeerId, + dummyRequest: DummyRequest +): Future[DummyResult[void]] {.async.} + +type + DummyProtocolErrorKind* {.pure.} = enum + UNKNOWN = uint32(000) + BAD_RESPONSE = uint32(300) + BAD_REQUEST = uint32(400) + PAYMENT_REQUIRED = uint(402) # error type specific for incentivization + NOT_FOUND = uint32(404) + SERVICE_UNAVAILABLE = uint32(503) + PEER_DIAL_FAILURE = uint32(504) + + DummyProtocolError* = object + case kind*: DummyProtocolErrorKind + of PEER_DIAL_FAILURE: + address*: string + of BAD_RESPONSE, BAD_REQUEST, NOT_FOUND, SERVICE_UNAVAILABLE, PAYMENT_REQUIRED: + cause*: string + else: + discard + + DummyProtocolResult* = Result[void, DummyProtocolError] + +proc genEligibilityStatus*(isEligible: bool): EligibilityStatus = + if isEligible: + EligibilityStatus( + statusCode: uint32(200), + statusDesc: some("OK")) + else: + EligibilityStatus( + statusCode: uint32(402), + statusDesc: some("Payment Required")) \ No newline at end of file diff --git a/waku/incentivization/eligibility.nim b/waku/incentivization/eligibility.nim new file mode 100644 index 0000000000..7ed6bd9081 --- /dev/null +++ b/waku/incentivization/eligibility.nim @@ -0,0 +1,26 @@ +import + std/options, + std/strscans, + std/sequtils, + testutils/unittests, + chronicles, + chronos, + libp2p/crypto/crypto + +import stew/results, libp2p/peerid + +import + ../../../waku/incentivization/rpc, + ../../../waku/incentivization/rpc_codec, + ../../../waku/incentivization/common, + ../../../waku/incentivization/txid_proof + + +proc isEligible*(eligibilityProof: EligibilityProof, ethClient: string): Future[bool] {.async.} = + result = await txidEligiblityCriteriaMet(eligibilityProof, ethClient) + +proc genDummyResponseWithEligibilityStatus*(proofValid: bool, requestId: string = ""): DummyResponse = + let eligibilityStatus = genEligibilityStatus(proofValid) + result.requestId = requestId + result.eligibilityStatus = eligibilityStatus + diff --git a/waku/incentivization/protocol.nim b/waku/incentivization/protocol.nim new file mode 100644 index 0000000000..d49caaf9b2 --- /dev/null +++ b/waku/incentivization/protocol.nim @@ -0,0 +1,60 @@ +import + std/[options, sequtils, sets, strutils, tables], + stew/byteutils, + chronicles, + chronos, + libp2p/peerid, + libp2p/protocols/protocol +import + ../node/peer_manager, + ../waku_core, + ./common, + ./rpc_codec, + ./rpc, + ./eligibility + +logScope: + topics = "waku incentivization PoC" + +type DummyProtocol* = ref object of LPProtocol + peerManager*: PeerManager + dummyHandler*: DummyHandler + ethClient*: string + +proc handleRequest*( + dummyProtocol: DummyProtocol, peerId: PeerId, buffer: seq[byte] + ): Future[DummyResponse] {.async.} = + let reqDecodeRes = DummyRequest.decode(buffer) + var isProofValid = false + var requestId = "" + if reqDecodeRes.isOk(): + let dummyRequest = reqDecodeRes.get() + let eligibilityProof = dummyRequest.eligibilityProof + requestId = dummyRequest.requestId + isProofValid = await isEligible(eligibilityProof, dummyProtocol.ethClient) + let response = genDummyResponseWithEligibilityStatus(isProofValid, requestId) + return response + +proc initProtocolHandler(dummyProtocol: DummyProtocol) = + proc handle(conn: Connection, proto: string) {.async.} = + let buffer = await conn.readLp(DefaultMaxRpcSize) + var dummyResponse = await handleRequest(dummyProtocol, conn.peerId, buffer) + await conn.writeLp(dummyResponse.encode().buffer) + + dummyProtocol.handler = handle + dummyProtocol.codec = DummyCodec + +proc new*( + T: type DummyProtocol, + peerManager: PeerManager, + dummyHandler: DummyHandler, + ethClient: string, + ): T = + let dummyProtocol = DummyProtocol( + peerManager: peerManager, + dummyHandler: dummyHandler, + ethClient: ethClient + ) + dummyProtocol.initProtocolHandler() + return dummyProtocol + diff --git a/waku/incentivization/rpc.nim b/waku/incentivization/rpc.nim index d16ca75a8d..35e8ee59be 100644 --- a/waku/incentivization/rpc.nim +++ b/waku/incentivization/rpc.nim @@ -11,3 +11,13 @@ type EligibilityStatus* = object statusCode*: uint32 statusDesc*: Option[string] + + DummyRequest* = object + requestId*: string + # request content goes here + eligibilityProof*: EligibilityProof + + DummyResponse* = object + requestId*: string + # response content goes here + eligibilityStatus*: EligibilityStatus diff --git a/waku/incentivization/rpc_codec.nim b/waku/incentivization/rpc_codec.nim index 5d3ce48d53..e8888c1494 100644 --- a/waku/incentivization/rpc_codec.nim +++ b/waku/incentivization/rpc_codec.nim @@ -1,6 +1,8 @@ import std/options import ../common/protobuf, ../waku_core, ./rpc +const DefaultMaxRpcSize* = -1 + # Codec for EligibilityProof proc encode*(epRpc: EligibilityProof): ProtoBuffer = @@ -49,3 +51,47 @@ proc decode*(T: type EligibilityStatus, buffer: seq[byte]): ProtobufResult[T] = else: esRpc.statusDesc = some(description) ok(esRpc) + +# Codec for DummyRequest + +proc encode*(request: DummyRequest): ProtoBuffer = + var pb = initProtoBuffer() + pb.write3(1, request.requestId) + pb.write3(10, request.eligibilityProof.encode()) + pb + +proc decode*(T: type DummyRequest, buffer: seq[byte]): ProtobufResult[T] = + let pb = initProtoBuffer(buffer) + var request = DummyRequest() + + if not ?pb.getField(1, request.requestId): + return err(ProtobufError.missingRequiredField("requestId")) + + var eligibilityProofBytes: seq[byte] + if not ?pb.getField(10, eligibilityProofBytes): + return err(ProtobufError.missingRequiredField("eligibilityProof")) + else: + request.eligibilityProof = ?EligibilityProof.decode(eligibilityProofBytes) + ok(request) + +# Codec for DummyResponse + +proc encode*(response: DummyResponse): ProtoBuffer = + var pb = initProtoBuffer() + pb.write3(1, response.requestId) + pb.write3(5, response.eligibilityStatus.encode()) + pb + +proc decode*(T: type DummyResponse, buffer: seq[byte]): ProtobufResult[T] = + let pb = initProtoBuffer(buffer) + var response = DummyResponse() + + if not ?pb.getField(1, response.requestId): + return err(ProtobufError.missingRequiredField("requestId")) + + var eligibilityStatusBytes: seq[byte] + if not ?pb.getField(5, eligibilityStatusBytes): + return err(ProtobufError.missingRequiredField("eligibilityStatus")) + else: + response.eligibilityStatus = ?EligibilityStatus.decode(eligibilityStatusBytes) + ok(response) diff --git a/waku/incentivization/txid_proof.nim b/waku/incentivization/txid_proof.nim new file mode 100644 index 0000000000..0c8708e86f --- /dev/null +++ b/waku/incentivization/txid_proof.nim @@ -0,0 +1,28 @@ +import + std/options, + chronos, + web3, + stew/byteutils + +import ../../../waku/incentivization/rpc + +proc genDummyRequestWithTxIdEligibilityProof*(txHashAsBytes: seq[byte], requestId: string = ""): DummyRequest = + result.requestId = requestId + result.eligibilityProof = EligibilityProof(proofOfPayment: some(txHashAsBytes)) + +proc checkTxIdExists(txHash: TxHash, ethClient: string): Future[bool] {.async.} = + let web3 = await newWeb3(ethClient) + try: + discard await web3.provider.eth_getTransactionByHash(txHash) + result = true + except ValueError as e: + result = false + await web3.close() + result + +proc txidEligiblityCriteriaMet*(eligibilityProof: EligibilityProof, ethClient: string): Future[bool] {.async.} = + if eligibilityProof.proofOfPayment.isNone(): + return false + let txHash = TxHash.fromHex(byteutils.toHex(eligibilityProof.proofOfPayment.get())) + let txExists = await checkTxIdExists(txHash, ethClient) + return txExists \ No newline at end of file From 13d3a52c9f2acd1491f3d1d17057c8c7d678e74c Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 31 Oct 2024 12:42:20 +0100 Subject: [PATCH 2/7] fix import paths --- tests/incentivization/test_poc.nim | 4 ++-- tests/incentivization/test_rpc_codec.nim | 2 +- waku/incentivization/common.nim | 2 +- waku/incentivization/eligibility.nim | 8 ++++---- waku/incentivization/rpc.nim | 2 +- waku/incentivization/txid_proof.nim | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/incentivization/test_poc.nim b/tests/incentivization/test_poc.nim index 0b9b3f6f77..adc9f71db7 100644 --- a/tests/incentivization/test_poc.nim +++ b/tests/incentivization/test_poc.nim @@ -9,12 +9,12 @@ import web3 import - ../../../waku/[ + waku/[ node/peer_manager, waku_core, ], ../testlib/[assertions, wakucore, testasync, futures, testutils], - ../../../waku/incentivization/[ + waku/incentivization/[ rpc, rpc_codec, common, diff --git a/tests/incentivization/test_rpc_codec.nim b/tests/incentivization/test_rpc_codec.nim index 8210b0b90f..0e05aca1af 100644 --- a/tests/incentivization/test_rpc_codec.nim +++ b/tests/incentivization/test_rpc_codec.nim @@ -6,7 +6,7 @@ import web3 import - ../../../waku/incentivization/[ + waku/incentivization/[ rpc, rpc_codec, common, diff --git a/waku/incentivization/common.nim b/waku/incentivization/common.nim index 2112cc4fbb..851a1af859 100644 --- a/waku/incentivization/common.nim +++ b/waku/incentivization/common.nim @@ -10,7 +10,7 @@ import import stew/results, libp2p/peerid import - ../../../waku/incentivization/rpc + waku/incentivization/rpc const DummyCodec* = "/vac/waku/dummy/0.0.1" diff --git a/waku/incentivization/eligibility.nim b/waku/incentivization/eligibility.nim index 7ed6bd9081..9654a946fc 100644 --- a/waku/incentivization/eligibility.nim +++ b/waku/incentivization/eligibility.nim @@ -10,10 +10,10 @@ import import stew/results, libp2p/peerid import - ../../../waku/incentivization/rpc, - ../../../waku/incentivization/rpc_codec, - ../../../waku/incentivization/common, - ../../../waku/incentivization/txid_proof + waku/incentivization/rpc, + waku/incentivization/rpc_codec, + waku/incentivization/common, + waku/incentivization/txid_proof proc isEligible*(eligibilityProof: EligibilityProof, ethClient: string): Future[bool] {.async.} = diff --git a/waku/incentivization/rpc.nim b/waku/incentivization/rpc.nim index 35e8ee59be..1477d4f5fd 100644 --- a/waku/incentivization/rpc.nim +++ b/waku/incentivization/rpc.nim @@ -1,5 +1,5 @@ import json_serialization, std/options -import ../waku_core +import waku/waku_core # Implementing the RFC: # https://github.com/vacp2p/rfc/tree/master/content/docs/rfcs/73 diff --git a/waku/incentivization/txid_proof.nim b/waku/incentivization/txid_proof.nim index 0c8708e86f..ddaaac6c0f 100644 --- a/waku/incentivization/txid_proof.nim +++ b/waku/incentivization/txid_proof.nim @@ -4,7 +4,7 @@ import web3, stew/byteutils -import ../../../waku/incentivization/rpc +import waku/incentivization/rpc proc genDummyRequestWithTxIdEligibilityProof*(txHashAsBytes: seq[byte], requestId: string = ""): DummyRequest = result.requestId = requestId From 5fbfa8f69243329ccfc58d80c303a939223db6bc Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 31 Oct 2024 13:04:29 +0100 Subject: [PATCH 3/7] remove DummyProtocol-related code (originally from experimental branch) --- tests/incentivization/test_poc.nim | 57 ++-------------------- tests/incentivization/test_rpc_codec.nim | 17 ------- waku/incentivization/client.nim | 55 ---------------------- waku/incentivization/common.nim | 30 ------------ waku/incentivization/eligibility.nim | 6 --- waku/incentivization/protocol.nim | 60 ------------------------ waku/incentivization/rpc.nim | 10 ---- waku/incentivization/rpc_codec.nim | 44 ----------------- waku/incentivization/txid_proof.nim | 4 -- 9 files changed, 5 insertions(+), 278 deletions(-) delete mode 100644 waku/incentivization/client.nim delete mode 100644 waku/incentivization/protocol.nim diff --git a/tests/incentivization/test_poc.nim b/tests/incentivization/test_poc.nim index adc9f71db7..280a8d17c5 100644 --- a/tests/incentivization/test_poc.nim +++ b/tests/incentivization/test_poc.nim @@ -18,13 +18,11 @@ import rpc, rpc_codec, common, - client, - protocol, txid_proof ] -# a random confirmed txis (Sepolia) +# a random confirmed txid (Sepolia) const TxHashExisting* = TxHash.fromHex( "0xc1be5f442d3688a8d3e4b5980a73f15e4351358e0f16e2fdd99c2517c9cf6270" ) @@ -34,58 +32,12 @@ const TxHashNonExisting* = TxHash.fromHex( const EthClient = "https://sepolia.infura.io/v3/470c2e9a16f24057aee6660081729fb9" -proc newTestDummyProtocolNode*( - switch: Switch, - handler: DummyHandler, - ethClient: string - ): Future[DummyProtocol] {.async.} = - let - peerManager = PeerManager.new(switch) - dummyProtocol = DummyProtocol.new(peerManager, handler, ethClient) - await dummyProtocol.start() - switch.mount(dummyProtocol) +#suite "Waku Incentivization PoC Eligibility Proofs": - return dummyProtocol - - -suite "Waku Incentivization PoC Dummy Protocol": - - var - serverSwitch {.threadvar.}: Switch - serverRemotePeerInfo {.threadvar.}: RemotePeerInfo - handlerFuture {.threadvar.}: Future[DummyRequest] - handler {.threadvar.}: DummyHandler - server {.threadvar.}: DummyProtocol - clientSwitch {.threadvar.}: Switch - client {.threadvar.}: WakuDummyClient - clientPeerId {.threadvar.}: PeerId - - asyncSetup: - - # setting up a server - serverSwitch = newTestSwitch() - handlerFuture = newFuture[DummyRequest]() - handler = proc( - peer: PeerId, dummyRequest: DummyRequest - ): Future[DummyResult[void]] {.async.} = - handlerFuture.complete(dummyRequest) - return ok() - server = await newTestDummyProtocolNode(serverSwitch, handler, EthClient) - - # setting up a client - clientSwitch = newTestSwitch() - let peerManager = PeerManager.new(clientSwitch) - client = WakuDummyClient.new(peerManager) - - await allFutures(serverSwitch.start(), clientSwitch.start()) - - clientPeerId = clientSwitch.peerInfo.peerId - serverRemotePeerInfo = serverSwitch.peerInfo.toRemotePeerInfo() - - asyncTeardown: - await allFutures(clientSwitch.stop(), serverSwitch.stop()) + # TODO: test eligibility without dummy protocol + #[ asyncTest "incentivization PoC: dummy protocol with a valid txid eligibility proof": let request = genDummyRequestWithTxIdEligibilityProof(@(TxHashExisting.bytes())) let response = await client.sendRequest(request, serverRemotePeerInfo) @@ -97,3 +49,4 @@ suite "Waku Incentivization PoC Dummy Protocol": let response = await client.sendRequest(request, serverRemotePeerInfo) check: response.isErr() + ]# diff --git a/tests/incentivization/test_rpc_codec.nim b/tests/incentivization/test_rpc_codec.nim index 0e05aca1af..f2b2281f7b 100644 --- a/tests/incentivization/test_rpc_codec.nim +++ b/tests/incentivization/test_rpc_codec.nim @@ -45,21 +45,4 @@ suite "Waku Incentivization Eligibility Codec": check: eligibilityStatus == decoded - asyncTest "encode dummy request": - let txHash = TxHash.fromHex( - "0x0000000000000000000000000000000000000000000000000000000000000000") - let txHashAsBytes = @(txHash.bytes()) - let dummyRequest = genDummyRequestWithTxIdEligibilityProof(txHashAsBytes) - let encoded = encode(dummyRequest) - let decoded = DummyRequest.decode(encoded.buffer).get() - check: - dummyRequest == decoded - - asyncTest "encode dummy response": - var dummyResponse = genDummyResponseWithEligibilityStatus(true) - let encoded = encode(dummyResponse) - let decoded = DummyResponse.decode(encoded.buffer).get() - check: - dummyResponse == decoded - diff --git a/waku/incentivization/client.nim b/waku/incentivization/client.nim deleted file mode 100644 index 4fe8170ff1..0000000000 --- a/waku/incentivization/client.nim +++ /dev/null @@ -1,55 +0,0 @@ -import std/options, chronicles, chronos, libp2p/protocols/protocol -import - ../node/peer_manager, ../waku_core, ./common, ./rpc_codec, ./rpc - -logScope: - topics = "waku incentivization PoC client" - -type WakuDummyClient* = ref object - peerManager*: PeerManager - -proc new*( - T: type WakuDummyClient, peerManager: PeerManager): T = - WakuDummyClient(peerManager: peerManager) - -proc sendDummyRequest( - dummyClient: WakuDummyClient, dummyRequest: DummyRequest, peer: PeerId | RemotePeerInfo -): Future[DummyResult[void]] {.async, gcsafe.} = - let connOpt = await dummyClient.peerManager.dialPeer(peer, DummyCodec) - if connOpt.isNone(): - return err("dialFailure") - let connection = connOpt.get() - await connection.writeLP(dummyRequest.encode().buffer) - - var buffer: seq[byte] - try: - buffer = await connection.readLp(DefaultMaxRpcSize.int) - except LPStreamRemoteClosedError: - return err("Exception reading: " & getCurrentExceptionMsg()) - - let decodeRespRes = DummyResponse.decode(buffer) - if decodeRespRes.isErr(): - return err("decodeRpcFailure") - - let dummyResponse = decodeRespRes.get() - - let requestId = dummyResponse.requestId - let eligibilityStatus = dummyResponse.eligibilityStatus - let statusCode = eligibilityStatus.statusCode - # status description is optional - var statusDesc = "" - let statusDescRes = eligibilityStatus.statusDesc - if statusDescRes.isSome(): - statusDesc = statusDescRes.get() - - if statusCode == 200: - return ok() - else: - return err(statusDesc) - -proc sendRequest*( - dummyClient: WakuDummyClient, - dummyRequest: DummyRequest, - peer: PeerId | RemotePeerInfo, -): Future[DummyResult[void]] {.async, gcsafe.} = - return await dummyClient.sendDummyRequest(dummyRequest, peer) diff --git a/waku/incentivization/common.nim b/waku/incentivization/common.nim index 851a1af859..d856fff54b 100644 --- a/waku/incentivization/common.nim +++ b/waku/incentivization/common.nim @@ -12,36 +12,6 @@ import stew/results, libp2p/peerid import waku/incentivization/rpc -const DummyCodec* = "/vac/waku/dummy/0.0.1" - -type DummyResult*[T] = Result[T, string] - -type DummyHandler* = proc( - peer: PeerId, - dummyRequest: DummyRequest -): Future[DummyResult[void]] {.async.} - -type - DummyProtocolErrorKind* {.pure.} = enum - UNKNOWN = uint32(000) - BAD_RESPONSE = uint32(300) - BAD_REQUEST = uint32(400) - PAYMENT_REQUIRED = uint(402) # error type specific for incentivization - NOT_FOUND = uint32(404) - SERVICE_UNAVAILABLE = uint32(503) - PEER_DIAL_FAILURE = uint32(504) - - DummyProtocolError* = object - case kind*: DummyProtocolErrorKind - of PEER_DIAL_FAILURE: - address*: string - of BAD_RESPONSE, BAD_REQUEST, NOT_FOUND, SERVICE_UNAVAILABLE, PAYMENT_REQUIRED: - cause*: string - else: - discard - - DummyProtocolResult* = Result[void, DummyProtocolError] - proc genEligibilityStatus*(isEligible: bool): EligibilityStatus = if isEligible: EligibilityStatus( diff --git a/waku/incentivization/eligibility.nim b/waku/incentivization/eligibility.nim index 9654a946fc..8bc63e3d9c 100644 --- a/waku/incentivization/eligibility.nim +++ b/waku/incentivization/eligibility.nim @@ -18,9 +18,3 @@ import proc isEligible*(eligibilityProof: EligibilityProof, ethClient: string): Future[bool] {.async.} = result = await txidEligiblityCriteriaMet(eligibilityProof, ethClient) - -proc genDummyResponseWithEligibilityStatus*(proofValid: bool, requestId: string = ""): DummyResponse = - let eligibilityStatus = genEligibilityStatus(proofValid) - result.requestId = requestId - result.eligibilityStatus = eligibilityStatus - diff --git a/waku/incentivization/protocol.nim b/waku/incentivization/protocol.nim deleted file mode 100644 index d49caaf9b2..0000000000 --- a/waku/incentivization/protocol.nim +++ /dev/null @@ -1,60 +0,0 @@ -import - std/[options, sequtils, sets, strutils, tables], - stew/byteutils, - chronicles, - chronos, - libp2p/peerid, - libp2p/protocols/protocol -import - ../node/peer_manager, - ../waku_core, - ./common, - ./rpc_codec, - ./rpc, - ./eligibility - -logScope: - topics = "waku incentivization PoC" - -type DummyProtocol* = ref object of LPProtocol - peerManager*: PeerManager - dummyHandler*: DummyHandler - ethClient*: string - -proc handleRequest*( - dummyProtocol: DummyProtocol, peerId: PeerId, buffer: seq[byte] - ): Future[DummyResponse] {.async.} = - let reqDecodeRes = DummyRequest.decode(buffer) - var isProofValid = false - var requestId = "" - if reqDecodeRes.isOk(): - let dummyRequest = reqDecodeRes.get() - let eligibilityProof = dummyRequest.eligibilityProof - requestId = dummyRequest.requestId - isProofValid = await isEligible(eligibilityProof, dummyProtocol.ethClient) - let response = genDummyResponseWithEligibilityStatus(isProofValid, requestId) - return response - -proc initProtocolHandler(dummyProtocol: DummyProtocol) = - proc handle(conn: Connection, proto: string) {.async.} = - let buffer = await conn.readLp(DefaultMaxRpcSize) - var dummyResponse = await handleRequest(dummyProtocol, conn.peerId, buffer) - await conn.writeLp(dummyResponse.encode().buffer) - - dummyProtocol.handler = handle - dummyProtocol.codec = DummyCodec - -proc new*( - T: type DummyProtocol, - peerManager: PeerManager, - dummyHandler: DummyHandler, - ethClient: string, - ): T = - let dummyProtocol = DummyProtocol( - peerManager: peerManager, - dummyHandler: dummyHandler, - ethClient: ethClient - ) - dummyProtocol.initProtocolHandler() - return dummyProtocol - diff --git a/waku/incentivization/rpc.nim b/waku/incentivization/rpc.nim index 1477d4f5fd..755cce2e44 100644 --- a/waku/incentivization/rpc.nim +++ b/waku/incentivization/rpc.nim @@ -11,13 +11,3 @@ type EligibilityStatus* = object statusCode*: uint32 statusDesc*: Option[string] - - DummyRequest* = object - requestId*: string - # request content goes here - eligibilityProof*: EligibilityProof - - DummyResponse* = object - requestId*: string - # response content goes here - eligibilityStatus*: EligibilityStatus diff --git a/waku/incentivization/rpc_codec.nim b/waku/incentivization/rpc_codec.nim index e8888c1494..e06180c431 100644 --- a/waku/incentivization/rpc_codec.nim +++ b/waku/incentivization/rpc_codec.nim @@ -51,47 +51,3 @@ proc decode*(T: type EligibilityStatus, buffer: seq[byte]): ProtobufResult[T] = else: esRpc.statusDesc = some(description) ok(esRpc) - -# Codec for DummyRequest - -proc encode*(request: DummyRequest): ProtoBuffer = - var pb = initProtoBuffer() - pb.write3(1, request.requestId) - pb.write3(10, request.eligibilityProof.encode()) - pb - -proc decode*(T: type DummyRequest, buffer: seq[byte]): ProtobufResult[T] = - let pb = initProtoBuffer(buffer) - var request = DummyRequest() - - if not ?pb.getField(1, request.requestId): - return err(ProtobufError.missingRequiredField("requestId")) - - var eligibilityProofBytes: seq[byte] - if not ?pb.getField(10, eligibilityProofBytes): - return err(ProtobufError.missingRequiredField("eligibilityProof")) - else: - request.eligibilityProof = ?EligibilityProof.decode(eligibilityProofBytes) - ok(request) - -# Codec for DummyResponse - -proc encode*(response: DummyResponse): ProtoBuffer = - var pb = initProtoBuffer() - pb.write3(1, response.requestId) - pb.write3(5, response.eligibilityStatus.encode()) - pb - -proc decode*(T: type DummyResponse, buffer: seq[byte]): ProtobufResult[T] = - let pb = initProtoBuffer(buffer) - var response = DummyResponse() - - if not ?pb.getField(1, response.requestId): - return err(ProtobufError.missingRequiredField("requestId")) - - var eligibilityStatusBytes: seq[byte] - if not ?pb.getField(5, eligibilityStatusBytes): - return err(ProtobufError.missingRequiredField("eligibilityStatus")) - else: - response.eligibilityStatus = ?EligibilityStatus.decode(eligibilityStatusBytes) - ok(response) diff --git a/waku/incentivization/txid_proof.nim b/waku/incentivization/txid_proof.nim index ddaaac6c0f..63de54f53b 100644 --- a/waku/incentivization/txid_proof.nim +++ b/waku/incentivization/txid_proof.nim @@ -6,10 +6,6 @@ import import waku/incentivization/rpc -proc genDummyRequestWithTxIdEligibilityProof*(txHashAsBytes: seq[byte], requestId: string = ""): DummyRequest = - result.requestId = requestId - result.eligibilityProof = EligibilityProof(proofOfPayment: some(txHashAsBytes)) - proc checkTxIdExists(txHash: TxHash, ethClient: string): Future[bool] {.async.} = let web3 = await newWeb3(ethClient) try: From 3cfdd01ba1c5a003b2887ce19a1cc4c125986347 Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 31 Oct 2024 13:07:02 +0100 Subject: [PATCH 4/7] fix formatting --- tests/incentivization/test_rpc_codec.nim | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/incentivization/test_rpc_codec.nim b/tests/incentivization/test_rpc_codec.nim index f2b2281f7b..58b9b09b16 100644 --- a/tests/incentivization/test_rpc_codec.nim +++ b/tests/incentivization/test_rpc_codec.nim @@ -28,21 +28,21 @@ suite "Waku Incentivization Eligibility Codec": check: epRpc == decoded - asyncTest "encode eligibility proof": - let txHash = TxHash.fromHex( - "0x0000000000000000000000000000000000000000000000000000000000000000") - let txHashAsBytes = @(txHash.bytes()) - let eligibilityProof = EligibilityProof(proofOfPayment: some(txHashAsBytes)) - let encoded = encode(eligibilityProof) - let decoded = EligibilityProof.decode(encoded.buffer).get() - check: - eligibilityProof == decoded - - asyncTest "encode eligibility status": - let eligibilityStatus = genEligibilityStatus(true) - let encoded = encode(eligibilityStatus) - let decoded = EligibilityStatus.decode(encoded.buffer).get() - check: - eligibilityStatus == decoded + asyncTest "encode eligibility proof": + let txHash = TxHash.fromHex( + "0x0000000000000000000000000000000000000000000000000000000000000000") + let txHashAsBytes = @(txHash.bytes()) + let eligibilityProof = EligibilityProof(proofOfPayment: some(txHashAsBytes)) + let encoded = encode(eligibilityProof) + let decoded = EligibilityProof.decode(encoded.buffer).get() + check: + eligibilityProof == decoded + + asyncTest "encode eligibility status": + let eligibilityStatus = genEligibilityStatus(true) + let encoded = encode(eligibilityStatus) + let decoded = EligibilityStatus.decode(encoded.buffer).get() + check: + eligibilityStatus == decoded From 3bf670b539589b26473127fee197d23cc4e5fccb Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 31 Oct 2024 14:00:26 +0100 Subject: [PATCH 5/7] add eligibility tests by txid --- tests/incentivization/test_poc.nim | 22 ++++++++++------------ tests/incentivization/test_rpc_codec.nim | 8 ++++++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/incentivization/test_poc.nim b/tests/incentivization/test_poc.nim index 280a8d17c5..03fb833da9 100644 --- a/tests/incentivization/test_poc.nim +++ b/tests/incentivization/test_poc.nim @@ -18,7 +18,8 @@ import rpc, rpc_codec, common, - txid_proof + txid_proof, + eligibility ] @@ -33,20 +34,17 @@ const TxHashNonExisting* = TxHash.fromHex( const EthClient = "https://sepolia.infura.io/v3/470c2e9a16f24057aee6660081729fb9" -#suite "Waku Incentivization PoC Eligibility Proofs": +suite "Waku Incentivization PoC Eligibility Proofs": - # TODO: test eligibility without dummy protocol - - #[ asyncTest "incentivization PoC: dummy protocol with a valid txid eligibility proof": - let request = genDummyRequestWithTxIdEligibilityProof(@(TxHashExisting.bytes())) - let response = await client.sendRequest(request, serverRemotePeerInfo) + let eligibilityProof = EligibilityProof(proofOfPayment: some(@(TxHashExisting.bytes()))) + let txIdExists = await isEligible(eligibilityProof, EthClient) check: - response.isOk() + txIdExists asyncTest "incentivization PoC: dummy protocol client with an invalid txid eligibility proof": - let request = genDummyRequestWithTxIdEligibilityProof(@(TxHashNonExisting.bytes())) - let response = await client.sendRequest(request, serverRemotePeerInfo) + let eligibilityProof = EligibilityProof(proofOfPayment: some(@(TxHashNonExisting.bytes()))) + let txIdExists = await isEligible(eligibilityProof, EthClient) check: - response.isErr() - ]# + not txIdExists + diff --git a/tests/incentivization/test_rpc_codec.nim b/tests/incentivization/test_rpc_codec.nim index 58b9b09b16..c622a77937 100644 --- a/tests/incentivization/test_rpc_codec.nim +++ b/tests/incentivization/test_rpc_codec.nim @@ -14,13 +14,17 @@ import eligibility ] +#[ let txHash = TxHash.fromHex( "0x0000000000000000000000000000000000000000000000000000000000000000" ) let txHashAsBytes = @(txHash.bytes()) +]# suite "Waku Incentivization Eligibility Codec": - asyncTest "encode eligibility proof": + + asyncTest "encode eligibility proof from bytes": + # FIXME: remove this test? var byteSequence: seq[byte] = @[1, 2, 3, 4, 5, 6, 7, 8] let epRpc = EligibilityProof(proofOfPayment: some(byteSequence)) let encoded = encode(epRpc) @@ -28,7 +32,7 @@ suite "Waku Incentivization Eligibility Codec": check: epRpc == decoded - asyncTest "encode eligibility proof": + asyncTest "encode eligibility proof from txid": let txHash = TxHash.fromHex( "0x0000000000000000000000000000000000000000000000000000000000000000") let txHashAsBytes = @(txHash.bytes()) From f4c04dc1e4126dfae1cfbef906462d86e3c5c67e Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 28 Nov 2024 14:46:36 +0700 Subject: [PATCH 6/7] replace eth_getTransactionByHash with getMinedTransactionReceipt (FIXME: sends multiple queries) --- waku/incentivization/txid_proof.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/waku/incentivization/txid_proof.nim b/waku/incentivization/txid_proof.nim index 63de54f53b..e5ee0b0951 100644 --- a/waku/incentivization/txid_proof.nim +++ b/waku/incentivization/txid_proof.nim @@ -9,7 +9,8 @@ import waku/incentivization/rpc proc checkTxIdExists(txHash: TxHash, ethClient: string): Future[bool] {.async.} = let web3 = await newWeb3(ethClient) try: - discard await web3.provider.eth_getTransactionByHash(txHash) + #discard await web3.provider.eth_getTransactionByHash(txHash) + discard await web3.getMinedTransactionReceipt(txHash) # FIXME: keeps querying result = true except ValueError as e: result = false From 31f7fee26bbf811b5b467dce591da8fb8a43e742 Mon Sep 17 00:00:00 2001 From: Sergei Tikhomirov Date: Thu, 28 Nov 2024 15:08:47 +0700 Subject: [PATCH 7/7] add receipt extraction after txid existence check --- waku/incentivization/txid_proof.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/waku/incentivization/txid_proof.nim b/waku/incentivization/txid_proof.nim index e5ee0b0951..df03efd546 100644 --- a/waku/incentivization/txid_proof.nim +++ b/waku/incentivization/txid_proof.nim @@ -9,11 +9,16 @@ import waku/incentivization/rpc proc checkTxIdExists(txHash: TxHash, ethClient: string): Future[bool] {.async.} = let web3 = await newWeb3(ethClient) try: - #discard await web3.provider.eth_getTransactionByHash(txHash) - discard await web3.getMinedTransactionReceipt(txHash) # FIXME: keeps querying + discard await web3.provider.eth_getTransactionByHash(txHash) result = true except ValueError as e: result = false + if result: + try: + discard await web3.getMinedTransactionReceipt(txHash) + result = true + except ValueError as e: + result = false await web3.close() result