Compare commits

...

6 Commits

Author SHA1 Message Date
Denis Angell
b4f7b91216 Merge branch 'dev' into reduced-import 2024-12-06 13:02:32 +01:00
Denis Angell
cba05af058 [fold] clang-format 2024-12-06 13:00:45 +01:00
Denis Angell
8e88a49d26 [enhance] update blackhole requirements
only `tefIMPORT_BLACKHOLED` on;

- SetRegularKey
- SignersListSet
2024-12-06 12:47:32 +01:00
Denis Angell
61753d39bb [fold] clang-format 2024-11-28 09:52:37 +01:00
Denis Angell
0033b3ae4c [fold] add test 2024-11-28 09:51:09 +01:00
Richard Holland
f636f8158e fixReduceImport 2024-11-27 10:16:22 +11:00
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 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))
{
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
// 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.
static constexpr std::size_t numFeatures = 74;
static constexpr std::size_t numFeatures = 75;
/** Amendments that this server supports and the default voting behavior.
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 fix240911;
extern uint256 const fixFloatDivide;
extern uint256 const fixReduceImport;
} // namespace ripple

View File

@@ -184,6 +184,7 @@ enum TEFcodes : TERUnderlyingType {
tefPAST_IMPORT_SEQ,
tefPAST_IMPORT_VL_SEQ,
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 (fix240911, 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
// because they could potentially get enabled.

View File

@@ -116,6 +116,7 @@ transResults()
MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."),
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(tefIMPORT_BLACKHOLED, "Cannot import keying because target account is blackholed."),
MAKE_ERROR(telLOCAL_ERROR, "Local failure."),
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)
{
auto const sle = env.le(keylet::import_vlseq(pk));
if (sle->isFieldPresent(sfImportSequence))
if (sle && sle->isFieldPresent(sfImportSequence))
return (*sle)[sfImportSequence];
return 0;
}
@@ -2672,6 +2672,134 @@ class Import_test : public beast::unit_test::suite
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
{
test::jtx::Env env{
@@ -4580,14 +4708,22 @@ class Import_test : public beast::unit_test::suite
// confirm signers set
auto const [signers, signersSle] =
signersKeyAndSle(*env.current(), alice);
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());
signersSle && signersSle->isFieldPresent(sfSignerEntries));
if (signersSle && signersSle->isFieldPresent(sfSignerEntries))
{
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
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:
void
run() override
@@ -6026,6 +6225,7 @@ public:
testMaxSupply(features);
testMinMax(features);
testHalving(features - featureOwnerPaysFee);
testBlackhole(features);
}
};