feat: add simple txid-based eligibility check with hard-coded params (#3166)

This commit is contained in:
Sergei Tikhomirov 2024-10-31 12:20:07 +01:00
parent 6627e34e52
commit a3bef8150d
8 changed files with 169 additions and 16 deletions

View File

@ -1 +1,3 @@
import ./test_rpc_codec
import
./test_rpc_codec,
./test_poc

View File

@ -0,0 +1,59 @@
{.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, txid_proof, eligibility]
# All txids from Ethereum Sepolia testnet
const TxHashNonExisting* =
TxHash.fromHex("0x0000000000000000000000000000000000000000000000000000000000000000")
const TxHashContractCreation* =
TxHash.fromHex("0xa2e39bee557144591fb7b2891ef44e1392f86c5ba1fc0afb6c0e862676ffd50f")
const TxHashContractCall* =
TxHash.fromHex("0x2761f066eeae9a259a0247f529133dd01b7f57bf74254a64d897433397d321cb")
const TxHashSimpleTransfer* =
TxHash.fromHex("0xa3985984b2ec3f1c3d473eb57a4820a56748f25dabbf9414f2b8380312b439cc")
const EthClient = "https://sepolia.infura.io/v3/470c2e9a16f24057aee6660081729fb9"
suite "Waku Incentivization PoC Eligibility Proofs":
asyncTest "incentivization PoC: non-existent tx is not eligible":
let eligibilityProof =
EligibilityProof(proofOfPayment: some(@(TxHashNonExisting.bytes())))
let txIsEligible = await isEligible(eligibilityProof, EthClient)
check:
not txIsEligible
asyncTest "incentivization PoC: contract creation tx is not eligible":
let eligibilityProof =
EligibilityProof(proofOfPayment: some(@(TxHashContractCreation.bytes())))
let txIsEligible = await isEligible(eligibilityProof, EthClient)
check:
not txIsEligible
asyncTest "incentivization PoC: contract call tx is not eligible":
# note: assuming payment in native currency (ETH), not a token
let eligibilityProof =
EligibilityProof(proofOfPayment: some(@(TxHashContractCall.bytes())))
let txIsEligible = await isEligible(eligibilityProof, EthClient)
check:
not txIsEligible
asyncTest "incentivization PoC: simple transfer tx is eligible":
let eligibilityProof =
EligibilityProof(proofOfPayment: some(@(TxHashSimpleTransfer.bytes())))
let txIdExists = await isEligible(eligibilityProof, EthClient)
check:
txIdExists
# TODO: add tests for simple transfer txs with wrong amount and wrong receiver

View File

@ -1,25 +1,35 @@
import
std/options,
std/strscans,
testutils/unittests,
chronicles,
chronos,
libp2p/crypto/crypto
libp2p/crypto/crypto,
web3
import
waku/incentivization/[
rpc,
rpc_codec,
common
]
import waku/incentivization/rpc, waku/incentivization/rpc_codec
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)
asyncTest "encode eligibility proof from txid":
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:
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

View File

@ -0,0 +1,13 @@
import std/options
import waku/incentivization/rpc
proc genEligibilityStatus*(isEligible: bool): EligibilityStatus =
if isEligible:
EligibilityStatus(
statusCode: uint32(200),
statusDesc: some("OK"))
else:
EligibilityStatus(
statusCode: uint32(402),
statusDesc: some("Payment Required"))

View File

@ -0,0 +1,6 @@
import std/options, chronos
import waku/incentivization/[rpc, txid_proof]
proc isEligible*(eligibilityProof: EligibilityProof, ethClient: string): Future[bool] {.async.} =
result = await txidEligiblityCriteriaMet(eligibilityProof, ethClient)

View File

@ -1,5 +1,4 @@
import json_serialization, std/options
import ../waku_core
import std/options
# Implementing the RFC:
# https://github.com/vacp2p/rfc/tree/master/content/docs/rfcs/73

View File

@ -1,6 +1,8 @@
import std/options
import ../common/protobuf, ../waku_core, ./rpc
const DefaultMaxRpcSize* = -1
# Codec for EligibilityProof
proc encode*(epRpc: EligibilityProof): ProtoBuffer =

View File

@ -0,0 +1,62 @@
import std/options, chronos, web3, stew/byteutils, stint, strutils
import waku/incentivization/rpc
# Function to convert a hex string to Address
proc toAddress*(hexStr: string): Address =
# Remove the "0x" prefix if it exists
let cleaned = if hexStr.startsWith("0x"): hexStr[2..^1] else: hexStr
# Ensure the length is exactly 40 characters (20 bytes)
if cleaned.len != 40:
raise newException(ValueError, "Invalid hexadecimal string length for Address")
var arr: array[20, byte]
for i in 0 ..< 20:
let byteValue = cleaned[i * 2 ..< i * 2 + 2] # Get two hex characters
arr[i] = byte(parseHexInt(byteValue))
result = Address(arr)
proc checkTxIdIsEligible(txHash: TxHash, ethClient: string): Future[bool] {.async.} =
let web3 = await newWeb3(ethClient)
try:
let tx = await web3.provider.eth_getTransactionByHash(txHash)
let txReceipt = await web3.getMinedTransactionReceipt(txHash)
result = true
if result:
# check that it is not a contract creation tx
let toAddressOption = txReceipt.to
let isContractCreationTx = toAddressOption.isNone
if isContractCreationTx:
result = false
else:
# check that it is a simple transfer (not a contract call)
# a simple transfer uses 21000 gas
let gasUsed = txReceipt.gasUsed
let isSimpleTransferTx = (gasUsed == Quantity(21000))
if not isSimpleTransferTx:
result = false
else:
# check that the amount is "as expected" (hard-coded for now)
let txValue = tx.value
let hasExpectedValue = (txValue == 200500000000005063.u256)
# check that the to address is "as expected" (hard-coded for now)
let toAddress = toAddressOption.get()
let hasExpectedToAddress = (toAddress == toAddress("0x5e809a85aa182a9921edd10a4163745bb3e36284"))
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 checkTxIdIsEligible(txHash, ethClient)
return txExists