1#include <test/jtx/proposal.h>
3#include <test/jtx/Account.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/JTx.h>
6#include <test/jtx/batch.h>
7#include <test/jtx/multisign.h>
8#include <test/jtx/ticket.h>
9#include <test/jtx/utility.h>
11#include <xrpl/basics/base_uint.h>
12#include <xrpl/basics/chrono.h>
13#include <xrpl/basics/contract.h>
14#include <xrpl/json/json_value.h>
15#include <xrpl/ledger/ReadView.h>
16#include <xrpl/ledger/helpers/ProposalHelpers.h>
17#include <xrpl/protocol/AccountID.h>
18#include <xrpl/protocol/Indexes.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STLedgerEntry.h>
22#include <xrpl/protocol/STObject.h>
23#include <xrpl/protocol/SeqProxy.h>
24#include <xrpl/protocol/TER.h>
25#include <xrpl/protocol/TxFlags.h>
26#include <xrpl/protocol/jss.h>
43 jv[jss::TransactionType] = jss::TransactionProposalCreate;
44 jv[jss::Account] = proposer.
human();
45 jv[sfProposedTransaction.jsonName] = proposedTx;
55 tx[jss::SigningPubKey] =
"";
59 tx[jss::Sequence] = 0;
60 tx[sfTicketSequence.jsonName] = ticketSeq;
89 for (
auto const& inner : inners)
91 if (
auto const account = inner[jss::Account].asString(); account != target.
human())
92 participants.
insert(account);
103 json::Value& rawTransactions = jv[jss::RawTransactions];
104 for (
auto const& inner : inners)
105 rawTransactions[rawTransactions.
size()][jss::RawTransaction] = inner;
113 env(
signers(target, 1, {{proposer, 1}}));
125 return firstTicketSeq;
131 return (env.
now() + delta).time_since_epoch().count();
143 return entry(env, target.
id(), ticketSeq);
157 for (
auto sle = view.
read(
root); sle;)
159 for (
auto const& key : sle->getFieldV256(sfIndexes))
162 page = sle->getFieldU64(sfIndexNext);
176 if (!before || !
after)
177 return !before && !
after;
179 return before->key() ==
after->key() &&
180 static_cast<STObject const&
>(*before) ==
static_cast<STObject const&
>(*after);
188isCreationField(SField
const& field)
190 return field == sfLedgerEntryType || field == sfFlags || field == sfOwner ||
191 field == sfProposedTransaction || field == sfExpiration || field == sfOwnerNode ||
192 field == sfPreviousTxnID || field == sfPreviousTxnLgrSeq || field == sfSponsor;
202 if (jt.
jv[jss::TransactionType].
asString() != jss::TransactionProposalCreate.cStr())
207 auto const& proposedTx = jt.
jv[sfProposedTransaction.jsonName];
215 auto const target = *parsedTarget;
217 auto const ticketSeq = proposedTx[sfTicketSequence.jsonName].asUInt();
219 auto const cost = proposedTx[jss::TransactionType].asString() == jss::Batch.cStr()
230 auto const& view = *env.
current();
232 auto const ownerCountBefore = env.
ownerCount(proposer);
234 auto const sponsoringOwnerCountBefore =
236 auto const proposalBefore = view.
read(proposalKeylet);
239 auto const proposerDirBefore = ownerDirKeys(view, proposer.id());
240 auto const targetDirBefore = ownerDirKeys(view, target);
244 auto const& view = *applied.
current();
251 applied.
ownerCount(proposer) == ownerCountBefore + (created ? cost : 0),
255 sponsoredOwnerCountBefore + (created && reserveSponsor ? cost : 0),
256 "proposal sponsored owner count");
261 *sponsoringOwnerCountBefore + (created ? cost : 0),
262 "proposal sponsoring owner count");
270 "proposal target ticket");
274 if (target != proposer.id())
278 "proposal target account");
279 test.expect(ownerDirKeys(view, target) == targetDirBefore,
"proposal target directory");
282 auto const sleProposal = view.
read(proposalKeylet);
288 test.expect(unchanged(proposalBefore, sleProposal),
"proposal unchanged");
293 ownerDirKeys(view, proposer.id()) == proposerDirBefore,
"proposal owner directory");
300 if (!
test.expect(!proposalBefore,
"proposal is new") ||
301 !
test.expect(sleProposal,
"proposal entry"))
307 test.expect(sleProposal->getAccountID(sfOwner) == proposer.id(),
"proposal owner");
308 test.expect(sleProposal->getFieldU32(sfExpiration) ==
expiration,
"proposal expiration");
310 reserveSponsor ? sleProposal->isFieldPresent(sfSponsor) &&
311 sleProposal->getAccountID(sfSponsor) == reserveSponsor->id()
312 : !sleProposal->isFieldPresent(sfSponsor),
315 auto const& stored = sleProposal->getFieldObject(sfProposedTransaction);
316 test.expect(stored ==
parse(proposedTx),
"proposal payload");
323 "proposal payload unsigned");
325 stored.getAccountID(sfAccount) == target &&
326 stored.getFieldU32(sfTicketSequence) == ticketSeq &&
327 stored.getFieldU32(sfSequence) == 0,
328 "proposal payload target");
333 test.expect(sleProposal->getFieldU32(sfFlags) == 0,
"proposal flags");
334 for (
auto const& field : *sleProposal)
336 if (field.getSType() != STI_NOTPRESENT)
339 isCreationField(field.getFName()),
340 "proposal field " + field.getFName().getName());
346 auto expectedDir = proposerDirBefore;
347 expectedDir.emplace(sleProposal->key(), sleProposal->getFieldU64(sfOwnerNode));
348 test.expect(ownerDirKeys(view, proposer.id()) == expectedDir,
"proposal owner directory");
UInt size() const
Number of values in array or object.
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
std::chrono::duration< rep, period > duration
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
std::shared_ptr< STLedgerEntry const > const_pointer
static constexpr SeqProxy rawTicket(std::uint32_t v)
Factory function to return a ticket-based SeqProxy.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
AccountID id() const
Returns the Account ID.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
TER ter() const
Return the TER for the last JTx.
std::uint32_t sponsoringOwnerCount(Account const &account) const
Return the number of sponsoring objects owned by an account.
SLE::const_pointer le(Account const &account) const
Return an account root.
std::uint32_t ownerCount(Account const &account) const
Return the number of objects owned by an account.
Account const & lookup(AccountID const &id) const
Returns the Account given the AccountID.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
std::uint32_t sponsoredOwnerCount(Account const &account) const
Return the number of sponsored objects owned by an account.
beast::unit_test::Suite & test
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
NetClock::time_point now()
Returns the current network time.
Adds an inner Batch transaction to a JTx and autofills it.
json::Value const & getTxn() const
void operator()(Env &, JTx &) const
T emplace_back(T... args)
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet txProposal(AccountID const &target, std::uint32_t ticketSequence) noexcept
A TransactionProposal.
Keylet ticket(AccountID const &id, SeqProxy const &ticketSeq)
A ticket belonging to an account.
Keylet page(UInt256 const &root, std::uint64_t const index=0) noexcept
A page in a directory.
Keylet account(AccountID const &id) noexcept
AccountID root.
bool hasSignatureField(STObject const &proposedTx)
Whether the proposed transaction carries any signature field.
bool hasEmptySigningPubKey(STObject const &proposedTx)
Whether the proposed transaction's SigningPubKey is present and empty, as unsigned canonical form req...
json::Value outer(jtx::Account const &account, uint32_t seq, STAmount const &fee, std::uint32_t flags)
Build an outer Batch transaction JSON object.
XRPAmount calcBatchFee(jtx::Env const &env, uint32_t const &numSigners, uint32_t const &txns=0)
Calculate the expected outer Batch transaction fee.
Helpers for constructing TransactionProposal test transactions.
json::Value innerTx(json::Value tx, std::uint32_t seq)
Put a transaction into the form an inner transaction of a proposed Batch takes, as batch::Inner does ...
std::uint32_t createTicket(Env &env, Account const &account, std::uint32_t count)
Create tickets for a proposal to be built against, and close the ledger.
json::Value unsignedPayload(Env const &env, json::Value tx, std::uint32_t ticketSeq)
Put a transaction of any type into the form a proposal stores it in: unsigned and ticket-based,...
constexpr std::uint32_t kBatchProposalOwnerCount
Owner-reserve increments held by a proposal of a Batch transaction.
constexpr std::uint32_t kProposalOwnerCount
Owner-reserve increments held by a proposal of an ordinary transaction.
SLE::const_pointer entry(Env const &env, AccountID const &target, std::uint32_t ticketSeq)
The proposal stored against a target account's ticket.
json::Value create(Account const &proposer, json::Value const &proposedTx, std::uint32_t expiration)
Build a TransactionProposalCreate carrying an unsigned proposed transaction.
void authorizeProposer(Env &env, Account const &target, Account const &proposer)
Give proposer a place on target's SignerList, so it may create proposals against target.
json::Value unsignedBatch(Env const &env, Account const &target, std::uint32_t ticketSeq, std::uint32_t flags, std::vector< json::Value > const &inners, std::optional< std::uint32_t > numSigners)
An unsigned outer Batch payload holding inners.
std::uint32_t expiration(Env &env, NetClock::duration delta)
An absolute expiration delta past the environment's current time.
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
void fillFee(json::Value &jv, ReadView const &view)
Set the fee automatically.
STObject parse(json::Value const &jv)
Convert JSON to STObject.
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
Number root(Number f, unsigned d)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
constexpr FlagValue spfSponsorReserve
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Execution context for applying a JSON transaction.