diff --git a/src/avalanche/delegationbuilder.h b/src/avalanche/delegationbuilder.h --- a/src/avalanche/delegationbuilder.h +++ b/src/avalanche/delegationbuilder.h @@ -26,7 +26,14 @@ public: explicit DelegationBuilder(const Proof &p); - bool importDelegation(const Delegation &d); + /** + * Import the levels from an existing delegation, up to the given public + * key. + * @param[in] d The delegation to import + * @param[in] pubkey The public key of the last level to import + * @return bool True on success + */ + bool importDelegation(const Delegation &d, const CPubKey &pubkey); bool addLevel(const CKey &key, const CPubKey &newMaster); diff --git a/src/avalanche/delegationbuilder.cpp b/src/avalanche/delegationbuilder.cpp --- a/src/avalanche/delegationbuilder.cpp +++ b/src/avalanche/delegationbuilder.cpp @@ -13,7 +13,8 @@ levels.push_back({p.getMaster(), {}}); } -bool DelegationBuilder::importDelegation(const Delegation &d) { +bool DelegationBuilder::importDelegation(const Delegation &d, + const CPubKey &pubkey) { if (d.getProofId() != proofid) { return false; } @@ -27,12 +28,19 @@ return true; } - dgid = d.getId(); for (auto &l : d.levels) { levels.back().sig = l.sig; levels.push_back({l.pubkey, {}}); + + if (l.pubkey == pubkey) { + break; + } } + // Compute the delegation id. The one from the imported delegation might + // differ if some levels got stripped. + dgid = build().computeDelegationId(); + return true; } 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 @@ -174,4 +174,73 @@ } } +static std::pair AddDelegationLevels(DelegationBuilder &dgb, + const Proof &p, + const CKey &master, + size_t levels) { + CKey privkey = master; + for (size_t i = 0; i < levels; i++) { + CKey k; + k.MakeNewKey(true); + BOOST_CHECK(dgb.addLevel(privkey, k.GetPubKey())); + privkey = k; + } + const Delegation dg = dgb.build(); + CheckDelegation(dg, p, privkey.GetPubKey()); + + return std::pair(dg, privkey); +} + +BOOST_AUTO_TEST_CASE(importDelegation) { + CKey key; + key.MakeNewKey(true); + + const Proof p = buildRandomProof(123456, key.GetPubKey()); + + std::pair dgKey5, dgKey10; + { + DelegationBuilder dgb(p); + dgKey5 = AddDelegationLevels(dgb, p, key, 5); + dgKey10 = AddDelegationLevels(dgb, p, dgKey5.second, 5); + BOOST_CHECK(dgKey5.first.getId() != dgKey10.first.getId()); + } + + { + DelegationBuilder dgb(p); + + // Import 5 levels delegation with no pubkey limit + BOOST_CHECK(dgb.importDelegation(dgKey5.first, CPubKey())); + BOOST_CHECK(dgb.build().getId() == dgKey5.first.getId()); + + // We cannot import twice + BOOST_CHECK(!dgb.importDelegation(dgKey5.first, CPubKey())); + } + { + DelegationBuilder dgb(p); + + // Import the 5 levels delegation with the last pubkey as a limit + BOOST_CHECK( + dgb.importDelegation(dgKey5.first, dgKey5.second.GetPubKey())); + BOOST_CHECK(dgb.build().getId() == dgKey5.first.getId()); + } + { + DelegationBuilder dgb(p); + + // Import the 10 levels delegation with the last pubkey as a limit + BOOST_CHECK( + dgb.importDelegation(dgKey10.first, dgKey10.second.GetPubKey())); + BOOST_CHECK(dgb.build().getId() == dgKey10.first.getId()); + } + { + DelegationBuilder dgb(p); + + // Import the 10 levels delegation with the last pubkey from the 5 + // levels delegation + BOOST_CHECK( + dgb.importDelegation(dgKey10.first, dgKey5.second.GetPubKey())); + const Delegation dg = dgb.build(); + BOOST_CHECK(dg.getId() == dgKey5.first.getId()); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -272,7 +272,7 @@ "The supplied delegation is not valid"); } - if (!dgb.importDelegation(dg)) { + if (!dgb.importDelegation(dg, auth)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to import the delegation"); }