mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 01:06:48 +00:00
fix: Store Delegate object in delegating and authorized account directories for proper deletion (#6681)
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/Dir.h>
|
||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -41,6 +42,7 @@
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -584,32 +586,179 @@ class Delegate_test : public beast::unit_test::suite
|
||||
testcase("test deleting account");
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this);
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
env.fund(XRP(100000), alice, bob);
|
||||
env.close();
|
||||
|
||||
env(delegate::set(alice, bob, {"Payment"}));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
|
||||
|
||||
for (std::uint32_t i = 0; i < 256; ++i)
|
||||
// Delegator (alice) deletes account: Delegate object is cleaned up from
|
||||
// both alice's and bob's owner directories.
|
||||
{
|
||||
Env env(*this);
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
Account const carol{"carol"};
|
||||
env.fund(XRP(100000), alice, bob, carol);
|
||||
env.close();
|
||||
|
||||
auto const aliceBalance = env.balance(alice);
|
||||
auto const bobBalance = env.balance(bob);
|
||||
env(delegate::set(alice, bob, {"Payment"}));
|
||||
env.close();
|
||||
|
||||
// alice deletes account, this will remove the Delegate object
|
||||
auto const deleteFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(alice, bob), fee(deleteFee));
|
||||
env.close();
|
||||
auto const delegateKey = keylet::delegate(alice.id(), bob.id());
|
||||
BEAST_EXPECT(env.closed()->exists(delegateKey));
|
||||
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
|
||||
BEAST_EXPECT(env.balance(bob) == bobBalance + aliceBalance - deleteFee);
|
||||
auto hasKey = [](xrpl::Dir const& dir, uint256 const& key) {
|
||||
return std::any_of( // NOLINT(modernize-use-ranges)
|
||||
dir.begin(), dir.end(), [&](auto const& sle) { return sle->key() == key; });
|
||||
};
|
||||
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::delegate(alice.id(), bob.id())));
|
||||
// Delegate object should appear in both alice's and bob's directories
|
||||
BEAST_EXPECT(
|
||||
hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), delegateKey.key));
|
||||
BEAST_EXPECT(
|
||||
hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), delegateKey.key));
|
||||
|
||||
for (std::uint32_t i = 0; i < 256; ++i)
|
||||
env.close();
|
||||
|
||||
auto const aliceBalance = env.balance(alice);
|
||||
auto const carolBalance = env.balance(carol);
|
||||
|
||||
// alice deletes account, this will remove the Delegate object from
|
||||
// both alice's and bob's owner directories
|
||||
auto const deleteFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(alice, carol), fee(deleteFee));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(alice.id())));
|
||||
BEAST_EXPECT(!env.closed()->exists(delegateKey));
|
||||
// bob's directory should no longer reference the Delegate object
|
||||
BEAST_EXPECT(
|
||||
!hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), delegateKey.key));
|
||||
BEAST_EXPECT(env.balance(carol) == carolBalance + aliceBalance - deleteFee);
|
||||
}
|
||||
|
||||
// Delegatee (bob) deletes account: Delegate object is cleaned up from
|
||||
// both alice's and bob's owner directories, freeing alice's reserve so
|
||||
// she can subsequently delete her own account.
|
||||
{
|
||||
Env env(*this);
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
Account const carol{"carol"};
|
||||
env.fund(XRP(100000), alice, bob, carol);
|
||||
env.close();
|
||||
|
||||
env(delegate::set(alice, bob, {"Payment"}));
|
||||
env.close();
|
||||
|
||||
auto const delegateKey = keylet::delegate(alice.id(), bob.id());
|
||||
BEAST_EXPECT(env.closed()->exists(delegateKey));
|
||||
|
||||
auto hasKey = [](xrpl::Dir const& dir, uint256 const& key) {
|
||||
return std::any_of( // NOLINT(modernize-use-ranges)
|
||||
dir.begin(), dir.end(), [&](auto const& sle) { return sle->key() == key; });
|
||||
};
|
||||
|
||||
BEAST_EXPECT(
|
||||
hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), delegateKey.key));
|
||||
BEAST_EXPECT(
|
||||
hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), delegateKey.key));
|
||||
|
||||
// The Delegate entry counts against alice's ownerCount.
|
||||
auto const sleAlice = env.closed()->read(keylet::account(alice.id()));
|
||||
BEAST_EXPECT(sleAlice);
|
||||
BEAST_EXPECT(sleAlice->getFieldU32(sfOwnerCount) == 1);
|
||||
|
||||
for (std::uint32_t i = 0; i < 256; ++i)
|
||||
env.close();
|
||||
|
||||
auto const bobBalance = env.balance(bob);
|
||||
auto const carolBalance = env.balance(carol);
|
||||
|
||||
// bob (the authorized/delegatee account) deletes his account.
|
||||
// This must clean up the Delegate object from both alice's and
|
||||
// bob's owner directories so alice's delegation does not survive
|
||||
// a potential account resurrection.
|
||||
auto const deleteFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(bob, carol), fee(deleteFee));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::account(bob.id())));
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(bob.id())));
|
||||
BEAST_EXPECT(!env.closed()->exists(delegateKey));
|
||||
// alice's directory should no longer reference the Delegate object
|
||||
BEAST_EXPECT(
|
||||
!hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), delegateKey.key));
|
||||
BEAST_EXPECT(env.balance(carol) == carolBalance + bobBalance - deleteFee);
|
||||
|
||||
// alice's ownerCount is now 0; she can delete her own account.
|
||||
auto const sleAlice2 = env.closed()->read(keylet::account(alice.id()));
|
||||
BEAST_EXPECT(sleAlice2);
|
||||
BEAST_EXPECT(sleAlice2->getFieldU32(sfOwnerCount) == 0);
|
||||
|
||||
auto const aliceDeleteFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(alice, carol), fee(aliceDeleteFee));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::account(alice.id())));
|
||||
}
|
||||
|
||||
// Multiple delegators -> same delegatee: when the delegatee (bob)
|
||||
// deletes his account, ALL Delegate objects (from alice and carol)
|
||||
// must be cleaned up from every delegator's directory.
|
||||
{
|
||||
Env env(*this);
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
Account const carol{"carol"};
|
||||
Account const dave{"dave"};
|
||||
env.fund(XRP(100000), alice, bob, carol, dave);
|
||||
env.close();
|
||||
|
||||
// Both alice and carol delegate to bob
|
||||
env(delegate::set(alice, bob, {"Payment"}));
|
||||
env(delegate::set(carol, bob, {"EscrowCreate"}));
|
||||
env.close();
|
||||
|
||||
auto const aliceBobKey = keylet::delegate(alice.id(), bob.id());
|
||||
auto const carolBobKey = keylet::delegate(carol.id(), bob.id());
|
||||
|
||||
auto hasKey = [](xrpl::Dir const& dir, uint256 const& key) {
|
||||
return std::any_of( // NOLINT(modernize-use-ranges)
|
||||
dir.begin(), dir.end(), [&](auto const& sle) { return sle->key() == key; });
|
||||
};
|
||||
|
||||
// Both Delegate objects exist and are in bob's directory
|
||||
BEAST_EXPECT(env.closed()->exists(aliceBobKey));
|
||||
BEAST_EXPECT(env.closed()->exists(carolBobKey));
|
||||
BEAST_EXPECT(
|
||||
hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), aliceBobKey.key));
|
||||
BEAST_EXPECT(
|
||||
hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(bob.id())), carolBobKey.key));
|
||||
|
||||
for (std::uint32_t i = 0; i < 256; ++i)
|
||||
env.close();
|
||||
|
||||
auto const bobBalance = env.balance(bob);
|
||||
auto const daveBalance = env.balance(dave);
|
||||
|
||||
auto const deleteFee = drops(env.current()->fees().increment);
|
||||
env(acctdelete(bob, dave), fee(deleteFee));
|
||||
env.close();
|
||||
|
||||
// bob's account and directory are gone
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::account(bob.id())));
|
||||
BEAST_EXPECT(!env.closed()->exists(keylet::ownerDir(bob.id())));
|
||||
|
||||
// Both Delegate objects are erased
|
||||
BEAST_EXPECT(!env.closed()->exists(aliceBobKey));
|
||||
BEAST_EXPECT(!env.closed()->exists(carolBobKey));
|
||||
|
||||
// alice's and carol's directories no longer reference the objects
|
||||
BEAST_EXPECT(
|
||||
!hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(alice.id())), aliceBobKey.key));
|
||||
BEAST_EXPECT(
|
||||
!hasKey(xrpl::Dir(*env.closed(), keylet::ownerDir(carol.id())), carolBobKey.key));
|
||||
|
||||
BEAST_EXPECT(env.balance(dave) == daveBalance + bobBalance - deleteFee);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user