mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 17:27:00 +00:00
PoC for sponsored fees
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -106,6 +106,9 @@ public:
|
||||
std::uint32_t
|
||||
getSeqValue() const;
|
||||
|
||||
AccountID
|
||||
getFeePayer() const;
|
||||
|
||||
boost::container::flat_set<AccountID>
|
||||
getMentionedAccounts() const;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -47,6 +47,7 @@ TxFormats::TxFormats()
|
||||
{sfSigners, soeOPTIONAL}, // submit_multisigned
|
||||
{sfNetworkID, soeOPTIONAL},
|
||||
{sfDelegate, soeOPTIONAL},
|
||||
{sfSponsor, soeOPTIONAL},
|
||||
};
|
||||
|
||||
#pragma push_macro("UNWRAP")
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user