diff --git a/src/avalanche/proofbuilder.h b/src/avalanche/proofbuilder.h --- a/src/avalanche/proofbuilder.h +++ b/src/avalanche/proofbuilder.h @@ -17,7 +17,7 @@ class ProofBuilder { uint64_t sequence; int64_t expirationTime; - CPubKey master; + CKey masterKey; struct StakeSigner { Stake stake; @@ -37,9 +37,9 @@ std::set stakes; public: - ProofBuilder(uint64_t sequence_, int64_t expirationTime_, CPubKey master_) + ProofBuilder(uint64_t sequence_, int64_t expirationTime_, CKey masterKey_) : sequence(sequence_), expirationTime(expirationTime_), - master(std::move(master_)) {} + masterKey(std::move(masterKey_)) {} [[nodiscard]] bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key); diff --git a/src/avalanche/proofbuilder.cpp b/src/avalanche/proofbuilder.cpp --- a/src/avalanche/proofbuilder.cpp +++ b/src/avalanche/proofbuilder.cpp @@ -43,7 +43,7 @@ signedStakes.push_back(handle.value().sign(proofid)); } - return Proof(sequence, expirationTime, std::move(master), + return Proof(sequence, expirationTime, masterKey.GetPubKey(), std::move(signedStakes)); } @@ -59,7 +59,7 @@ CHashWriter ss2(SER_GETHASH, 0); ss2 << ss.GetHash(); - ss2 << master; + ss2 << masterKey.GetPubKey(); return ProofId(ss2.GetHash()); } diff --git a/src/avalanche/test/delegation_tests.cpp b/src/avalanche/test/delegation_tests.cpp --- a/src/avalanche/test/delegation_tests.cpp +++ b/src/avalanche/test/delegation_tests.cpp @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(verify_random) { auto key = CKey::MakeCompressedKey(); - const Proof p = buildRandomProof(123456, key.GetPubKey()); + const Proof p = buildRandomProof(123456, key); DelegationBuilder dgb(p); { diff --git a/src/avalanche/test/orphanproofpool_tests.cpp b/src/avalanche/test/orphanproofpool_tests.cpp --- a/src/avalanche/test/orphanproofpool_tests.cpp +++ b/src/avalanche/test/orphanproofpool_tests.cpp @@ -22,7 +22,7 @@ static std::shared_ptr makeProof(const size_t nStakes) { const Amount v = 5 * COIN; const int height = 1234; - ProofBuilder pb(0, 0, CPubKey()); + ProofBuilder pb(0, 0, CKey::MakeCompressedKey()); for (size_t i = 0; i < nStakes; i++) { TxId txid(GetRandHash()); BOOST_CHECK(pb.addUTXO(COutPoint(txid, 0), v, height, false, diff --git a/src/avalanche/test/peermanager_tests.cpp b/src/avalanche/test/peermanager_tests.cpp --- a/src/avalanche/test/peermanager_tests.cpp +++ b/src/avalanche/test/peermanager_tests.cpp @@ -458,7 +458,7 @@ BOOST_AUTO_TEST_CASE(node_binding_reorg) { avalanche::PeerManager pm; - ProofBuilder pb(0, 0, CPubKey()); + ProofBuilder pb(0, 0, CKey::MakeCompressedKey()); auto key = CKey::MakeCompressedKey(); const CScript script = GetScriptForDestination(PKHash(key.GetPubKey())); COutPoint utxo(TxId(GetRandHash()), 0); @@ -545,8 +545,9 @@ } avalanche::PeerManager pm; + CKey masterKey = CKey::MakeCompressedKey(); const auto getPeerId = [&](const std::vector &outpoints) { - ProofBuilder pb(0, 0, CPubKey()); + ProofBuilder pb(0, 0, masterKey); for (const auto &o : outpoints) { BOOST_CHECK(pb.addUTXO(o, v, height, false, key)); } @@ -578,7 +579,7 @@ // Duplicated input. { - ProofBuilder pb(0, 0, CPubKey()); + ProofBuilder pb(0, 0, CKey::MakeCompressedKey()); COutPoint o(txid1, 3); BOOST_CHECK(pb.addUTXO(o, v, height, false, key)); PeerId peerid = pm.getPeerId(std::make_shared( @@ -614,7 +615,7 @@ const int wrongHeight = 12345; const auto makeProof = [&](const COutPoint &outpoint, const int h) { - ProofBuilder pb(0, 0, CPubKey()); + ProofBuilder pb(0, 0, CKey::MakeCompressedKey()); BOOST_CHECK(pb.addUTXO(outpoint, v, h, false, key)); return std::make_shared(pb.build()); }; diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp --- a/src/avalanche/test/processor_tests.cpp +++ b/src/avalanche/test/processor_tests.cpp @@ -119,7 +119,7 @@ std::shared_ptr GetProof() { size_t current_coinbase = next_coinbase++; const CTransaction &coinbase = *m_coinbase_txns[current_coinbase]; - ProofBuilder pb(0, 0, masterpriv.GetPubKey()); + ProofBuilder pb(0, 0, masterpriv); BOOST_CHECK(pb.addUTXO(COutPoint(coinbase.GetId(), 0), coinbase.vout[0].nValue, current_coinbase + 1, true, coinbaseKey)); diff --git a/src/avalanche/test/proof_tests.cpp b/src/avalanche/test/proof_tests.cpp --- a/src/avalanche/test/proof_tests.cpp +++ b/src/avalanche/test/proof_tests.cpp @@ -45,7 +45,7 @@ const uint64_t sequence = InsecureRandBits(64); const int64_t expiration = InsecureRandBits(64); - ProofBuilder pb(sequence, expiration, master); + ProofBuilder pb(sequence, expiration, key); for (int i = 0; i < 3; i++) { key.MakeNewKey(true); @@ -350,7 +350,7 @@ const uint32_t h, const bool is_coinbase, const CKey &k) { // Generate a proof that match the UTXO. - ProofBuilder pb(0, 0, pubkey); + ProofBuilder pb(0, 0, key); BOOST_CHECK(pb.addUTXO(o, v, h, is_coinbase, k)); Proof p = pb.build(); @@ -401,7 +401,7 @@ // No stake { - Proof p = ProofBuilder(0, 0, pubkey).build(); + Proof p = ProofBuilder(0, 0, key).build(); ProofValidationState state; BOOST_CHECK(!p.verify(state, coins)); @@ -410,7 +410,7 @@ // Dust thresold { - ProofBuilder pb(0, 0, pubkey); + ProofBuilder pb(0, 0, key); BOOST_CHECK( pb.addUTXO(pkh_outpoint, Amount::zero(), height, false, key)); Proof p = pb.build(); @@ -421,7 +421,7 @@ } { - ProofBuilder pb(0, 0, pubkey); + ProofBuilder pb(0, 0, key); BOOST_CHECK(pb.addUTXO(pkh_outpoint, PROOF_DUST_THRESHOLD - 1 * SATOSHI, height, false, key)); Proof p = pb.build(); @@ -433,7 +433,7 @@ // Duplicated input { - ProofBuilder pb(0, 0, pubkey); + ProofBuilder pb(0, 0, key); BOOST_CHECK(pb.addUTXO(pkh_outpoint, value, height, false, key)); Proof p = TestProofBuilder::buildDuplicatedStakes(pb); @@ -450,7 +450,7 @@ coins.AddCoin(other_pkh_outpoint, Coin(other_pkh_output, height, false), false); - ProofBuilder pb(0, 0, pubkey); + ProofBuilder pb(0, 0, key); BOOST_CHECK(pb.addUTXO(pkh_outpoint, value, height, false, key)); BOOST_CHECK(pb.addUTXO(other_pkh_outpoint, value, height, false, key)); Proof p = TestProofBuilder::buildWithReversedOrderStakes(pb); @@ -467,7 +467,6 @@ CCoinsViewCache coins(&coinsDummy); auto key = CKey::MakeCompressedKey(); - const CPubKey pubkey = key.GetPubKey(); const Amount value = 12345 * COIN; const uint32_t height = 10; @@ -478,7 +477,7 @@ } auto computeProofId = [&]() { - ProofBuilder pb(0, 0, pubkey); + ProofBuilder pb(0, 0, key); for (const COutPoint &outpoint : outpoints) { BOOST_CHECK(pb.addUTXO(outpoint, value, height, false, key)); } diff --git a/src/avalanche/test/util.h b/src/avalanche/test/util.h --- a/src/avalanche/test/util.h +++ b/src/avalanche/test/util.h @@ -7,7 +7,7 @@ #include #include -#include +#include #include @@ -15,7 +15,8 @@ constexpr uint32_t MIN_VALID_PROOF_SCORE = 100 * PROOF_DUST_THRESHOLD / COIN; -Proof buildRandomProof(uint32_t score, const CPubKey &master = CPubKey()); +Proof buildRandomProof(uint32_t score, + const CKey &masterKey = CKey::MakeCompressedKey()); bool hasDustStake(const Proof &proof); diff --git a/src/avalanche/test/util.cpp b/src/avalanche/test/util.cpp --- a/src/avalanche/test/util.cpp +++ b/src/avalanche/test/util.cpp @@ -17,7 +17,7 @@ namespace avalanche { -Proof buildRandomProof(uint32_t score, const CPubKey &master) { +Proof buildRandomProof(uint32_t score, const CKey &masterKey) { auto key = CKey::MakeCompressedKey(); const COutPoint o(TxId(GetRandHash()), 0); @@ -33,7 +33,7 @@ coins.AddCoin(o, Coin(CTxOut(v, script), height, is_coinbase), false); } - ProofBuilder pb(0, std::numeric_limits::max(), master); + ProofBuilder pb(0, std::numeric_limits::max(), masterKey); BOOST_CHECK(pb.addUTXO(o, v, height, is_coinbase, std::move(key))); return pb.build(); } @@ -59,7 +59,7 @@ CHashWriter ss2(SER_GETHASH, 0); ss2 << ss.GetHash(); - ss2 << pb.master; + ss2 << pb.masterKey.GetPubKey(); return ProofId(ss2.GetHash()); } @@ -77,7 +77,7 @@ signedStakes.push_back(handle.value().sign(proofid)); } - return Proof(pb.sequence, pb.expirationTime, std::move(pb.master), + return Proof(pb.sequence, pb.expirationTime, pb.masterKey.GetPubKey(), std::move(signedStakes)); } @@ -94,7 +94,7 @@ CHashWriter ss2(SER_GETHASH, 0); ss2 << ss.GetHash(); - ss2 << pb.master; + ss2 << pb.masterKey.GetPubKey(); return ProofId(ss2.GetHash()); } @@ -112,7 +112,7 @@ signedStakes.push_back(signedStake); } - return Proof(pb.sequence, pb.expirationTime, std::move(pb.master), + return Proof(pb.sequence, pb.expirationTime, pb.masterKey.GetPubKey(), std::move(signedStakes)); } diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -190,8 +190,8 @@ "The proof's sequence"}, {"expiration", RPCArg::Type::NUM, RPCArg::Optional::NO, "A timestamp indicating when the proof expire"}, - {"master", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, - "The master public key"}, + {"master", RPCArg::Type::STR, RPCArg::Optional::NO, + "The master private key in base58-encoding"}, { "stakes", RPCArg::Type::ARR, @@ -235,8 +235,13 @@ const uint64_t sequence = request.params[0].get_int64(); const int64_t expiration = request.params[1].get_int64(); - avalanche::ProofBuilder pb(sequence, expiration, - ParsePubKey(request.params[2])); + + CKey masterKey = DecodeSecret(request.params[2].get_str()); + if (!masterKey.IsValid()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid master key"); + } + + avalanche::ProofBuilder pb(sequence, expiration, masterKey); const UniValue &stakes = request.params[3].get_array(); for (size_t i = 0; i < stakes.size(); i++) { diff --git a/test/functional/abc_p2p_avalanche_peer_discovery.py b/test/functional/abc_p2p_avalanche_peer_discovery.py --- a/test/functional/abc_p2p_avalanche_peer_discovery.py +++ b/test/functional/abc_p2p_avalanche_peer_discovery.py @@ -55,7 +55,7 @@ privkey = ECKey() privkey.set(bytes.fromhex( "12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747"), True) - pubkey = privkey.get_pubkey() + wif_privkey = bytes_to_wif(privkey.get_bytes()) self.log.info( "Check the node is signalling the avalanche service bit only if there is a proof.") @@ -71,8 +71,7 @@ proof_sequence = 11 proof_expiration = 12 proof = node.buildavalancheproof( - proof_sequence, proof_expiration, pubkey.get_bytes().hex(), - stakes) + proof_sequence, proof_expiration, wif_privkey, stakes) # Restart the node self.restart_node(0, self.extra_args[0] + [ @@ -122,8 +121,7 @@ stakes = create_coinbase_stakes(node, [blockhashes[1]], addrkey0.key) interface_proof_hex = node.buildavalancheproof( - proof_sequence, proof_expiration, pubkey.get_bytes().hex(), - stakes) + proof_sequence, proof_expiration, wif_privkey, stakes) limited_id = FromHex( AvalancheProof(), interface_proof_hex).limited_proofid diff --git a/test/functional/abc_p2p_avalanche_voting.py b/test/functional/abc_p2p_avalanche_voting.py --- a/test/functional/abc_p2p_avalanche_voting.py +++ b/test/functional/abc_p2p_avalanche_voting.py @@ -19,6 +19,7 @@ assert_equal, wait_until, ) +from test_framework.wallet_util import bytes_to_wif BLOCK_ACCEPTED = 0 BLOCK_INVALID = 1 @@ -138,18 +139,18 @@ privkey = ECKey() privkey.set(bytes.fromhex( "12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747"), True) - pubkey = privkey.get_pubkey() proof_sequence = 11 proof_expiration = 12 proof = node.buildavalancheproof( - proof_sequence, proof_expiration, pubkey.get_bytes().hex(), + proof_sequence, proof_expiration, bytes_to_wif( + privkey.get_bytes()), stakes) # Activate the quorum. for n in quorum: success = node.addavalanchenode( - n.nodeid, pubkey.get_bytes().hex(), proof) + n.nodeid, privkey.get_pubkey().get_bytes().hex(), proof) assert success is True def can_find_block_in_poll(hash, resp=BLOCK_ACCEPTED): diff --git a/test/functional/abc_rpc_addavalanchenode.py b/test/functional/abc_rpc_addavalanchenode.py --- a/test/functional/abc_rpc_addavalanchenode.py +++ b/test/functional/abc_rpc_addavalanchenode.py @@ -19,6 +19,7 @@ from test_framework.util import ( assert_raises_rpc_error, ) +from test_framework.wallet_util import bytes_to_wif def add_interface_node(test_node) -> int: @@ -45,12 +46,13 @@ privkey = ECKey() privkey.generate() + wif_privkey = bytes_to_wif(privkey.get_bytes()) proof_master = privkey.get_pubkey().get_bytes().hex() proof_sequence = 42 proof_expiration = 2000000000 proof = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, stakes) + proof_sequence, proof_expiration, wif_privkey, stakes) nodeid = add_interface_node(node) @@ -74,7 +76,7 @@ "Proof has invalid format", proof="f000") no_stake = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, []) + proof_sequence, proof_expiration, wif_privkey, []) check_addavalanchenode_error(-8, "The proof is invalid: no-stake", proof=no_stake) 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 @@ -72,6 +72,7 @@ privkey = ECKey() privkey.set(bytes.fromhex( "12b004fff7f4b69ef8650e767f18f11ede158148b425660723b9f9a66e61f747"), True) + wif_privkey = bytes_to_wif(privkey.get_bytes()) def get_hex_pubkey(privkey): return privkey.get_pubkey().get_bytes().hex() @@ -81,7 +82,7 @@ proof_expiration = 12 stakes = create_coinbase_stakes(node, [blockhashes[0]], addrkey0.key) proof = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, stakes) + proof_sequence, proof_expiration, wif_privkey, stakes) self.log.info("Test decodeavalancheproof RPC") proofobj = FromHex(AvalancheProof(), proof) @@ -165,7 +166,7 @@ # Invalid proof no_stake = node.buildavalancheproof(proof_sequence, proof_expiration, - proof_master, []) + wif_privkey, []) # Invalid privkey assert_raises_rpc_error(-5, "The private key is invalid", @@ -225,17 +226,17 @@ # Test invalid proofs dust = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, + proof_sequence, proof_expiration, wif_privkey, create_coinbase_stakes(node, [blockhashes[0]], addrkey0.key, amount="0")) dust_amount = Decimal(f"{PROOF_DUST_THRESHOLD * 0.9999:.4f}") dust2 = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, + proof_sequence, proof_expiration, wif_privkey, create_coinbase_stakes(node, [blockhashes[0]], addrkey0.key, amount=str(dust_amount))) missing_stake = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, [{ + proof_sequence, proof_expiration, wif_privkey, [{ 'txid': '0' * 64, 'vout': 0, 'amount': 10000000, @@ -302,11 +303,11 @@ good_proof = node.buildavalancheproof( proof_sequence, proof_expiration, - proof_master, maximum_stakes) + wif_privkey, maximum_stakes) too_many_utxos = node.buildavalancheproof( proof_sequence, proof_expiration, - proof_master, too_many_stakes) + wif_privkey, too_many_stakes) assert node.verifyavalancheproof(good_proof) @@ -330,7 +331,7 @@ check_rpc_failure(too_many_utxos, "too-many-utxos") conflicting_utxo = node.buildavalancheproof( - proof_sequence + 1, proof_expiration, proof_master, stakes) + proof_sequence + 1, proof_expiration, wif_privkey, stakes) assert_raises_rpc_error(-8, "The proof has conflicting utxo with an existing proof", node.sendavalancheproof, conflicting_utxo) diff --git a/test/functional/abc_rpc_buildavalancheproof.py b/test/functional/abc_rpc_buildavalancheproof.py --- a/test/functional/abc_rpc_buildavalancheproof.py +++ b/test/functional/abc_rpc_buildavalancheproof.py @@ -10,6 +10,7 @@ from test_framework.util import ( assert_raises_rpc_error, ) +from test_framework.wallet_util import bytes_to_wif class BuildAvalancheProofTest(BitcoinTestFramework): @@ -26,11 +27,10 @@ privkey = ECKey() privkey.generate() - - proof_master = privkey.get_pubkey().get_bytes().hex() + wif_privkey = bytes_to_wif(privkey.get_bytes()) def check_buildavalancheproof_error( - error_code, error_message, stakes): + error_code, error_message, stakes, master_key=wif_privkey): assert_raises_rpc_error( error_code, error_message, @@ -39,8 +39,7 @@ 0, # Expiration 0, - # Master - proof_master, + master_key, stakes, ) @@ -48,6 +47,12 @@ self.log.info("Error cases") + check_buildavalancheproof_error(-8, + "Invalid master key", + [good_stake], + master_key=bytes_to_wif(b'f00') + ) + negative_vout = good_stake.copy() negative_vout['vout'] = -1 check_buildavalancheproof_error(-22, @@ -89,7 +94,7 @@ ) self.log.info("Happy path") - assert node.buildavalancheproof(0, 0, proof_master, [good_stake]) + assert node.buildavalancheproof(0, 0, wif_privkey, [good_stake]) if __name__ == '__main__': diff --git a/test/functional/abc_rpc_getavalanchepeerinfo.py b/test/functional/abc_rpc_getavalanchepeerinfo.py --- a/test/functional/abc_rpc_getavalanchepeerinfo.py +++ b/test/functional/abc_rpc_getavalanchepeerinfo.py @@ -10,6 +10,7 @@ from test_framework.key import ECKey from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal +from test_framework.wallet_util import bytes_to_wif class GetAvalanchePeerInfoTest(BitcoinTestFramework): @@ -39,7 +40,8 @@ proof_sequence = 11 proof_expiration = 12 proof = node.buildavalancheproof( - proof_sequence, proof_expiration, pubkey.get_bytes().hex(), + proof_sequence, proof_expiration, bytes_to_wif( + privkey.get_bytes()), [stake]) return (pubkey.get_bytes().hex(), proof) diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py --- a/test/functional/p2p_eviction.py +++ b/test/functional/p2p_eviction.py @@ -92,6 +92,7 @@ "Create 4 peers and protect them from eviction by sending us a proof") privkey = ECKey() privkey.generate() + wif_privkey = bytes_to_wif(privkey.get_bytes()) pubkey = privkey.get_pubkey() stakes = create_coinbase_stakes( @@ -103,7 +104,7 @@ proof_peer.sync_with_ping() proof = node.buildavalancheproof( - 42, 2000000000, pubkey.get_bytes().hex(), [stakes[i]]) + 42, 2000000000, wif_privkey, [stakes[i]]) avaproof_msg = msg_avaproof() avaproof_msg.proof = FromHex(AvalancheProof(), proof) @@ -153,7 +154,7 @@ "Create 128 peers and protect them from eviction by sending an avahello message") proof = node.buildavalancheproof( - 42, 2000000000, pubkey.get_bytes().hex(), [stakes[0]]) + 42, 2000000000, wif_privkey, [stakes[0]]) proof_obj = FromHex(AvalancheProof(), proof) delegation = node.delegateavalancheproof( f"{proof_obj.limited_proofid:064x}", diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -27,6 +27,7 @@ p2p_port, wait_until, ) +from test_framework.wallet_util import bytes_to_wif def assert_net_servicesnames(servicesflag, servicenames): @@ -185,7 +186,7 @@ privkey = ECKey() privkey.generate() proof = self.nodes[1].buildavalancheproof( - 42, 2000000000, privkey.get_pubkey().get_bytes().hex(), stake) + 42, 2000000000, bytes_to_wif(privkey.get_bytes()), stake) self.nodes[1].sendavalancheproof(proof) self.sync_proofs()