Refactor 4: Transactor extra signing support

This commit is contained in:
Ed Hennis
2025-05-17 15:12:54 +01:00
committed by Bronek Kozicki
parent 4fe3ec8a08
commit fb5d94bbef
5 changed files with 90 additions and 29 deletions

View File

@@ -149,8 +149,8 @@ public:
sMD_ChangeNew = 0x02, // new value when it changes sMD_ChangeNew = 0x02, // new value when it changes
sMD_DeleteFinal = 0x04, // final value when it is deleted sMD_DeleteFinal = 0x04, // final value when it is deleted
sMD_Create = 0x08, // value when it's created sMD_Create = 0x08, // value when it's created
sMD_Always = 0x10, // value when node containing it is affected at all sMD_Always = 0x10, // value when node containing it is affected at all
sMD_BaseTen = 0x20, // value is treated as base 10, overriding behavior sMD_BaseTen = 0x20, // value is treated as base 10, overriding behavior
sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT
// _only_, then it is a pseudo-account // _only_, then it is a pseudo-account
sMD_Default = sMD_Default =

View File

@@ -74,6 +74,10 @@ public:
/** @} */ /** @} */
/** Create an amount from an account ID. Should only be used when the secret
* key is unavailable, such as for pseudo-accounts. */
explicit Account(std::string name, AccountID const& id);
enum AcctStringType { base58Seed, other }; enum AcctStringType { base58Seed, other };
/** Create an account from a base58 seed string. Throws on invalid seed. */ /** Create an account from a base58 seed string. Throws on invalid seed. */
Account(AcctStringType stringType, std::string base58SeedStr); Account(AcctStringType stringType, std::string base58SeedStr);

View File

@@ -86,6 +86,14 @@ Account::Account(AcctStringType stringType, std::string base58SeedStr)
{ {
} }
Account::Account(std::string name, AccountID const& id)
: Account(name, randomKeyPair(KeyType::secp256k1), privateCtorTag{})
{
// override the randomly generated values
id_ = id;
human_ = toBase58(id_);
}
IOU IOU
Account::operator[](std::string const& s) const Account::operator[](std::string const& s) const
{ {

View File

@@ -160,26 +160,28 @@ preflight1(PreflightContext const& ctx, std::uint32_t flagMask)
return tesSUCCESS; return tesSUCCESS;
} }
/** Checks whether the signature appears valid */ std::optional<NotTEC>
NotTEC preflightCheckSimulateKeys(
preflight2(PreflightContext const& ctx) ApplyFlags flags,
STObject const& sigObject,
beast::Journal j)
{ {
if (ctx.flags & tapDRY_RUN) // simulation if (flags & tapDRY_RUN) // simulation
{ {
if (!ctx.tx.getSignature().empty()) if (!sigObject.getFieldVL(sfTxnSignature).empty())
{ {
// NOTE: This code should never be hit because it's checked in the // NOTE: This code should never be hit because it's checked in the
// `simulate` RPC // `simulate` RPC
return temINVALID; // LCOV_EXCL_LINE return temINVALID; // LCOV_EXCL_LINE
} }
if (!ctx.tx.isFieldPresent(sfSigners)) if (!sigObject.isFieldPresent(sfSigners))
{ {
// no signers, no signature - a valid simulation // no signers, no signature - a valid simulation
return tesSUCCESS; return tesSUCCESS;
} }
for (auto const& signer : ctx.tx.getFieldArray(sfSigners)) for (auto const& signer : sigObject.getFieldArray(sfSigners))
{ {
if (signer.isFieldPresent(sfTxnSignature) && if (signer.isFieldPresent(sfTxnSignature) &&
!signer[sfTxnSignature].empty()) !signer[sfTxnSignature].empty())
@@ -191,6 +193,17 @@ preflight2(PreflightContext const& ctx)
} }
return tesSUCCESS; return tesSUCCESS;
} }
return {};
}
/** Checks whether the signature appears valid */
NotTEC
preflight2(PreflightContext const& ctx)
{
if (auto const ret = preflightCheckSimulateKeys(ctx.flags, ctx.tx, ctx.j))
// Skips following checks if the transaction is being simulated,
// regardless of success or failure
return *ret;
auto const sigValid = checkValidity( auto const sigValid = checkValidity(
ctx.app.getHashRouter(), ctx.tx, ctx.rules, ctx.app.config()); ctx.app.getHashRouter(), ctx.tx, ctx.rules, ctx.app.config());
@@ -609,28 +622,44 @@ Transactor::apply()
} }
NotTEC NotTEC
Transactor::checkSign(PreclaimContext const& ctx) Transactor::checkSign(
PreclaimContext const& ctx,
AccountID const& id,
STObject const& sigObject)
{ {
if (ctx.flags & tapDRY_RUN) if (ctx.flags & tapDRY_RUN)
{ {
// This code must be different for `simulate` // This code must be different for `simulate`
// Since the public key may be empty even for single signing // Since the public key may be empty even for single signing
if (ctx.tx.isFieldPresent(sfSigners)) if (sigObject.isFieldPresent(sfSigners))
return checkMultiSign(ctx); return checkMultiSign(ctx, id, sigObject);
return checkSingleSign(ctx); return checkSingleSign(ctx, id, sigObject);
} }
// If the pk is empty, then we must be multi-signing. // If the pk is empty, then we must be multi-signing.
if (ctx.tx.getSigningPubKey().empty()) if (sigObject.getFieldVL(sfSigningPubKey).empty())
return checkMultiSign(ctx); return checkMultiSign(ctx, id, sigObject);
return checkSingleSign(ctx); return checkSingleSign(ctx, id, sigObject);
} }
NotTEC NotTEC
Transactor::checkSingleSign(PreclaimContext const& ctx) Transactor::checkSign(PreclaimContext const& ctx)
{
auto const idAccount = ctx.tx.isFieldPresent(sfDelegate)
? ctx.tx.getAccountID(sfDelegate)
: ctx.tx.getAccountID(sfAccount);
return checkSign(ctx, idAccount, ctx.tx);
}
// TODO generalize
NotTEC
Transactor::checkSingleSign(
PreclaimContext const& ctx,
AccountID const& idAccount,
STObject const& sigObject)
{ {
// Check that the value in the signing key slot is a public key. // Check that the value in the signing key slot is a public key.
auto const pkSigner = ctx.tx.getSigningPubKey(); auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey);
if (!(ctx.flags & tapDRY_RUN) && !publicKeyType(makeSlice(pkSigner))) if (!(ctx.flags & tapDRY_RUN) && !publicKeyType(makeSlice(pkSigner)))
{ {
JLOG(ctx.j.trace()) JLOG(ctx.j.trace())
@@ -639,9 +668,6 @@ Transactor::checkSingleSign(PreclaimContext const& ctx)
} }
// Look up the account. // Look up the account.
auto const idAccount = ctx.tx.isFieldPresent(sfDelegate)
? ctx.tx.getAccountID(sfDelegate)
: ctx.tx.getAccountID(sfAccount);
auto const sleAccount = ctx.view.read(keylet::account(idAccount)); auto const sleAccount = ctx.view.read(keylet::account(idAccount));
if (!sleAccount) if (!sleAccount)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -708,13 +734,14 @@ Transactor::checkSingleSign(PreclaimContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
} }
// TODO generalize
NotTEC NotTEC
Transactor::checkMultiSign(PreclaimContext const& ctx) Transactor::checkMultiSign(
PreclaimContext const& ctx,
AccountID const& id,
STObject const& sigObject)
{ {
auto const id = ctx.tx.isFieldPresent(sfDelegate) // Get id's SignerList and Quorum.
? ctx.tx.getAccountID(sfDelegate)
: ctx.tx.getAccountID(sfAccount);
// Get mTxnAccountID's SignerList and Quorum.
std::shared_ptr<STLedgerEntry const> sleAccountSigners = std::shared_ptr<STLedgerEntry const> sleAccountSigners =
ctx.view.read(keylet::signers(id)); ctx.view.read(keylet::signers(id));
// If the signer list doesn't exist the account is not multi-signing. // If the signer list doesn't exist the account is not multi-signing.
@@ -740,7 +767,7 @@ Transactor::checkMultiSign(PreclaimContext const& ctx)
return accountSigners.error(); return accountSigners.error();
// Get the array of transaction signers. // Get the array of transaction signers.
STArray const& txSigners(ctx.tx.getFieldArray(sfSigners)); STArray const& txSigners(sigObject.getFieldArray(sfSigners));
// Walk the accountSigners performing a variety of checks and see if // Walk the accountSigners performing a variety of checks and see if
// the quorum is met. // the quorum is met.

View File

@@ -217,6 +217,12 @@ protected:
static XRPAmount static XRPAmount
calculateOwnerReserveFee(ReadView const& view, STTx const& tx); calculateOwnerReserveFee(ReadView const& view, STTx const& tx);
static NotTEC
checkSign(
PreclaimContext const& ctx,
AccountID const& id,
STObject const& sigObject);
// Base class always returns true // Base class always returns true
static bool static bool
isEnabled(PreflightContext const& ctx); isEnabled(PreflightContext const& ctx);
@@ -248,9 +254,15 @@ private:
TER TER
payFee(); payFee();
static NotTEC static NotTEC
checkSingleSign(PreclaimContext const& ctx); checkSingleSign(
PreclaimContext const& ctx,
AccountID const& id,
STObject const& sigObject);
static NotTEC static NotTEC
checkMultiSign(PreclaimContext const& ctx); checkMultiSign(
PreclaimContext const& ctx,
AccountID const& id,
STObject const& sigObject);
void trapTransaction(uint256) const; void trapTransaction(uint256) const;
}; };
@@ -281,6 +293,16 @@ preflightCheckSigningKey(STObject const& sigObject, beast::Journal j);
NotTEC NotTEC
preflight1(PreflightContext const& ctx, std::uint32_t flagMask); preflight1(PreflightContext const& ctx, std::uint32_t flagMask);
/** Checks the special signing key state needed for simulation
*
* Normally called from preflight2 with ctx.tx.
*/
std::optional<NotTEC>
preflightCheckSimulateKeys(
ApplyFlags flags,
STObject const& sigObject,
beast::Journal j);
/** Checks whether the signature appears valid */ /** Checks whether the signature appears valid */
NotTEC NotTEC
preflight2(PreflightContext const& ctx); preflight2(PreflightContext const& ctx);