add last ledger seq enforcement, accid attestations

This commit is contained in:
Richard Holland
2023-12-03 16:19:18 +00:00
parent d0baeab32f
commit deb4b34b9b
11 changed files with 75 additions and 15 deletions

View File

@@ -39,9 +39,18 @@ Attest::preflight(PreflightContext const& ctx)
auto const flags = ctx.tx.getFlags();
if (ctx.rules.enabled(fix1543) && (flags & tfAttestMask))
if (flags & tfAttestMask)
return temINVALID_FLAG;
bool const hasTxnID = ctx.tx.isFieldPresent(sfAttestedTxnID);
bool const hasAccID = ctx.tx.isFieldPresent(sfAttestedAccID);
if ((hasTxnID && hasAccID) || (!hasTxnID && !hasAccID))
{
JLOG(ctx.j.warn())
<< "Attest: must specify exactly one of: AttestedTxnID, AttestedAccID";
return temMALFORMED;
}
return preflight2(ctx);
}
@@ -59,7 +68,9 @@ Attest::preclaim(PreclaimContext const& ctx)
return terNO_ACCOUNT;
Keylet kl =
keylet::attestation(id, ctx.tx.getFieldH256(sfAttestedTxnID));
ctx.tx.isFieldPresent(sfAttestedTxnID)
? keylet::attestationTxn(id, ctx.tx.getFieldH256(sfAttestedTxnID))
: keylet::attestationAcc(id, ctx.tx.getAccountID(sfAttestedAccID));
uint32_t flags = ctx.tx.getFlags();
@@ -96,9 +107,10 @@ Attest::doApply()
return tecINSUFFICIENT_RESERVE;
}
Keylet kl =
keylet::attestation(account_, ctx_.tx.getFieldH256(sfAttestedTxnID));
ctx_.tx.isFieldPresent(sfAttestedTxnID)
? keylet::attestationTxn(account_, ctx_.tx.getFieldH256(sfAttestedTxnID))
: keylet::attestationAcc(account_, ctx_.tx.getAccountID(sfAttestedAccID));
uint32_t flags = ctx_.tx.getFlags();
@@ -132,7 +144,11 @@ Attest::doApply()
sleA->setAccountID(sfOwner, account_);
sleA->setFieldH256(sfAttestedTxnID, ctx_.tx.getFieldH256(sfAttestedTxnID));
if (ctx_.tx.isFieldPresent(sfAttestedTxnID))
sleA->setFieldH256(sfAttestedTxnID, ctx_.tx.getFieldH256(sfAttestedTxnID));
else
sleA->setAccountID(sfAttestedAccID, ctx_.tx.getAccountID(sfAttestedAccID));
auto const page = view().dirInsert(
keylet::ownerDir(account_), kl, describeOwnerDir(account_));

View File

@@ -99,7 +99,16 @@ preflight0(PreflightContext const& ctx)
<< "applyTransaction: attesters array too big (max 32) or empty.";
return temMALFORMED;
}
// enforce that it must specify a last ledger seq
if (!ctx.tx.isFieldPresent(sfLastLedgerSequence))
{
JLOG(ctx.j.warn())
<< "applyTransaction: txns with attesters must specify a last ledger sequence <= cur + 256";
return temMALFORMED;
}
// sanity check entries
std::set<AccountID> used;
for (auto const& attester: attesters)
@@ -111,12 +120,16 @@ preflight0(PreflightContext const& ctx)
return temMALFORMED;
}
if (used.find(attester.getAccountID(sfAccount)) != used.end())
AccountID const& id = attester.getAccountID(sfAccount);
if (used.find(id) != used.end())
{
JLOG(ctx.j.warn())
<< "applyTransaction: attesters array contained duplicate attester ID.";
return temMALFORMED;
}
used.emplace(id);
}
}
@@ -1974,7 +1987,7 @@ Transactor::operator()()
for (auto const& attester : attesters)
{
Keylet kl = keylet::attestation(attester.getAccountID(sfAccount), txid);
Keylet kl = keylet::attestationTxn(attester.getAccountID(sfAccount), txid);
if (!view().exists(kl))
{
JLOG(j.warn())
@@ -1986,6 +1999,14 @@ Transactor::operator()()
// remove from dir
auto sleA = view().peek(kl);
if (!sleA->isFieldPresent(sfAttestedTxnID))
{
JLOG(j.warn())
<< "Transactor: Warning!!! Attestation is of the wrong type at end of attested txn "
<< txid;
continue;
}
AccountID owner = sleA->getAccountID(sfOwner);
auto sle = view().peek(keylet::account(owner));

View File

@@ -163,6 +163,14 @@ public:
if (ctx.tx.isFieldPresent(sfAttesters) && ctx.view.rules().enabled(featureAttestations))
{
// check last ledger sequence
if (!ctx.tx.isFieldPresent(sfLastLedgerSequence))
return tecINTERNAL;
if (ctx.tx.getFieldU32(sfLastLedgerSequence) > ctx.view.seq() + 256)
return tecLAST_LEDGER_SEQ_TOO_HIGH;
// check if the required attestations are present on ledger
auto const& attesters = ctx.tx.getFieldArray(sfAttesters);
@@ -171,7 +179,7 @@ public:
// each required attestation must exist on the ledger to allow the txn through
// otherwise it gets marked retry
for (auto const& attester : attesters)
if (!ctx.view.exists(keylet::attestation(attester.getAccountID(sfAccount), txid)))
if (!ctx.view.exists(keylet::attestationTxn(attester.getAccountID(sfAccount), txid)))
return terRETRY;
}
return tesSUCCESS;

View File

@@ -298,7 +298,10 @@ Keylet
uritoken(AccountID const& issuer, Blob const& uri) noexcept;
Keylet
attestation(AccountID const& issuer, uint256 const& txnid) noexcept;
attestationTxn(AccountID const& issuer, uint256 const& txnid) noexcept;
Keylet
attestationAcc(AccountID const& issuer, AccountID const& issuee) noexcept;
} // namespace keylet

View File

@@ -553,6 +553,7 @@ extern SF_ACCOUNT const sfEmitCallback;
// account (uncommon)
extern SF_ACCOUNT const sfHookAccount;
extern SF_ACCOUNT const sfNFTokenMinter;
extern SF_ACCOUNT const sfAttestedAccID;
// path set
extern SField const sfPaths;

View File

@@ -337,6 +337,7 @@ enum TECcodes : TERUnderlyingType {
tecXCHAIN_PAYMENT_FAILED = 184, // RESERVED - XCHAIN
tecXCHAIN_SELF_COMMIT = 185, // RESERVED - XCHAIN
tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 186, // RESERVED - XCHAIN
tecLAST_LEDGER_SEQ_TOO_HIGH = 187,
tecLAST_POSSIBLE_ENTRY = 255,
};

View File

@@ -45,7 +45,8 @@ namespace ripple {
*/
enum class LedgerNameSpace : std::uint16_t {
ACCOUNT = 'a',
ATTESTATION = 'A',
ATTESTATION_ACC = 'A',
ATTESTATION_TXN = 't',
DIR_NODE = 'd',
TRUST_LINE = 'r',
OFFER = 'o',
@@ -444,9 +445,14 @@ uritoken(AccountID const& issuer, Blob const& uri) noexcept
LedgerNameSpace::URI_TOKEN, issuer, Slice{uri.data(), uri.size()})};
}
Keylet attestation(AccountID const& issuer, uint256 const& txnid) noexcept
Keylet attestationTxn(AccountID const& issuer, uint256 const& txnid) noexcept
{
return {ltATTESTATION, indexHash(LedgerNameSpace::ATTESTATION, issuer, txnid)};
return {ltATTESTATION, indexHash(LedgerNameSpace::ATTESTATION_TXN, issuer, txnid)};
}
Keylet attestationAcc(AccountID const& issuer, AccountID const& issuee) noexcept
{
return {ltATTESTATION, indexHash(LedgerNameSpace::ATTESTATION_ACC, issuer, issuee)};
}
} // namespace keylet

View File

@@ -368,7 +368,8 @@ LedgerFormats::LedgerFormats()
{
{sfOwner, soeREQUIRED},
{sfOwnerNode, soeREQUIRED},
{sfAttestedTxnID, soeREQUIRED},
{sfAttestedTxnID, soeOPTIONAL},
{sfAttestedAccID, soeOPTIONAL},
{sfPreviousTxnID, soeREQUIRED},
{sfPreviousTxnLgrSeq, soeREQUIRED},
},

View File

@@ -306,6 +306,7 @@ CONSTRUCT_TYPED_SFIELD(sfEmitCallback, "EmitCallback", ACCOUNT,
// account (uncommon)
CONSTRUCT_TYPED_SFIELD(sfHookAccount, "HookAccount", ACCOUNT, 16);
CONSTRUCT_TYPED_SFIELD(sfAttestedAccID, "AttestedAccID", ACCOUNT, 98);
// vector of 256-bit
CONSTRUCT_TYPED_SFIELD(sfIndexes, "Indexes", VECTOR256, 1, SField::sMD_Never);

View File

@@ -91,6 +91,7 @@ transResults()
MAKE_ERROR(tecHOOK_REJECTED, "Rejected by hook on sending or receiving account."),
MAKE_ERROR(tecREQUIRES_FLAG, "The transaction or part-thereof requires a flag that wasn't set."),
MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."),
MAKE_ERROR(tecLAST_LEDGER_SEQ_TOO_HIGH, "The sfLastLedgerSequence was higher than seq + 256."),
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
MAKE_ERROR(tefBAD_AUTH, "Transaction's public key is not authorized."),

View File

@@ -446,7 +446,8 @@ TxFormats::TxFormats()
add(jss::Attest,
ttATTEST,
{
{sfAttestedTxnID, soeREQUIRED},
{sfAttestedTxnID, soeOPTIONAL},
{sfAttestedAccID, soeOPTIONAL},
{sfTicketSequence, soeOPTIONAL},
},
commonFields);