mirror of https://github.com/waku-org/nwaku.git
feat: add i13n PoC with dummy request-response protocol
This commit is contained in:
parent
76d5b2642d
commit
23453cbc65
|
@ -1,2 +1,4 @@
|
|||
import
|
||||
./test_rpc_codec
|
||||
./test_rpc_codec,
|
||||
./test_eligibility,
|
||||
./test_poc
|
|
@ -0,0 +1,24 @@
|
|||
import
|
||||
std/options,
|
||||
testutils/unittests,
|
||||
chronos
|
||||
|
||||
import
|
||||
../../../waku/incentivization/[
|
||||
rpc,common
|
||||
]
|
||||
|
||||
|
||||
suite "Waku Incentivization Eligibility Testing":
|
||||
|
||||
asyncTest "check eligibility success":
|
||||
var byteSequence: seq[byte] = @[1, 2, 3, 4, 5, 6, 7, 8]
|
||||
let eligibilityProof = EligibilityProof(proofOfPayment: some(byteSequence))
|
||||
check:
|
||||
isEligible(eligibilityProof)
|
||||
|
||||
asyncTest "check eligibility failure":
|
||||
var byteSequence: seq[byte] = @[0, 2, 3, 4, 5, 6, 7, 8]
|
||||
let eligibilityProof = EligibilityProof(proofOfPayment: some(byteSequence))
|
||||
check:
|
||||
not isEligible(eligibilityProof)
|
|
@ -0,0 +1,86 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
std/[options, strscans],
|
||||
testutils/unittests,
|
||||
chronicles,
|
||||
chronos,
|
||||
libp2p/crypto/crypto
|
||||
|
||||
import
|
||||
../../../waku/[
|
||||
node/peer_manager,
|
||||
waku_core,
|
||||
],
|
||||
../testlib/[assertions, wakucore, testasync, futures, testutils],
|
||||
../../../waku/incentivization/[
|
||||
rpc,
|
||||
rpc_codec,
|
||||
common,
|
||||
client,
|
||||
protocol,
|
||||
]
|
||||
|
||||
|
||||
proc newTestDummyProtocolNode*(
|
||||
switch: Switch,
|
||||
handler: DummyHandler
|
||||
): Future[DummyProtocol] {.async.} =
|
||||
let
|
||||
peerManager = PeerManager.new(switch)
|
||||
dummyProtocol = DummyProtocol.new(peerManager, handler)
|
||||
|
||||
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)
|
||||
|
||||
# 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 eligibility proof":
|
||||
let request = genDummyRequestWithEligibilityProof(true)
|
||||
let response = await client.sendRequest(request, serverRemotePeerInfo)
|
||||
check:
|
||||
response.isOk()
|
||||
|
||||
asyncTest "incentivization PoC: dummy protocol client with an invalid eligibility proof":
|
||||
let request = genDummyRequestWithEligibilityProof(false)
|
||||
let response = await client.sendRequest(request, serverRemotePeerInfo)
|
||||
check:
|
||||
response.isErr()
|
|
@ -8,25 +8,37 @@ import
|
|||
|
||||
import
|
||||
../../../waku/incentivization/rpc,
|
||||
../../../waku/incentivization/rpc_codec
|
||||
|
||||
../../../waku/incentivization/rpc_codec,
|
||||
../../../waku/incentivization/common
|
||||
|
||||
suite "Waku Incentivization Eligibility Codec":
|
||||
|
||||
asyncTest "encode eligibility proof":
|
||||
var byteSequence: seq[byte] = @[1, 2, 3, 4, 5, 6, 7, 8]
|
||||
let epRpc = EligibilityProof(proofOfPayment: some(byteSequence))
|
||||
let encoded = encode(epRpc)
|
||||
let eligibilityProof = genEligibilityProof(true)
|
||||
let encoded = encode(eligibilityProof)
|
||||
let decoded = EligibilityProof.decode(encoded.buffer).get()
|
||||
check:
|
||||
epRpc == decoded
|
||||
eligibilityProof == decoded
|
||||
|
||||
asyncTest "encode eligibility status":
|
||||
let esRpc = EligibilityStatus(
|
||||
statusCode: uint32(200),
|
||||
statusDesc: some("OK")
|
||||
)
|
||||
let encoded = encode(esRpc)
|
||||
let eligibilityStatus = genEligibilityStatus(true)
|
||||
let encoded = encode(eligibilityStatus)
|
||||
let decoded = EligibilityStatus.decode(encoded.buffer).get()
|
||||
check:
|
||||
esRpc == decoded
|
||||
eligibilityStatus == decoded
|
||||
|
||||
asyncTest "encode dummy request":
|
||||
let dummyRequest = genDummyRequestWithEligibilityProof(true)
|
||||
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
|
||||
|
||||
|
|
@ -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)
|
|
@ -0,0 +1,83 @@
|
|||
import
|
||||
std/options,
|
||||
std/strscans,
|
||||
testutils/unittests,
|
||||
chronicles,
|
||||
chronos,
|
||||
libp2p/crypto/crypto
|
||||
|
||||
import stew/results, chronos, libp2p/peerid
|
||||
|
||||
import
|
||||
../../../waku/incentivization/rpc,
|
||||
../../../waku/incentivization/rpc_codec
|
||||
|
||||
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 genEligibilityProof*(startsWithOne: bool): EligibilityProof =
|
||||
let byteSequence: seq[byte] = (
|
||||
if startsWithOne:
|
||||
@[1, 2, 3, 4, 5, 6, 7, 8]
|
||||
else:
|
||||
@[0, 2, 3, 4, 5, 6, 7, 8])
|
||||
EligibilityProof(proofOfPayment: some(byteSequence))
|
||||
|
||||
proc genEligibilityStatus*(isEligible: bool): EligibilityStatus =
|
||||
if isEligible:
|
||||
EligibilityStatus(
|
||||
statusCode: uint32(200),
|
||||
statusDesc: some("OK"))
|
||||
else:
|
||||
EligibilityStatus(
|
||||
statusCode: uint32(402),
|
||||
statusDesc: some("Payment Required"))
|
||||
|
||||
proc genDummyRequestWithEligibilityProof*(proofValid: bool, requestId: string = ""): DummyRequest =
|
||||
let eligibilityProof = genEligibilityProof(proofValid)
|
||||
result.requestId = requestId
|
||||
result.eligibilityProof = eligibilityProof
|
||||
|
||||
proc genDummyResponseWithEligibilityStatus*(proofValid: bool, requestId: string = ""): DummyResponse =
|
||||
let eligibilityStatus = genEligibilityStatus(proofValid)
|
||||
result.requestId = requestId
|
||||
result.eligibilityStatus = eligibilityStatus
|
||||
|
||||
proc dummyEligibilityCriteriaMet(eligibilityProof: EligibilityProof): bool =
|
||||
# a dummy criterion: the first element of the proof byte array equals 1
|
||||
let proofOfPayment = eligibilityProof.proofOfPayment
|
||||
if proofOfPayment.isSome:
|
||||
return (proofOfPayment.get()[0] == 1)
|
||||
else:
|
||||
return false
|
||||
|
||||
proc isEligible*(eligibilityProof: EligibilityProof): bool =
|
||||
dummyEligibilityCriteriaMet(eligibilityProof)
|
|
@ -0,0 +1,56 @@
|
|||
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
|
||||
|
||||
logScope:
|
||||
topics = "waku incentivization PoC"
|
||||
|
||||
type DummyProtocol* = ref object of LPProtocol
|
||||
peerManager*: PeerManager
|
||||
dummyHandler*: DummyHandler
|
||||
|
||||
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 = isEligible(eligibilityProof)
|
||||
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,
|
||||
): T =
|
||||
let dummyProtocol = DummyProtocol(
|
||||
peerManager: peerManager,
|
||||
dummyHandler: dummyHandler,
|
||||
)
|
||||
dummyProtocol.initProtocolHandler()
|
||||
return dummyProtocol
|
||||
|
|
@ -15,3 +15,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
|
||||
|
|
|
@ -5,6 +5,7 @@ import
|
|||
../waku_core,
|
||||
./rpc
|
||||
|
||||
const DefaultMaxRpcSize* = -1
|
||||
|
||||
# Codec for EligibilityProof
|
||||
|
||||
|
@ -28,7 +29,6 @@ proc decode*(T: type EligibilityProof, buffer: seq[byte]): ProtobufResult[T] =
|
|||
epRpc.proofOfPayment = some(proofOfPayment)
|
||||
ok(epRpc)
|
||||
|
||||
|
||||
# Codec for EligibilityStatus
|
||||
|
||||
proc encode*(esRpc: EligibilityStatus): ProtoBuffer =
|
||||
|
@ -57,3 +57,47 @@ proc decode*(T: type EligibilityStatus, buffer: seq[byte]): ProtobufResult[T] =
|
|||
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)
|
||||
|
|
Loading…
Reference in New Issue