diff --git a/src/avalanche/delegation.h b/src/avalanche/delegation.h --- a/src/avalanche/delegation.h +++ b/src/avalanche/delegation.h @@ -46,6 +46,7 @@ const DelegationId &getId() const { return dgid; } ProofId getProofId() const; + const CPubKey &getProofMaster() const { return proofMaster; } SERIALIZE_METHODS(Delegation, obj) { READWRITE(obj.limitedProofid, obj.proofMaster, obj.levels); diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -299,8 +299,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 " @@ -313,8 +313,9 @@ }, RPCResult{RPCResult::Type::STR_HEX, "delegation", "A string that is a serialized, hex-encoded delegation."}, - RPCExamples{HelpExampleRpc("delegateavalancheproof", - "\"\" \"\" \"\"")}, + RPCExamples{ + HelpExampleRpc("delegateavalancheproof", + "\"\" \"\" \"\"")}, } .Check(request); @@ -325,16 +326,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()) { @@ -344,15 +337,16 @@ 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"); @@ -364,27 +358,31 @@ "The supplied delegation is not valid"); } - if (!dgb.importDelegation(dg)) { + if (privkey.GetPubKey() != auth) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "The supplied private key does not match the delegation"); + } + + dgb.reset(new avalanche::DelegationBuilder{limitedProofId, + dg.getProofMaster()}); + + if (!dgb->importDelegation(dg)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to import the delegation"); } } else { - auth = proof.getMaster(); - } - - if (privkey.GetPubKey() != auth) { - throw JSONRPCError( - RPC_INVALID_PARAMETER, - "The private key does not match the proof or the delegation"); + auth = privkey.GetPubKey(); + dgb.reset(new avalanche::DelegationBuilder{limitedProofId, auth}); } - 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, @@ -167,20 +166,10 @@ random_privkey = gen_privkey() 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, - ) - # 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 +178,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 +190,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, @@ -258,6 +239,8 @@ assert_raises_rpc_error(-8, "The proof is invalid: " + message, node.verifyavalancheproof, proof) + no_stake = node.buildavalancheproof(proof_sequence, proof_expiration, + proof_master, []) check_verifyavalancheproof_failure(no_stake, "no-stake") check_verifyavalancheproof_failure(dust, "amount-below-dust-threshold") check_verifyavalancheproof_failure(duplicate_stake, "duplicated-stake")