PoC for sponsored fees

This commit is contained in:
Mayukha Vadari
2024-03-13 11:02:49 -04:00
committed by tequ
parent edb4f0342c
commit cad305ac7e
10 changed files with 95 additions and 6 deletions

View File

@@ -244,7 +244,9 @@ public:
getFieldV256(SField const& field) const;
STArray const&
getFieldArray(SField const& field) const;
STCurrency const&
const STObject&
getFieldObject(SField const& field) const;
const STCurrency&
getFieldCurrency(SField const& field) const;
STNumber const&
getFieldNumber(SField const& field) const;

View File

@@ -106,6 +106,9 @@ public:
std::uint32_t
getSeqValue() const;
AccountID
getFeePayer() const;
boost::container::flat_set<AccountID>
getMentionedAccounts() const;

View File

@@ -62,6 +62,10 @@ constexpr std::uint32_t tfInnerBatchTxn = 0x40000000;
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn;
constexpr std::uint32_t tfUniversalMask = ~tfUniversal;
// Sponsor flags:
constexpr std::uint32_t tfSponsorFee = 0x00000001;
constexpr std::uint32_t tfSponsorReserve = 0x00000002;
// AccountSet flags:
constexpr std::uint32_t tfRequireDestTag = 0x00010000;
constexpr std::uint32_t tfOptionalDestTag = 0x00020000;

View File

@@ -32,6 +32,7 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(Sponsor, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -362,6 +362,7 @@ UNTYPED_SFIELD(sfCredential, OBJECT, 33)
UNTYPED_SFIELD(sfRawTransaction, OBJECT, 34)
UNTYPED_SFIELD(sfBatchSigner, OBJECT, 35)
UNTYPED_SFIELD(sfBook, OBJECT, 36)
UNTYPED_SFIELD(sfSponsor, OBJECT, 37)
// array of objects (common)
// ARRAY/1 is reserved for end of array

View File

@@ -172,6 +172,15 @@ InnerObjectFormats::InnerObjectFormats()
{sfBookDirectory, soeREQUIRED},
{sfBookNode, soeREQUIRED},
});
add(sfSponsor.jsonName.c_str(),
sfSponsor.getCode(),
{
{sfAccount, soeREQUIRED},
{sfFlags, soeREQUIRED},
{sfSignature, soeOPTIONAL},
{sfSigners, soeOPTIONAL},
});
}
InnerObjectFormats const&

View File

@@ -689,6 +689,13 @@ STObject::getFieldArray(SField const& field) const
return getFieldByConstRef<STArray>(field, empty);
}
const STObject&
STObject::getFieldObject(SField const& field) const
{
static STObject const empty(field);
return getFieldByConstRef<STObject>(field, empty);
}
STCurrency const&
STObject::getFieldCurrency(SField const& field) const
{

View File

@@ -233,6 +233,25 @@ STTx::getSeqValue() const
return getSeqProxy().value();
}
AccountID
STTx::getFeePayer() const
{
if (isFieldPresent(sfSponsor))
{
if (getFieldObject(sfSponsor)[sfFlags] & tfSponsorFee)
{
return getFieldObject(sfSponsor)[sfAccount];
}
}
if (isFieldPresent(sfDelegate))
{
return getAccountID(sfDelegate);
}
return getAccountID(sfAccount);
}
void
STTx::sign(PublicKey const& publicKey, SecretKey const& secretKey)
{

View File

@@ -47,6 +47,7 @@ TxFormats::TxFormats()
{sfSigners, soeOPTIONAL}, // submit_multisigned
{sfNetworkID, soeOPTIONAL},
{sfDelegate, soeOPTIONAL},
{sfSponsor, soeOPTIONAL},
};
#pragma push_macro("UNWRAP")

View File

@@ -34,6 +34,7 @@
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/STAccount.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/UintTypes.h>
@@ -107,6 +108,11 @@ preflight1(PreflightContext const& ctx)
return temBAD_SIGNER;
}
if (ctx.tx.isFieldPresent(sfSponsor) && !ctx.rules.enabled(featureSponsor))
{
return temDISABLED;
}
auto const ret = preflight0(ctx);
if (!isTesSuccess(ret))
return ret;
@@ -152,6 +158,29 @@ preflight1(PreflightContext const& ctx)
!ctx.rules.enabled(featureBatch),
"Inner batch transaction must have a parent batch ID.");
// Sponsor checks
if (ctx.tx.isFieldPresent(sfSponsor))
{
auto const sponsor = ctx.tx.getFieldObject(sfSponsor);
if (sponsor[sfAccount] == ctx.tx[sfAccount])
{
JLOG(ctx.j.debug()) << "preflight1: invalid sponsor account";
return temMALFORMED;
}
if (!(sponsor[sfFlags] & tfSponsorFee) &&
!(sponsor[sfFlags] & tfSponsorReserve))
{
JLOG(ctx.j.debug()) << "preflight1: invalid sponsor flags";
return temMALFORMED;
}
if (!sponsor.isFieldPresent(sfSignature) &&
!sponsor.isFieldPresent(sfSigners))
{
JLOG(ctx.j.debug()) << "preflight1: no sfSignature or sfSigners";
return temMALFORMED;
}
}
return tesSUCCESS;
}
@@ -292,9 +321,8 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
if (feePaid == beast::zero)
return tesSUCCESS;
auto const id = ctx.tx.isFieldPresent(sfDelegate)
? ctx.tx.getAccountID(sfDelegate)
: ctx.tx.getAccountID(sfAccount);
auto const id = ctx.tx.getFeePayer();
JLOG(ctx.j.trace()) << "Fee payer: " + to_string(id);
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
return terNO_ACCOUNT;
@@ -338,13 +366,22 @@ Transactor::payFee()
}
else
{
auto const sle = view().peek(keylet::account(account_));
auto const id = ctx_.tx.getFeePayer();
auto const sle = view().peek(keylet::account(id));
JLOG(j_.trace()) << "Fee payer: " + to_string(id);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
if (id != account_) // sponsor
{
sle->setFieldAmount(
sfBalance, sle->getFieldAmount(sfBalance) - feePaid);
view().update(sle);
return tesSUCCESS;
}
// Deduct the fee, so it's not available during the transaction.
// Will only write the account back if the transaction succeeds.
mSourceBalance -= feePaid;
sle->setFieldAmount(sfBalance, mSourceBalance);
@@ -608,6 +645,11 @@ Transactor::checkSign(PreclaimContext const& ctx)
STArray const& txSigners(ctx.tx.getFieldArray(sfSigners));
return checkMultiSign(ctx.view, idAccount, txSigners, ctx.flags, ctx.j);
}
// if (ctx.tx.isFieldPresent(sfSponsor))
// {
// // TODO: check the sponsor signature
// }
// Check Single Sign
XRPL_ASSERT(