Compare commits

..

3 Commits

Author SHA1 Message Date
tequ
4a65401448 Fix Cron stacking (#627) 2025-11-15 17:41:07 +10:00
tequ
8bcebdea42 Support 'cron' type for account_objects (#624) 2025-11-06 15:19:15 +10:00
Alloy Networks
4cc63c028a Change validators.txt to validators-xahau.txt (#619) 2025-11-01 15:26:56 +10:00
9 changed files with 73 additions and 32 deletions

View File

@@ -1769,7 +1769,7 @@ pool.ntp.org
# Unless an absolute path is specified, it will be considered relative to the # Unless an absolute path is specified, it will be considered relative to the
# folder in which the xahaud.cfg file is located. # folder in which the xahaud.cfg file is located.
[validators_file] [validators_file]
validators.txt validators-xahau.txt
# Turn down default logging to save disk space in the long run. # Turn down default logging to save disk space in the long run.
# Valid values here are trace, debug, info, warning, error, and fatal # Valid values here are trace, debug, info, warning, error, and fatal

View File

@@ -1482,9 +1482,13 @@ TxQ::accept(Application& app, OpenView& view)
{ {
uint32_t currentTime = uint32_t currentTime =
view.parentCloseTime().time_since_epoch().count(); view.parentCloseTime().time_since_epoch().count();
uint256 klStart = keylet::cron(0, AccountID(beast::zero)).key; bool fixCron = view.rules().enabled(fixCronStacking);
uint256 const klEnd = std::optional<AccountID> accountID = std::nullopt;
keylet::cron(currentTime + 1, AccountID(beast::zero)).key; if (!fixCron)
accountID = AccountID(beast::zero);
uint256 klStart = keylet::cron(0, accountID).key;
uint256 const klEnd = keylet::cron(currentTime + 1, accountID).key;
std::set<AccountID> cronAccs; std::set<AccountID> cronAccs;

View File

@@ -93,6 +93,16 @@ Cron::doApply()
auto& view = ctx_.view(); auto& view = ctx_.view();
auto const& tx = ctx_.tx; auto const& tx = ctx_.tx;
if (view.rules().enabled(fixCronStacking))
{
if (auto const seq = tx.getFieldU32(sfLedgerSequence);
seq != view.info().seq)
{
JLOG(j_.warn()) << "Cron: wrong ledger seq=" << seq;
return tefFAILURE;
}
}
AccountID const& id = tx.getAccountID(sfOwner); AccountID const& id = tx.getAccountID(sfOwner);
auto sle = view.peek(keylet::account(id)); auto sle = view.peek(keylet::account(id));

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 = 88; static constexpr std::size_t numFeatures = 89;
/** 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
@@ -376,6 +376,7 @@ extern uint256 const featureIOUIssuerWeakTSH;
extern uint256 const featureCron; extern uint256 const featureCron;
extern uint256 const fixInvalidTxFlags; extern uint256 const fixInvalidTxFlags;
extern uint256 const featureExtendedHookState; extern uint256 const featureExtendedHookState;
extern uint256 const fixCronStacking;
} // namespace ripple } // namespace ripple

View File

@@ -298,7 +298,7 @@ Keylet
uritoken(AccountID const& issuer, Blob const& uri); uritoken(AccountID const& issuer, Blob const& uri);
Keylet Keylet
cron(uint32_t timestamp, AccountID const& id); cron(uint32_t timestamp, std::optional<AccountID> const& id = std::nullopt);
} // namespace keylet } // namespace keylet

View File

@@ -482,6 +482,7 @@ REGISTER_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixCronStacking, 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

@@ -466,7 +466,7 @@ uritoken(AccountID const& issuer, Blob const& uri)
// Examples: 100M → ~5.4e-12, 1B → ~5.4e-11, 10B → ~5.4e-10, 100B → ~5.4e-9 // Examples: 100M → ~5.4e-12, 1B → ~5.4e-11, 10B → ~5.4e-10, 100B → ~5.4e-9
// (negligible). // (negligible).
Keylet Keylet
cron(uint32_t timestamp, AccountID const& id) cron(uint32_t timestamp, std::optional<AccountID> const& id)
{ {
static const uint256 ns = indexHash(LedgerNameSpace::CRON); static const uint256 ns = indexHash(LedgerNameSpace::CRON);
@@ -481,7 +481,14 @@ cron(uint32_t timestamp, AccountID const& id)
h[10] = static_cast<uint8_t>((timestamp >> 8) & 0xFFU); h[10] = static_cast<uint8_t>((timestamp >> 8) & 0xFFU);
h[11] = static_cast<uint8_t>((timestamp >> 0) & 0xFFU); h[11] = static_cast<uint8_t>((timestamp >> 0) & 0xFFU);
const uint256 accHash = indexHash(LedgerNameSpace::CRON, timestamp, id); if (!id.has_value())
{
// final 20 bytes are zero
std::memset(h + 12, 0, 20);
return {ltCRON, uint256::fromVoid(h)};
}
const uint256 accHash = indexHash(LedgerNameSpace::CRON, timestamp, *id);
// final 20 bytes are account ID // final 20 bytes are account ID
std::memcpy(h + 12, accHash.cdata(), 20); std::memcpy(h + 12, accHash.cdata(), 20);

View File

@@ -1106,30 +1106,32 @@ chooseLedgerEntryType(Json::Value const& params)
std::pair<RPC::Status, LedgerEntryType> result{RPC::Status::OK, ltANY}; std::pair<RPC::Status, LedgerEntryType> result{RPC::Status::OK, ltANY};
if (params.isMember(jss::type)) if (params.isMember(jss::type))
{ {
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 22> static constexpr std::array<std::pair<char const*, LedgerEntryType>, 23>
types{ types{{
{{jss::account, ltACCOUNT_ROOT}, {jss::account, ltACCOUNT_ROOT},
{jss::amendments, ltAMENDMENTS}, {jss::amendments, ltAMENDMENTS},
{jss::check, ltCHECK}, {jss::check, ltCHECK},
{jss::deposit_preauth, ltDEPOSIT_PREAUTH}, {jss::deposit_preauth, ltDEPOSIT_PREAUTH},
{jss::directory, ltDIR_NODE}, {jss::directory, ltDIR_NODE},
{jss::escrow, ltESCROW}, {jss::escrow, ltESCROW},
{jss::emitted_txn, ltEMITTED_TXN}, {jss::emitted_txn, ltEMITTED_TXN},
{jss::hook, ltHOOK}, {jss::hook, ltHOOK},
{jss::hook_definition, ltHOOK_DEFINITION}, {jss::hook_definition, ltHOOK_DEFINITION},
{jss::hook_state, ltHOOK_STATE}, {jss::hook_state, ltHOOK_STATE},
{jss::fee, ltFEE_SETTINGS}, {jss::fee, ltFEE_SETTINGS},
{jss::hashes, ltLEDGER_HASHES}, {jss::hashes, ltLEDGER_HASHES},
{jss::import_vlseq, ltIMPORT_VLSEQ}, {jss::import_vlseq, ltIMPORT_VLSEQ},
{jss::offer, ltOFFER}, {jss::offer, ltOFFER},
{jss::payment_channel, ltPAYCHAN}, {jss::payment_channel, ltPAYCHAN},
{jss::uri_token, ltURI_TOKEN}, {jss::uri_token, ltURI_TOKEN},
{jss::signer_list, ltSIGNER_LIST}, {jss::signer_list, ltSIGNER_LIST},
{jss::state, ltRIPPLE_STATE}, {jss::state, ltRIPPLE_STATE},
{jss::ticket, ltTICKET}, {jss::ticket, ltTICKET},
{jss::nft_offer, ltNFTOKEN_OFFER}, {jss::nft_offer, ltNFTOKEN_OFFER},
{jss::nft_page, ltNFTOKEN_PAGE}, {jss::nft_page, ltNFTOKEN_PAGE},
{jss::unl_report, ltUNL_REPORT}}}; {jss::unl_report, ltUNL_REPORT},
{jss::cron, ltCRON},
}};
auto const& p = params[jss::type]; auto const& p = params[jss::type];
if (!p.isString()) if (!p.isString())

View File

@@ -781,6 +781,22 @@ public:
auto const& hook = resp[jss::result][jss::account_objects][0u]; auto const& hook = resp[jss::result][jss::account_objects][0u];
BEAST_EXPECT(hook[sfAccount.jsonName] == gw.human()); BEAST_EXPECT(hook[sfAccount.jsonName] == gw.human());
} }
{
// Create a Cron
env(cron::set(gw),
cron::startTime(env.now().time_since_epoch().count() + 100),
cron::delay(100),
cron::repeat(200),
fee(XRP(1)));
env.close();
}
{
// Find the cron.
Json::Value const resp = acct_objs(gw, jss::cron);
BEAST_EXPECT(acct_objs_is_size(resp, 1));
auto const& cron = resp[jss::result][jss::account_objects][0u];
BEAST_EXPECT(cron[sfOwner.jsonName] == gw.human());
}
{ {
// See how "deletion_blockers_only" handles gw's directory. // See how "deletion_blockers_only" handles gw's directory.
Json::Value params; Json::Value params;