diff --git a/tests/incentivization/test_poc.nim b/tests/incentivization/test_poc.nim index 91c030cea..7490c2304 100644 --- a/tests/incentivization/test_poc.nim +++ b/tests/incentivization/test_poc.nim @@ -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) diff --git a/waku/incentivization/eligibility_manager.nim b/waku/incentivization/eligibility_manager.nim index 74d676fba..3343f7186 100644 --- a/waku/incentivization/eligibility_manager.nim +++ b/waku/incentivization/eligibility_manager.nim @@ -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: