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;
map_.emplace(std::move(masterKey), std::move(m));
// Increment sequence to invalidate cached manifest messages
seq_++;
return ManifestDisposition::accepted;
}

View File

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

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;
}
@@ -93,16 +102,6 @@ Cron::doApply()
auto& view = ctx_.view();
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);
auto sle = view.peek(keylet::account(id));
@@ -115,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;
}
@@ -124,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;
}
@@ -136,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
@@ -144,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);
@@ -166,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

@@ -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 = 89;
static constexpr std::size_t numFeatures = 88;
/** Amendments that this server supports and the default voting behavior.
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 fixInvalidTxFlags;
extern uint256 const featureExtendedHookState;
extern uint256 const fixCronStacking;
} // namespace ripple

View File

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

View File

@@ -482,7 +482,6 @@ REGISTER_FEATURE(IOUIssuerWeakTSH, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(Cron, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixInvalidTxFlags, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(ExtendedHookState, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixCronStacking, Supported::yes, VoteBehavior::DefaultYes);
// The following amendments are obsolete, but must remain supported
// 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
// (negligible).
Keylet
cron(uint32_t timestamp, std::optional<AccountID> const& id)
cron(uint32_t timestamp, AccountID const& id)
{
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[11] = static_cast<uint8_t>((timestamp >> 0) & 0xFFU);
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);
const uint256 accHash = indexHash(LedgerNameSpace::CRON, timestamp, id);
// final 20 bytes are account ID
std::memcpy(h + 12, accHash.cdata(), 20);