Compare commits

...

8 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
3879c529c1 Fix: failing assert (#397) 2024-11-28 19:20:44 +10: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
Ekiserrepé
a05d58a6e9 Update README.md (#396)
Updated Xaman link.
2024-11-26 08:52:49 +10:00
8 changed files with 254 additions and 14 deletions

View File

@@ -67,5 +67,5 @@ git-subtree. See those directories' README files for more details.
- [explorer.xahau.network](https://explorer.xahau.network) - [explorer.xahau.network](https://explorer.xahau.network)
- **Testnet & Faucet**: Test applications and obtain test XAH at [xahau-test.net](https://xahau-test.net) and use the testnet explorer at [explorer.xahau.network](https://explorer.xahau.network). - **Testnet & Faucet**: Test applications and obtain test XAH at [xahau-test.net](https://xahau-test.net) and use the testnet explorer at [explorer.xahau.network](https://explorer.xahau.network).
- **Supporting Wallets**: A list of wallets that support XAH and Xahau-based assets. - **Supporting Wallets**: A list of wallets that support XAH and Xahau-based assets.
- [Xumm](https://xumm.app) - [Xaman](https://xaman.app)
- [Crossmark](https://crossmark.io) - [Crossmark](https://crossmark.io)

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

@@ -710,10 +710,7 @@ Shard::finalize(bool writeSQLite, std::optional<uint256> const& referenceHash)
if (writeSQLite && !storeSQLite(ledger)) if (writeSQLite && !storeSQLite(ledger))
return fail("failed storing to SQLite databases"); return fail("failed storing to SQLite databases");
assert( assert(ledger->info().seq == ledgerSeq && ledger->read(keylet::fees()));
ledger->info().seq == ledgerSeq &&
(ledger->info().seq < XRP_LEDGER_EARLIEST_FEES ||
ledger->read(keylet::fees())));
hash = ledger->info().parentHash; hash = ledger->info().parentHash;
next = std::move(ledger); next = std::move(ledger);

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);
BEAST_EXPECT(
signersSle && signersSle->isFieldPresent(sfSignerEntries));
if (signersSle && signersSle->isFieldPresent(sfSignerEntries))
{
auto const signerEntries = auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries); signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2); BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(signerEntries[0u].getFieldU16(sfSignerWeight) == 1); BEAST_EXPECT(
signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT( BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == carol.id()); signerEntries[0u].getAccountID(sfAccount) == carol.id());
BEAST_EXPECT(signerEntries[1u].getFieldU16(sfSignerWeight) == 1); BEAST_EXPECT(
BEAST_EXPECT(signerEntries[1u].getAccountID(sfAccount) == bob.id()); 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);
} }
}; };