Compare commits

..

2 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
7 changed files with 38 additions and 47 deletions

View File

@@ -471,10 +471,6 @@ ManifestCache::applyManifest(Manifest m)
auto masterKey = m.masterKey; auto masterKey = m.masterKey;
map_.emplace(std::move(masterKey), std::move(m)); map_.emplace(std::move(masterKey), std::move(m));
// Increment sequence to invalidate cached manifest messages
seq_++;
return ManifestDisposition::accepted; return ManifestDisposition::accepted;
} }

View File

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

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

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 = 89; static constexpr std::size_t numFeatures = 88;
/** 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,7 +376,6 @@ 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, std::optional<AccountID> const& id = std::nullopt); cron(uint32_t timestamp, AccountID const& id);
} // namespace keylet } // namespace keylet

View File

@@ -482,7 +482,6 @@ 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, std::optional<AccountID> const& id) cron(uint32_t timestamp, AccountID const& id)
{ {
static const uint256 ns = indexHash(LedgerNameSpace::CRON); static const uint256 ns = indexHash(LedgerNameSpace::CRON);
@@ -481,14 +481,7 @@ cron(uint32_t timestamp, std::optional<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);
if (!id.has_value()) const uint256 accHash = indexHash(LedgerNameSpace::CRON, timestamp, id);
{
// 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);