Compare commits

...

4 Commits

Author SHA1 Message Date
tequ
1d0b31f85a add LedgerSequence check 2025-11-13 17:42:08 +09:00
tequ
e3863181c5 . 2025-11-13 16:28:16 +09: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
4 changed files with 74 additions and 38 deletions

View File

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

View File

@@ -44,12 +44,15 @@ Cron::preflight(PreflightContext const& ctx)
auto const ret = preflight0(ctx);
if (!isTesSuccess(ret))
{
JLOG(ctx.j.fatal()) << "Cron: preflight failed";
return ret;
}
auto account = ctx.tx.getAccountID(sfAccount);
if (account != beast::zero)
{
JLOG(ctx.j.warn()) << "Cron: Bad source id";
JLOG(ctx.j.fatal()) << "Cron: Bad source id";
return temBAD_SRC_ACCOUNT;
}
@@ -57,21 +60,21 @@ Cron::preflight(PreflightContext const& ctx)
auto const fee = ctx.tx.getFieldAmount(sfFee);
if (!fee.native() || fee != beast::zero)
{
JLOG(ctx.j.warn()) << "Cron: invalid fee";
JLOG(ctx.j.fatal()) << "Cron: invalid fee";
return temBAD_FEE;
}
if (!ctx.tx.getSigningPubKey().empty() || !ctx.tx.getSignature().empty() ||
ctx.tx.isFieldPresent(sfSigners))
{
JLOG(ctx.j.warn()) << "Cron: Bad signature";
JLOG(ctx.j.fatal()) << "Cron: Bad signature";
return temBAD_SIGNATURE;
}
if (ctx.tx.getFieldU32(sfSequence) != 0 ||
ctx.tx.isFieldPresent(sfPreviousTxnID))
{
JLOG(ctx.j.warn()) << "Cron: Bad sequence";
JLOG(ctx.j.fatal()) << "Cron: Bad sequence";
return temBAD_SEQUENCE;
}
@@ -84,6 +87,12 @@ Cron::preclaim(PreclaimContext const& ctx)
if (!ctx.view.rules().enabled(featureCron))
return temDISABLED;
if (ctx.tx.getFieldU32(sfLedgerSequence) != ctx.view.info().seq)
{
JLOG(ctx.j.fatal()) << "Cron: wrong ledger sequence";
return tefFAILURE;
}
return tesSUCCESS;
}
@@ -105,7 +114,7 @@ Cron::doApply()
if (!sle->isFieldPresent(sfCron))
{
JLOG(j_.warn()) << "Cron: sfCron missing from account " << id;
JLOG(j_.fatal()) << "Cron: sfCron missing from account " << id;
return tefINTERNAL;
}
@@ -114,7 +123,7 @@ Cron::doApply()
auto sleCron = view.peek(klOld);
if (!sleCron)
{
JLOG(j_.warn()) << "Cron: Cron object missing for account " << id;
JLOG(j_.fatal()) << "Cron: Cron object missing for account " << id;
return tesSUCCESS;
}
@@ -126,7 +135,10 @@ Cron::doApply()
// do all this sanity checking before we modify the ledger...
uint32_t afterTime = lastStartTime + delay;
if (afterTime < lastStartTime)
{
JLOG(j_.fatal()) << "Cron: afterTime < lastStartTime";
return tefINTERNAL;
}
// in all circumstances the Cron object is deleted...
// if there are further crons to do then a new one is created at the next
@@ -134,7 +146,10 @@ Cron::doApply()
if (!view.dirRemove(
keylet::ownerDir(id), (*sleCron)[sfOwnerNode], klOld, false))
{
JLOG(j_.fatal()) << "Cron: Ownerdir bad. " << id;
return tefBAD_LEDGER;
}
view.erase(sleCron);
@@ -156,19 +171,22 @@ Cron::doApply()
auto const page =
view.dirInsert(keylet::ownerDir(id), klCron, describeOwnerDir(id));
if (!page)
{
JLOG(j_.fatal()) << "Cron: Ownerdir full. " << id;
return tecDIR_FULL;
}
sleCron = std::make_shared<SLE>(klCron);
auto newSleCron = std::make_shared<SLE>(klCron);
sleCron->setFieldU64(sfOwnerNode, *page);
sleCron->setFieldU32(sfDelaySeconds, delay);
sleCron->setFieldU32(sfRepeatCount, recur - 1);
sleCron->setFieldU32(sfStartTime, afterTime);
sleCron->setAccountID(sfOwner, id);
newSleCron->setFieldU64(sfOwnerNode, *page);
newSleCron->setFieldU32(sfDelaySeconds, delay);
newSleCron->setFieldU32(sfRepeatCount, recur - 1);
newSleCron->setFieldU32(sfStartTime, afterTime);
newSleCron->setAccountID(sfOwner, id);
sle->setFieldH256(sfCron, klCron.key);
view.insert(sleCron);
view.insert(newSleCron);
view.update(sle);
return tesSUCCESS;

View File

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

View File

@@ -781,6 +781,22 @@ public:
auto const& hook = resp[jss::result][jss::account_objects][0u];
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.
Json::Value params;