diff --git a/src/avalanche/delegation.h b/src/avalanche/delegation.h --- a/src/avalanche/delegation.h +++ b/src/avalanche/delegation.h @@ -45,6 +45,9 @@ explicit Delegation() {} const DelegationId &getId() const { return dgid; } + const LimitedProofId &getLimitedProofId() const { return limitedProofid; } + const CPubKey &getProofMaster() const { return proofMaster; } + ProofId getProofId() const; SERIALIZE_METHODS(Delegation, obj) { diff --git a/src/avalanche/delegationbuilder.h b/src/avalanche/delegationbuilder.h --- a/src/avalanche/delegationbuilder.h +++ b/src/avalanche/delegationbuilder.h @@ -6,28 +6,32 @@ #define BITCOIN_AVALANCHE_DELEGATIONBUILDER_H #include -#include -#include #include class CKey; +class CPubKey; namespace avalanche { +struct LimitedProofId; class Proof; class DelegationBuilder { LimitedProofId limitedProofid; - ProofId proofid; DelegationId dgid; std::vector levels; + DelegationBuilder(const LimitedProofId <dProofId, + const CPubKey &proofMaster, + const DelegationId &delegationId); + public: + DelegationBuilder(const LimitedProofId <dProofId, + const CPubKey &proofMaster); explicit DelegationBuilder(const Proof &p); - - bool importDelegation(const Delegation &d); + explicit DelegationBuilder(const Delegation &dg); bool addLevel(const CKey &delegatorKey, const CPubKey &delegatedPubKey); diff --git a/src/avalanche/delegationbuilder.cpp b/src/avalanche/delegationbuilder.cpp --- a/src/avalanche/delegationbuilder.cpp +++ b/src/avalanche/delegationbuilder.cpp @@ -4,36 +4,37 @@ #include +#include +#include +#include + #include namespace avalanche { -DelegationBuilder::DelegationBuilder(const Proof &p) - : limitedProofid(p.getLimitedId()), proofid(p.getId()), dgid(proofid) { - levels.push_back({p.getMaster(), {}}); +DelegationBuilder::DelegationBuilder(const LimitedProofId <dProofId, + const CPubKey &proofMaster, + const DelegationId &delegationId) + : limitedProofid(ltdProofId), dgid(delegationId) { + levels.push_back({proofMaster, {}}); } -bool DelegationBuilder::importDelegation(const Delegation &d) { - if (d.getProofId() != proofid) { - return false; - } +DelegationBuilder::DelegationBuilder(const LimitedProofId <dProofId, + const CPubKey &proofMaster) + : DelegationBuilder(ltdProofId, proofMaster, + DelegationId(ltdProofId.computeProofId(proofMaster))) {} - if (levels.size() > 1) { - // We already imported a delegation - return false; - } - - if (!d.levels.size()) { - return true; - } +DelegationBuilder::DelegationBuilder(const Proof &p) + : DelegationBuilder(p.getLimitedId(), p.getMaster(), + DelegationId(p.getId())) {} - dgid = d.getId(); - for (auto &l : d.levels) { +DelegationBuilder::DelegationBuilder(const Delegation &dg) + : DelegationBuilder(dg.getLimitedProofId(), dg.getProofMaster(), + dg.getId()) { + for (auto &l : dg.levels) { levels.back().sig = l.sig; levels.push_back({l.pubkey, {}}); } - - return true; } bool DelegationBuilder::addLevel(const CKey &delegatorKey, diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -300,8 +300,8 @@ "delegateavalancheproof", "Delegate the avalanche proof to another public key.\n", { - {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, - "The proof to be delegated."}, + {"limitedproofid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, + "The limited id of the proof to be delegated."}, {"privatekey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key in base58-encoding. Must match the proof master " "public key or the upper level parent delegation public key if " @@ -314,8 +314,9 @@ }, RPCResult{RPCResult::Type::STR_HEX, "delegation", "A string that is a serialized, hex-encoded delegation."}, - RPCExamples{HelpExampleRpc("delegateavalancheproof", - "\"\" \"\" \"\"")}, + RPCExamples{ + HelpExampleRpc("delegateavalancheproof", + "\"\" \"\" \"\"")}, } .Check(request); @@ -326,16 +327,8 @@ throw JSONRPCError(RPC_INTERNAL_ERROR, "Avalanche is not initialized"); } - avalanche::Proof proof; - bilingual_str error; - if (!avalanche::Proof::FromHex(proof, request.params[0].get_str(), error)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, error.original); - } - - avalanche::ProofValidationState proofState; - if (!proof.verify(proofState)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "The proof is invalid"); - } + avalanche::LimitedProofId limitedProofId{ + ParseHashV(request.params[0], "limitedproofid")}; const CKey privkey = DecodeSecret(request.params[1].get_str()); if (!privkey.IsValid()) { @@ -345,47 +338,45 @@ const CPubKey pubkey = ParsePubKey(request.params[2]); - avalanche::DelegationBuilder dgb(proof); - CPubKey auth; + std::unique_ptr dgb; if (request.params.size() >= 4 && !request.params[3].isNull()) { avalanche::Delegation dg; CDataStream ss(ParseHexV(request.params[3], "delegation"), SER_NETWORK, PROTOCOL_VERSION); ss >> dg; - if (dg.getProofId() != proof.getId()) { + if (dg.getProofId() != + limitedProofId.computeProofId(dg.getProofMaster())) { throw JSONRPCError( RPC_INVALID_PARAMETER, "The supplied delegation does not match the proof"); } + CPubKey auth; avalanche::DelegationState dgState; if (!dg.verify(dgState, auth)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "The supplied delegation is not valid"); } - if (!dgb.importDelegation(dg)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, - "Failed to import the delegation"); + if (privkey.GetPubKey() != auth) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "The supplied private key does not match the delegation"); } + dgb = std::make_unique(dg); } else { - auth = proof.getMaster(); - } - - if (privkey.GetPubKey() != auth) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "The private key does not match the proof or the delegation"); + dgb = std::make_unique( + limitedProofId, privkey.GetPubKey()); } - if (!dgb.addLevel(privkey, pubkey)) { + if (!dgb->addLevel(privkey, pubkey)) { throw JSONRPCError(RPC_MISC_ERROR, "Unable to build the delegation"); } CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << dgb.build(); + ss << dgb->build(); return HexStr(ss); } diff --git a/test/functional/abc_p2p_avalanche.py b/test/functional/abc_p2p_avalanche.py --- a/test/functional/abc_p2p_avalanche.py +++ b/test/functional/abc_p2p_avalanche.py @@ -15,6 +15,7 @@ from test_framework.p2p import P2PInterface, p2p_lock from test_framework.messages import ( AvalancheDelegation, + AvalancheProof, AvalancheResponse, AvalancheVote, CInv, @@ -385,11 +386,15 @@ interface_proof_hex = node.buildavalancheproof( proof_sequence, proof_expiration, pubkey.get_bytes().hex(), stakes) + limited_id = FromHex( + AvalancheProof(), + interface_proof_hex).limited_proofid + # delegate delegated_key = ECKey() delegated_key.generate() interface_delegation_hex = node.delegateavalancheproof( - interface_proof_hex, + f"{limited_id:0{64}x}", bytes_to_wif(privkey.get_bytes()), delegated_key.get_pubkey().get_bytes().hex(), None) 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 @@ -76,13 +76,12 @@ self.log.info("Test decodeavalancheproof RPC") proofobj = FromHex(AvalancheProof(), proof) decodedproof = node.decodeavalancheproof(proof) + limited_id_hex = f"{proofobj.limited_proofid:0{64}x}" assert_equal(decodedproof["sequence"], proof_sequence) assert_equal(decodedproof["expiration"], proof_expiration) assert_equal(decodedproof["master"], proof_master) assert_equal(decodedproof["proofid"], f"{proofobj.proofid:0{64}x}") - assert_equal( - decodedproof["limitedid"], - f"{proofobj.limited_proofid:0{64}x}") + assert_equal(decodedproof["limitedid"], limited_id_hex) assert_equal(decodedproof["stakes"][0]["txid"], stakes[0]["txid"]) assert_equal(decodedproof["stakes"][0]["vout"], stakes[0]["vout"]) assert_equal(decodedproof["stakes"][0]["height"], stakes[0]["height"]) @@ -157,7 +156,7 @@ for _ in range(10): delegated_privkey = gen_privkey() delegation = node.delegateavalancheproof( - proof, + limited_id_hex, bytes_to_wif(delegator_privkey.get_bytes()), get_hex_pubkey(delegated_privkey), delegation, @@ -168,19 +167,13 @@ random_pubkey = get_hex_pubkey(random_privkey) # Invalid proof - no_stake = node.buildavalancheproof( - proof_sequence, proof_expiration, proof_master, []) - assert_raises_rpc_error(-8, "The proof is invalid", - node.delegateavalancheproof, - no_stake, - bytes_to_wif(privkey.get_bytes()), - random_pubkey, - ) + no_stake = node.buildavalancheproof(proof_sequence, proof_expiration, + proof_master, []) # Invalid privkey assert_raises_rpc_error(-5, "The private key is invalid", node.delegateavalancheproof, - proof, + limited_id_hex, bytes_to_wif(bytes(32)), random_pubkey, ) @@ -189,7 +182,7 @@ bad_dg = AvalancheDelegation() assert_raises_rpc_error(-8, "The supplied delegation does not match the proof", node.delegateavalancheproof, - proof, + limited_id_hex, bytes_to_wif(privkey.get_bytes()), random_pubkey, bad_dg.serialize().hex(), @@ -201,24 +194,16 @@ bad_dg.levels = [AvalancheDelegationLevel()] assert_raises_rpc_error(-8, "The supplied delegation is not valid", node.delegateavalancheproof, - proof, + limited_id_hex, bytes_to_wif(privkey.get_bytes()), random_pubkey, bad_dg.serialize().hex(), ) - # Wrong privkey, does not match the proof - assert_raises_rpc_error(-8, "The private key does not match the proof or the delegation", - node.delegateavalancheproof, - proof, - bytes_to_wif(random_privkey.get_bytes()), - random_pubkey, - ) - # Wrong privkey, match the proof but does not match the delegation - assert_raises_rpc_error(-8, "The private key does not match the proof or the delegation", + assert_raises_rpc_error(-8, "The supplied private key does not match the delegation", node.delegateavalancheproof, - proof, + limited_id_hex, bytes_to_wif(privkey.get_bytes()), random_pubkey, delegation,