diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -909,8 +909,13 @@ "The hex encoded proof matching the identifier."}, {RPCResult::Type::BOOL, "orphan", "Whether the proof is an orphan."}, - {RPCResult::Type::BOOL, "isBoundToPeer", + {RPCResult::Type::BOOL, "boundToPeer", "Whether the proof is bound to an avalanche peer."}, + {RPCResult::Type::BOOL, "conflicting", + "Whether the proof has a conflicting UTXO with an avalanche " + "peer."}, + {RPCResult::Type::BOOL, "finalized", + "Whether the proof is finalized by vote."}, }}, }, RPCExamples{HelpExampleRpc("getrawavalancheproof", "")}, @@ -926,10 +931,17 @@ bool isOrphan = false; bool isBoundToPeer = false; - auto proof = - g_avalanche->withPeerManager([&](avalanche::PeerManager &pm) { + bool conflicting = false; + bool finalized = false; + auto proof = g_avalanche->withPeerManager( + [&](const avalanche::PeerManager &pm) { isOrphan = pm.isOrphan(proofid); isBoundToPeer = pm.isBoundToPeer(proofid); + conflicting = pm.isInConflictingPool(proofid); + finalized = + pm.forPeer(proofid, [&](const avalanche::Peer &p) { + return p.hasFinalized; + }); return pm.getProof(proofid); }); @@ -943,7 +955,9 @@ ss << *proof; ret.pushKV("proof", HexStr(ss)); ret.pushKV("orphan", isOrphan); - ret.pushKV("isBoundToPeer", isBoundToPeer); + ret.pushKV("boundToPeer", isBoundToPeer); + ret.pushKV("conflicting", conflicting); + ret.pushKV("finalized", finalized); return ret; }, diff --git a/test/functional/abc_p2p_avalanche_proof_voting.py b/test/functional/abc_p2p_avalanche_proof_voting.py --- a/test/functional/abc_p2p_avalanche_proof_voting.py +++ b/test/functional/abc_p2p_avalanche_proof_voting.py @@ -20,12 +20,7 @@ AvalancheVote, ) from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - assert_equal, - assert_greater_than, - assert_raises_rpc_error, - try_rpc, -) +from test_framework.util import assert_equal, assert_raises_rpc_error, try_rpc from test_framework.wallet_util import bytes_to_wif QUORUM_NODE_COUNT = 16 @@ -244,18 +239,14 @@ self.log.info("Test the peer replacement rate limit") + def vote_until_finalized(proofid): + self.can_find_proof_in_poll( + proofid, response=AvalancheProofVoteResponse.ACTIVE) + return node.getrawavalancheproof( + f"{proofid:0{64}x}").get("finalized", False) + # Wait until proof_seq30 is finalized - retry = 5 - while retry > 0: - try: - with node.assert_debug_log([f"Avalanche finalized proof {proofid_seq30:0{64}x}"]): - self.wait_until(lambda: not self.can_find_proof_in_poll( - proofid_seq30, response=AvalancheProofVoteResponse.ACTIVE)) - break - except AssertionError: - retry -= 1 - - assert_greater_than(retry, 0) + self.wait_until(lambda: vote_until_finalized(proofid_seq30)) # Not enough assert self.conflicting_proof_cooldown < self.peer_replacement_cooldown diff --git a/test/functional/abc_rpc_avalancheproof.py b/test/functional/abc_rpc_avalancheproof.py --- a/test/functional/abc_rpc_avalancheproof.py +++ b/test/functional/abc_rpc_avalancheproof.py @@ -8,6 +8,7 @@ from test_framework.address import ADDRESS_ECREG_UNSPENDABLE, base58_to_byte from test_framework.avatools import ( + avalanche_proof_from_hex, create_coinbase_stakes, create_stakes, get_proof_ids, @@ -21,6 +22,7 @@ AvalancheProof, FromHex, LegacyAvalancheProof, + msg_avaproof, ) from test_framework.p2p import P2PInterface, p2p_lock from test_framework.test_framework import BitcoinTestFramework @@ -449,6 +451,8 @@ stake_age = node.getblockcount() self.restart_node(0, self.extra_args[0] + [ "-avaproofstakeutxoconfirmations={}".format(stake_age), + '-enableavalancheproofreplacement=1', + '-avalancheconflictingproofcooldown=0' ]) # Good proof @@ -471,11 +475,30 @@ raw_proof = node.getrawavalancheproof("{:064x}".format(proofid)) assert_equal(raw_proof['proof'], proof) assert_equal(raw_proof['orphan'], False) - assert_equal(raw_proof['isBoundToPeer'], True) + assert_equal(raw_proof['boundToPeer'], True) + assert_equal(raw_proof['conflicting'], False) + assert_equal(raw_proof['finalized'], False) assert_raises_rpc_error(-8, "Proof not found", node.getrawavalancheproof, '0' * 64) + conflicting_proof = node.buildavalancheproof( + proof_sequence - 1, proof_expiration, wif_privkey, stakes) + conflicting_proofobj = avalanche_proof_from_hex(conflicting_proof) + conflicting_proofid_hex = f"{conflicting_proofobj.proofid:0{64}x}" + + msg = msg_avaproof() + msg.proof = conflicting_proofobj + peer.send_message(msg) + wait_for_proof(node, conflicting_proofid_hex) + + raw_proof = node.getrawavalancheproof(conflicting_proofid_hex) + assert_equal(raw_proof['proof'], conflicting_proof) + assert_equal(raw_proof['orphan'], False) + assert_equal(raw_proof['boundToPeer'], False) + assert_equal(raw_proof['conflicting'], True) + assert_equal(raw_proof['finalized'], False) + # To orphan the proof, we make it immature by switching to a shorter # chain node.invalidateblock(node.getbestblockhash()) @@ -496,7 +519,9 @@ raw_proof = node.getrawavalancheproof("{:064x}".format(proofid)) assert_equal(raw_proof['proof'], proof) assert_equal(raw_proof['orphan'], True) - assert_equal(raw_proof['isBoundToPeer'], False) + assert_equal(raw_proof['boundToPeer'], False) + assert_equal(raw_proof['conflicting'], False) + assert_equal(raw_proof['finalized'], False) self.log.info("Bad proof should be rejected at startup")