feat: incentivization POC: add double-spend check for txid-based eligibility (#3264)

* add double-spend check for txid-based eligibility

* Apply suggestions from code review

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* split assert into two in double-spending test

* remove unnecessary import

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

---------

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
This commit is contained in:
Sergei Tikhomirov 2025-01-31 18:53:46 +01:00 committed by GitHub
parent 46747fd496
commit c142994772
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 3 deletions

View File

@ -1,7 +1,7 @@
{.used.}
import
std/[options],
std/options,
testutils/unittests,
chronos,
web3,
@ -194,5 +194,22 @@ suite "Waku Incentivization PoC Eligibility Proofs":
assert isEligible.isOk(), isEligible.error
asyncTest "incentivization PoC: double-spend tx is not eligible":
## Test that the same tx submitted twice is not eligible the second time
let eligibilityProof =
EligibilityProof(proofOfPayment: some(@(txHashRightReceiverRightAmount.bytes())))
let isEligibleOnce = await manager.isEligibleTxId(
eligibilityProof, receiverExpected, TxValueExpectedWei
)
let isEligibleTwice = await manager.isEligibleTxId(
eligibilityProof, receiverExpected, TxValueExpectedWei
)
assert isEligibleOnce.isOk()
assert isEligibleTwice.isErr(), isEligibleTwice.error
# Stop Anvil daemon
stopAnvil(runAnvil)

View File

@ -1,4 +1,4 @@
import std/options, chronos, web3, stew/byteutils, stint, results, chronicles
import std/[options, sets], chronos, web3, stew/byteutils, stint, results, chronicles
import waku/incentivization/rpc, tests/waku_rln_relay/[utils_onchain, utils]
@ -7,12 +7,13 @@ const TxReceiptQueryTimeout = 3.seconds
type EligibilityManager* = ref object # FIXME: make web3 private?
web3*: Web3
seenTxIds*: HashSet[TxHash]
# Initialize the eligibilityManager with a web3 instance
proc init*(
T: type EligibilityManager, ethClient: string
): Future[EligibilityManager] {.async.} =
result = EligibilityManager(web3: await newWeb3(ethClient))
return EligibilityManager(web3: await newWeb3(ethClient), seenTxIds: initHashSet[TxHash]())
# TODO: handle error if web3 instance is not established
# Clean up the web3 instance
@ -60,6 +61,11 @@ proc isEligibleTxId*(
var tx: TransactionObject
var txReceipt: ReceiptObject
let txHash = TxHash.fromHex(byteutils.toHex(eligibilityProof.proofOfPayment.get()))
# check that it is not a double-spend
let txHashWasSeen = (txHash in eligibilityManager.seenTxIds)
eligibilityManager.seenTxIds.incl(txHash)
if txHashWasSeen:
return err("TxHash " & $txHash & " was already checked (double-spend attempt)")
try:
let txAndTxReceipt = await eligibilityManager.getTxAndTxReceipt(txHash)
txAndTxReceipt.isOkOr: