diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3432,6 +3432,20 @@ return -3; }; +/** + * Decide a response for an Avalanche poll about the given transaction. + * + * FIXME This function should be expanded to return different vote responses + * based on inspection of mempool. + * + * @param[in] CTxMemPool The mempool to base our votes on + * @param[in] TxId The id of the transaction being polled for + * @param[out] uint32_t Our current vote for the proof + */ +static uint32_t getAvalancheVoteForTx(CTxMemPool &mempool, const TxId &id) { + return -1; +}; + /** * Decide a response for an Avalanche poll about the given proof. * @@ -5064,6 +5078,9 @@ // If inv's type is known, get a vote for its hash switch (inv.type) { + case MSG_TX: { + vote = getAvalancheVoteForTx(m_mempool, TxId(inv.hash)); + } break; case MSG_BLOCK: { vote = WITH_LOCK(cs_main, return getAvalancheVoteForBlock( BlockHash(inv.hash))); diff --git a/test/functional/abc_p2p_avalanche_transaction_voting.py b/test/functional/abc_p2p_avalanche_transaction_voting.py new file mode 100755 --- /dev/null +++ b/test/functional/abc_p2p_avalanche_transaction_voting.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# Copyright (c) 2022 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test avalanche transaction polling.""" +import random + +from test_framework.avatools import get_ava_p2p_interface +from test_framework.key import ECPubKey +from test_framework.messages import MSG_TX, AvalancheVote, AvalancheVoteError +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal +from test_framework.wallet import MiniWallet + + +class AvalancheTransactionVotingTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.extra_args = [['-enableavalanche=1']] + + def run_test(self): + node = self.nodes[0] + poll_node = get_ava_p2p_interface(node) + + # Create helper to check expected poll responses + avakey = ECPubKey() + avakey.set(bytes.fromhex(node.getavalanchekey())) + + def assert_response(expected): + response = poll_node.wait_for_avaresponse() + r = response.response + + # Verify signature. + assert avakey.verify_schnorr(response.sig, r.get_hash()) + + # Verify correct votes list + votes = r.votes + assert_equal(len(votes), len(expected)) + for i in range(0, len(votes)): + assert_equal(repr(votes[i]), repr(expected[i])) + + # Create random non-existing tx ids and poll for them + tx_ids = [random.randint(0, 2**256) for _ in range(10)] + poll_node.send_poll(tx_ids, MSG_TX) + + # Each tx id should get an UNKNOWN response + assert_response( + [AvalancheVote(AvalancheVoteError.UNKNOWN, id) for id in tx_ids]) + + # Make real txs + num_txs = 5 + wallet = MiniWallet(node) + wallet.generate(num_txs) + + # Mature the coinbases + node.generate(100) + + assert_equal(node.getmempoolinfo()['size'], 0) + tx_ids = [int(wallet.send_self_transfer(from_node=node) + ['txid'], 16) for _ in range(num_txs)] + assert_equal(node.getmempoolinfo()['size'], num_txs) + + # These real txs are also voted as UNKNOWN for now + poll_node.send_poll(tx_ids, MSG_TX) + assert_response( + [AvalancheVote(AvalancheVoteError.UNKNOWN, id) for id in tx_ids]) + + +if __name__ == '__main__': + AvalancheTransactionVotingTest().main()