fixReduceImport (#398)

Co-authored-by: Denis Angell <dangell@transia.co>
This commit is contained in:
Richard Holland
2024-11-27 10:16:22 +11:00
parent e9468d8b4a
commit 532a471a35
6 changed files with 252 additions and 9 deletions

View File

@@ -889,6 +889,45 @@ Import::preclaim(PreclaimContext const& ctx)
} }
auto const& sle = ctx.view.read(keylet::account(ctx.tx[sfAccount])); auto const& sle = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
auto const tt = stpTrans->getTxnType();
if ((tt == ttSIGNER_LIST_SET || tt == ttREGULAR_KEY_SET) &&
ctx.view.rules().enabled(fixReduceImport) && sle)
{
// blackhole check
do
{
// if master key is not set then it is not blackholed
if (!(sle->getFlags() & lsfDisableMaster))
break;
// if a regular key is set then it must be acc 0, 1, or 2 otherwise
// not blackholed
if (sle->isFieldPresent(sfRegularKey))
{
AccountID rk = sle->getAccountID(sfRegularKey);
static const AccountID ACCOUNT_ZERO(0);
static const AccountID ACCOUNT_ONE(1);
static const AccountID ACCOUNT_TWO(2);
if (rk != ACCOUNT_ZERO && rk != ACCOUNT_ONE &&
rk != ACCOUNT_TWO)
break;
}
// if a signer list is set then it's not blackholed
auto const signerListKeylet = keylet::signers(ctx.tx[sfAccount]);
if (ctx.view.exists(signerListKeylet))
break;
// execution to here means it's blackholed
JLOG(ctx.j.warn())
<< "Import: during preclaim target account is blackholed "
<< ctx.tx[sfAccount] << ", bailing.";
return tefIMPORT_BLACKHOLED;
} while (0);
}
if (sle && sle->isFieldPresent(sfImportSequence)) if (sle && sle->isFieldPresent(sfImportSequence))
{ {
uint32_t sleImportSequence = sle->getFieldU32(sfImportSequence); uint32_t sleImportSequence = sle->getFieldU32(sfImportSequence);

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how // Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this. // the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 74; static constexpr std::size_t numFeatures = 75;
/** Amendments that this server supports and the default voting behavior. /** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated Whether they are enabled depends on the Rules defined in the validated
@@ -362,6 +362,7 @@ extern uint256 const fix240819;
extern uint256 const fixPageCap; extern uint256 const fixPageCap;
extern uint256 const fix240911; extern uint256 const fix240911;
extern uint256 const fixFloatDivide; extern uint256 const fixFloatDivide;
extern uint256 const fixReduceImport;
} // namespace ripple } // namespace ripple

View File

@@ -184,6 +184,7 @@ enum TEFcodes : TERUnderlyingType {
tefPAST_IMPORT_SEQ, tefPAST_IMPORT_SEQ,
tefPAST_IMPORT_VL_SEQ, tefPAST_IMPORT_VL_SEQ,
tefNONDIR_EMIT, tefNONDIR_EMIT,
tefIMPORT_BLACKHOLED,
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -468,6 +468,7 @@ REGISTER_FIX (fix240819, Supported::yes, VoteBehavior::De
REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix240911, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fix240911, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixFloatDivide, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fixFloatDivide, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixReduceImport, Supported::yes, VoteBehavior::DefaultYes);
// The following amendments are obsolete, but must remain supported // The following amendments are obsolete, but must remain supported
// because they could potentially get enabled. // because they could potentially get enabled.

View File

@@ -116,6 +116,7 @@ transResults()
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."), MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."), MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."),
MAKE_ERROR(tefNONDIR_EMIT, "An emitted txn was injected into the ledger without a corresponding directory entry."), MAKE_ERROR(tefNONDIR_EMIT, "An emitted txn was injected into the ledger without a corresponding directory entry."),
MAKE_ERROR(tefIMPORT_BLACKHOLED, "Cannot import keying because target account is blackholed."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."), MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
MAKE_ERROR(telBAD_DOMAIN, "Domain too long."), MAKE_ERROR(telBAD_DOMAIN, "Domain too long."),

View File

@@ -79,7 +79,7 @@ class Import_test : public beast::unit_test::suite
importVLSequence(jtx::Env const& env, PublicKey const& pk) importVLSequence(jtx::Env const& env, PublicKey const& pk)
{ {
auto const sle = env.le(keylet::import_vlseq(pk)); auto const sle = env.le(keylet::import_vlseq(pk));
if (sle->isFieldPresent(sfImportSequence)) if (sle && sle->isFieldPresent(sfImportSequence))
return (*sle)[sfImportSequence]; return (*sle)[sfImportSequence];
return 0; return 0;
} }
@@ -2672,6 +2672,134 @@ class Import_test : public beast::unit_test::suite
env(import::import(alice, tmpXpop), ter(temMALFORMED)); env(import::import(alice, tmpXpop), ter(temMALFORMED));
} }
// tefIMPORT_BLACKHOLED - SetRegularKey (w/seed) AccountZero
{
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// Set Regular Key
Json::Value jv;
jv[jss::Account] = alice.human();
const AccountID ACCOUNT_ZERO(0);
jv["RegularKey"] = to_string(ACCOUNT_ZERO);
jv[jss::TransactionType] = jss::SetRegularKey;
env(jv, alice);
// Disable Master Key
env(fset(alice, asfDisableMaster), sig(alice));
env.close();
// Import with Master Key
Json::Value tmpXpop =
import::loadXpop(ImportTCSetRegularKey::w_seed);
env(import::import(alice, tmpXpop),
ter(tefIMPORT_BLACKHOLED),
fee(feeDrops * 10),
sig(alice));
env.close();
}
// tefIMPORT_BLACKHOLED - SetRegularKey (w/seed) AccountOne
{
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// Set Regular Key
Json::Value jv;
jv[jss::Account] = alice.human();
const AccountID ACCOUNT_ONE(1);
jv["RegularKey"] = to_string(ACCOUNT_ONE);
jv[jss::TransactionType] = jss::SetRegularKey;
env(jv, alice);
// Disable Master Key
env(fset(alice, asfDisableMaster), sig(alice));
env.close();
// Import with Master Key
Json::Value tmpXpop =
import::loadXpop(ImportTCSetRegularKey::w_seed);
env(import::import(alice, tmpXpop),
ter(tefIMPORT_BLACKHOLED),
fee(feeDrops * 10),
sig(alice));
env.close();
}
// tefIMPORT_BLACKHOLED - SetRegularKey (w/seed) AccountTwo
{
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// Set Regular Key
Json::Value jv;
jv[jss::Account] = alice.human();
const AccountID ACCOUNT_TWO(2);
jv["RegularKey"] = to_string(ACCOUNT_TWO);
jv[jss::TransactionType] = jss::SetRegularKey;
env(jv, alice);
// Disable Master Key
env(fset(alice, asfDisableMaster), sig(alice));
env.close();
// Import with Master Key
Json::Value tmpXpop =
import::loadXpop(ImportTCSetRegularKey::w_seed);
env(import::import(alice, tmpXpop),
ter(tefIMPORT_BLACKHOLED),
fee(feeDrops * 10),
sig(alice));
env.close();
}
// tefIMPORT_BLACKHOLED - SignersListSet (w/seed)
{
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// Set Regular Key
Json::Value jv;
jv[jss::Account] = alice.human();
const AccountID ACCOUNT_ZERO(0);
jv["RegularKey"] = to_string(ACCOUNT_ZERO);
jv[jss::TransactionType] = jss::SetRegularKey;
env(jv, alice);
// Disable Master Key
env(fset(alice, asfDisableMaster), sig(alice));
env.close();
// Import with Master Key
Json::Value tmpXpop =
import::loadXpop(ImportTCSignersListSet::w_seed);
env(import::import(alice, tmpXpop),
ter(tefIMPORT_BLACKHOLED),
fee(feeDrops * 10),
sig(alice));
env.close();
}
// tefPAST_IMPORT_SEQ // tefPAST_IMPORT_SEQ
{ {
test::jtx::Env env{ test::jtx::Env env{
@@ -4580,14 +4708,22 @@ class Import_test : public beast::unit_test::suite
// confirm signers set // confirm signers set
auto const [signers, signersSle] = auto const [signers, signersSle] =
signersKeyAndSle(*env.current(), alice); signersKeyAndSle(*env.current(), alice);
auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT( BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == carol.id()); signersSle && signersSle->isFieldPresent(sfSignerEntries));
BEAST_EXPECT(signerEntries[1u].getFieldU16(sfSignerWeight) == 1); if (signersSle && signersSle->isFieldPresent(sfSignerEntries))
BEAST_EXPECT(signerEntries[1u].getAccountID(sfAccount) == bob.id()); {
auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(
signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == carol.id());
BEAST_EXPECT(
signerEntries[1u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[1u].getAccountID(sfAccount) == bob.id());
}
// confirm multisign tx // confirm multisign tx
env.close(); env.close();
@@ -5986,6 +6122,69 @@ class Import_test : public beast::unit_test::suite
} }
} }
void
testBlackhole(FeatureBitset features)
{
testcase("blackhole");
using namespace test::jtx;
using namespace std::literals;
auto blackholeAccount = [&](Env& env, Account const& acct) {
// Set Regular Key
Json::Value jv;
jv[jss::Account] = acct.human();
const AccountID ACCOUNT_ZERO(0);
jv["RegularKey"] = to_string(ACCOUNT_ZERO);
jv[jss::TransactionType] = jss::SetRegularKey;
env(jv, acct);
// Disable Master Key
env(fset(acct, asfDisableMaster), sig(acct));
env.close();
};
auto burnHeader = [&](Env& env) {
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(100'000'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 100'000'000'000'000);
};
// AccountSet (w/seed)
{
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// Burn Header
burnHeader(env);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// Blackhole Account
blackholeAccount(env, alice);
// Import with Master Key
Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
env(import::import(alice, tmpXpop),
ter(tesSUCCESS),
fee(feeDrops * 10),
sig(alice));
env.close();
}
}
public: public:
void void
run() override run() override
@@ -6026,6 +6225,7 @@ public:
testMaxSupply(features); testMaxSupply(features);
testMinMax(features); testMinMax(features);
testHalving(features - featureOwnerPaysFee); testHalving(features - featureOwnerPaysFee);
testBlackhole(features);
} }
}; };