mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-21 19:18:22 +00:00
Compare commits
65
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d5a406cc3 | ||
|
|
c0f4f855af | ||
|
|
f0a2ecddab | ||
|
|
ad73699c30 | ||
|
|
569a1aa674 | ||
|
|
45f63750bc | ||
|
|
00a547668b | ||
|
|
4b82a19493 | ||
|
|
ae6c4006d4 | ||
|
|
a0f04b4157 | ||
|
|
7c8044fa0c | ||
|
|
cb277e3512 | ||
|
|
bfc8b714a9 | ||
|
|
925f7a0127 | ||
|
|
8e082ffcf0 | ||
|
|
e592a33428 | ||
|
|
fa9efc6e17 | ||
|
|
bd8b57ba5f | ||
|
|
d260b4be6d | ||
|
|
df4835d4f0 | ||
|
|
522689aea6 | ||
|
|
795eb2fdfb | ||
|
|
e1ce7f40d8 | ||
|
|
cac9d753e1 | ||
|
|
9696e9021a | ||
|
|
8e82bc8de1 | ||
|
|
21c4a3b02d | ||
|
|
6a1cb5fc1a | ||
|
|
346316e9a3 | ||
|
|
41aeb2149a | ||
|
|
489d2d84fa | ||
|
|
32c8893ee8 | ||
|
|
20b1c3efc2 | ||
|
|
59975e1742 | ||
|
|
0dc1ae9783 | ||
|
|
a5610d8443 | ||
|
|
0536d4f0dd | ||
|
|
41bcd23edd | ||
|
|
4dbffc8201 | ||
|
|
d2ac3a0b58 | ||
|
|
830bc0d1f3 | ||
|
|
9214e7d3fd | ||
|
|
0c0a0e8794 | ||
|
|
86b3835d54 | ||
|
|
7990cfb8a0 | ||
|
|
c01a0539fe | ||
|
|
cdfe9cb65d | ||
|
|
4258eca938 | ||
|
|
01919230a5 | ||
|
|
2ba6f4cc68 | ||
|
|
c5ae59c4a0 | ||
|
|
1ac1d40d06 | ||
|
|
fb10515cff | ||
|
|
3c85a0180f | ||
|
|
096fa967da | ||
|
|
1ced7dc8f3 | ||
|
|
df1458a136 | ||
|
|
cdd5ebf4c1 | ||
|
|
1a9a11fc31 | ||
|
|
a9430d4778 | ||
|
|
fed5093488 | ||
|
|
6c74fc1a7d | ||
|
|
939f8b1a49 | ||
|
|
1f8c462c42 | ||
|
|
47df026dcc |
No files matched your search
@@ -30,6 +30,10 @@ Version 3.5.0 is not yet released.
|
||||
|
||||
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive the same `transaction` message as the `transactions` stream for each validated transaction whose metadata affects a subscribed issuance. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
||||
- `ledger_entry`: Add full support for checks, NFT offers, payment channels, and signer lists. ([#6319](https://github.com/XRPLF/rippled/pull/6319))
|
||||
- `channel_authorize`, `channel_verify`: `amount` now also accepts the transaction `Amount` JSON object for a token payment channel (`currency`, `issuer` and `value` for a trust line token; `mpt_issuance_id` and `value` for an MPT), and the claim authorization is serialized for that token. A string of drops is still used for an XRP channel. An object that names XRP, is negative or is malformed returns `channelAmtMalformed`. On the command line, the amount argument may be that JSON object as a single argument. For a token channel the signed message is the `PaymentChannelClaim` hash prefix, the channel ID and the amount serialized as an `Amount` field value without its field header; the XRP message is unchanged. ([#7935](https://github.com/XRPLF/rippled/pull/7935))
|
||||
- `PaymentChannelCreate`, `PaymentChannelFund`, `PaymentChannelClaim`: `Amount` (and `Balance` on `PaymentChannelClaim`) now accept a trust line or MPT value in addition to XRP, gated by the `TokenPaychan` amendment. ([#7935](https://github.com/XRPLF/rippled/pull/7935))
|
||||
- `ledger_entry`, `account_objects`: The `PayChannel` ledger entry now includes optional `TransferRate` and `IssuerNode` fields, present on a channel funded with a trust line or MPT value. ([#7935](https://github.com/XRPLF/rippled/pull/7935))
|
||||
- `account_channels`: For a token payment channel, `amount` and `balance` are `Amount` JSON objects (`currency`, `issuer` and `value` for a trust line token; `mpt_issuance_id` and `value` for an MPT). An XRP channel still returns strings of drops. ([#7935](https://github.com/XRPLF/rippled/pull/7935))
|
||||
|
||||
### Bugfixes in 3.5.0
|
||||
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTAmount.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -53,153 +40,22 @@ escrowLockPreclaimHelper(
|
||||
beast::Journal j);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowLockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
STAmount const balance = (*sleRippleState)[sfBalance];
|
||||
|
||||
// If balance is positive, issuer must have higher address than account
|
||||
if (balance > beast::kZero && issuer < account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(view, issue, account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, issue, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(view, account, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(view, dest, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
STAmount const spendableAmount =
|
||||
accountHolds(view, account, issue.currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the amount is not addable to the balance, return tecPRECISION_LOSS
|
||||
if (!canAdd(spendableAmount, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
beast::Journal j);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowLockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
|
||||
if (!sleIssuance->isFlag(lsfMPTCanEscrow))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(view, mptIssue, dest, AuthType::WeakAuth); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(view, mptIssue, account, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
view,
|
||||
account,
|
||||
amount.get<MPTIssue>(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
beast::Journal j);
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
@@ -211,43 +67,22 @@ escrowLockApplyHelper(
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowLockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter =
|
||||
directSendNoFee(view, sender, issuer, amount, !amount.holds<MPTIssue>(), journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowLockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = lockEscrowMPT(view, sender, amount, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
beast::Journal journal);
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
@@ -258,62 +93,20 @@ escrowUnlockPreclaimHelper(
|
||||
bool checkFreeze = true);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowUnlockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has deep frozen the destination, return tecFROZEN
|
||||
if (checkFreeze &&
|
||||
isDeepFrozen(view, account, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
bool checkFreeze);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowUnlockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (checkFreeze && isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
bool checkFreeze);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -332,7 +125,7 @@ escrowUnlockApplyHelper(
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowUnlockApplyHelper<Issue>(
|
||||
ApplyViewContext ctx,
|
||||
Rate lockedRate,
|
||||
@@ -343,138 +136,10 @@ escrowUnlockApplyHelper<Issue>(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
if (senderIssuer)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (receiverIssuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx,
|
||||
sleDest,
|
||||
xrpBalance,
|
||||
*sponsorSle,
|
||||
{.ownerCountDelta = 1},
|
||||
journal,
|
||||
tecNO_LINE_INSUF_RESERVE);
|
||||
!isTesSuccess(ret))
|
||||
{
|
||||
JLOG(journal.trace()) << "Trust line does not exist. "
|
||||
"Insufficient reserve to create line.";
|
||||
return ret;
|
||||
}
|
||||
|
||||
Currency const currency = issue.currency;
|
||||
STAmount initialBalance(issue);
|
||||
initialBalance.get<Issue>().account = noAccount();
|
||||
|
||||
if (TER const ter = trustCreate(
|
||||
ctx.view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
!sleDest->isFlag(lsfDefaultRipple), //
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
*sponsorSle, // sponsor
|
||||
journal); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
ctx.view.update(sleDest);
|
||||
}
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && !receiverIssuer)
|
||||
return tecNO_LINE;
|
||||
|
||||
auto const xferRate = transferRate(ctx.view, amount);
|
||||
// update if issuer rate is less than locked rate
|
||||
if (xferRate < lockedRate)
|
||||
lockedRate = xferRate;
|
||||
|
||||
// Transfer Rate only applies when:
|
||||
// 1. Issuer is not involved in the transfer (senderIssuer or
|
||||
// receiverIssuer)
|
||||
// 2. The locked rate is different from the parity rate
|
||||
|
||||
// NOTE: Transfer fee in escrow works a bit differently from a normal
|
||||
// payment. In escrow, the fee is deducted from the locked/sending amount,
|
||||
// whereas in a normal payment, the transfer fee is taken on top of the
|
||||
// sending amount.
|
||||
auto finalAmt = amount;
|
||||
if ((!senderIssuer && !receiverIssuer) && lockedRate != kParityRate)
|
||||
{
|
||||
// compute transfer fee, if any
|
||||
auto const xferFee =
|
||||
amount.value() - divideRound(amount, lockedRate, amount.get<Issue>(), true);
|
||||
// compute balance to transfer
|
||||
finalAmt = amount.value() - xferFee;
|
||||
}
|
||||
|
||||
// validate the line limit if the account submitting txn is not the receiver
|
||||
// of the funds
|
||||
if (!createAsset)
|
||||
{
|
||||
auto const sleRippleState = ctx.view.peek(trustLineKey);
|
||||
if (!sleRippleState)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// if the issuer is the high, then we use the low limit
|
||||
// otherwise we use the high limit
|
||||
STAmount const lineLimit =
|
||||
sleRippleState->getFieldAmount(recvLow ? sfLowLimit : sfHighLimit);
|
||||
|
||||
STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
|
||||
// flip the sign of the line balance if the issuer is not high
|
||||
if (!recvLow)
|
||||
lineBalance.negate();
|
||||
|
||||
// add the final amount to the line balance
|
||||
lineBalance += finalAmt;
|
||||
|
||||
// if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
|
||||
if (lineLimit < lineBalance)
|
||||
return tecLIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
// if destination is not the issuer then transfer funds
|
||||
if (!receiverIssuer)
|
||||
{
|
||||
auto const ter = directSendNoFee(ctx.view, issuer, receiver, finalAmt, true, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
beast::Journal journal);
|
||||
|
||||
template <>
|
||||
inline TER
|
||||
TER
|
||||
escrowUnlockApplyHelper<MPTIssue>(
|
||||
ApplyViewContext ctx,
|
||||
Rate lockedRate,
|
||||
@@ -485,83 +150,7 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
auto const mptID = amount.get<MPTIssue>().getMptID();
|
||||
auto const issuanceKey = keylet::mptokenIssuance(mptID);
|
||||
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
|
||||
if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
|
||||
{
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// update owner count.
|
||||
increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal);
|
||||
}
|
||||
|
||||
if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
auto const xferRate = transferRate(ctx.view, amount);
|
||||
// update if issuer rate is less than locked rate
|
||||
if (xferRate < lockedRate)
|
||||
lockedRate = xferRate;
|
||||
|
||||
// Transfer Rate only applies when:
|
||||
// 1. Issuer is not involved in the transfer (senderIssuer or
|
||||
// receiverIssuer)
|
||||
// 2. The locked rate is different from the parity rate
|
||||
|
||||
// NOTE: Transfer fee in escrow works a bit differently from a normal
|
||||
// payment. In escrow, the fee is deducted from the locked/sending amount,
|
||||
// whereas in a normal payment, the transfer fee is taken on top of the
|
||||
// sending amount.
|
||||
auto finalAmt = amount;
|
||||
if ((!senderIssuer && !receiverIssuer) && lockedRate != kParityRate)
|
||||
{
|
||||
if (ctx.view.rules().enabled(fixCleanup3_4_0))
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
lockedRate >= kParityRate,
|
||||
"xrpl::escrowUnlockApplyHelper<MPTIssue> : lockedRate is at least parity");
|
||||
// MPTs are integral, so round the delivered amount down and
|
||||
// charge any fractional transfer fee to the escrowed amount.
|
||||
auto const delivered =
|
||||
mulRatio(amount.mpt(), kParityRate.value, lockedRate.value, false);
|
||||
finalAmt = STAmount(amount.asset(), delivered.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
// compute transfer fee, if any
|
||||
auto const xferFee =
|
||||
amount.value() - divideRound(amount, lockedRate, amount.asset(), true);
|
||||
// compute balance to transfer
|
||||
finalAmt = amount.value() - xferFee;
|
||||
}
|
||||
}
|
||||
return unlockEscrowMPT(
|
||||
ctx.view,
|
||||
sender,
|
||||
receiver,
|
||||
finalAmt,
|
||||
ctx.view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
|
||||
journal);
|
||||
}
|
||||
beast::Journal journal);
|
||||
|
||||
/**
|
||||
* Smart escrow kill switches, driven by the voted FeeSettings.
|
||||
|
||||
@@ -3,9 +3,16 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
@@ -13,17 +20,81 @@
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Close a payment channel and return its remaining funds to the channel owner.
|
||||
* Check that a payment channel transaction may carry a token amount.
|
||||
*
|
||||
* @param slep The SLE for the PayChannel object to close.
|
||||
* @param view The apply view in which ledger state modifications are made.
|
||||
* @param key The ledger key identifying the PayChannel entry.
|
||||
* @param j Journal used for fatal-level diagnostic messages.
|
||||
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
* Token channels rely on fixCleanup3_2_0 and fixTokenEscrowV1, so both must be
|
||||
* enabled alongside featureTokenPaychan.
|
||||
*
|
||||
* @param rules The current ledger rules used to check amendment status.
|
||||
* @return temBAD_AMOUNT if featureTokenPaychan is not enabled; temDISABLED if
|
||||
* fixCleanup3_2_0 or fixTokenEscrowV1 is not enabled; tesSUCCESS otherwise.
|
||||
*/
|
||||
NotTEC
|
||||
checkTokenPayChanRules(Rules const& rules);
|
||||
|
||||
/**
|
||||
* Validate the token amount of a PaymentChannelCreate or PaymentChannelFund
|
||||
* transaction during preflight.
|
||||
*
|
||||
* @param amount The channel or funding amount from the transaction.
|
||||
* @return tesSUCCESS if the amount is valid; temBAD_AMOUNT or temBAD_CURRENCY
|
||||
* otherwise.
|
||||
*/
|
||||
template <ValidIssueType T>
|
||||
NotTEC
|
||||
payChanAmountPreflightHelper(STAmount const& amount);
|
||||
|
||||
template <>
|
||||
NotTEC
|
||||
payChanAmountPreflightHelper<Issue>(STAmount const& amount);
|
||||
|
||||
template <>
|
||||
NotTEC
|
||||
payChanAmountPreflightHelper<MPTIssue>(STAmount const& amount);
|
||||
|
||||
/**
|
||||
* Check that locking @p amount debits the source's trust line by exactly
|
||||
* @p amount.
|
||||
*
|
||||
* @param view The ledger view holding the source's trust line.
|
||||
* @param account The channel source whose balance is debited.
|
||||
* @param amount The amount to lock in the channel.
|
||||
* @param j Journal passed to the balance lookup.
|
||||
* @return tecPRECISION_LOSS if @p amount is an IOU and the source's balance
|
||||
* less @p amount is not exactly representable; tesSUCCESS otherwise,
|
||||
* including for XRP and MPT amounts.
|
||||
*/
|
||||
TER
|
||||
closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal j);
|
||||
payChanLockPrecisionHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Close a payment channel and return its remaining funds to the channel owner.
|
||||
*
|
||||
* @param slep The SLE for the PayChannel object to close.
|
||||
* @param ctx The apply view context (view and transaction) in which ledger
|
||||
* state modifications are made.
|
||||
* @param key The ledger key identifying the PayChannel entry.
|
||||
* @param txAccount The account submitting the transaction that closes the
|
||||
* channel.
|
||||
* @param submitterPreFeeBalance The submitter's XRP balance before the
|
||||
* transaction fee; the source's balance when the source closes its own
|
||||
* channel and its trust line must be re-created for the refund.
|
||||
* @param j Journal used for fatal-level diagnostic messages.
|
||||
* @return tesSUCCESS on success; tefBAD_LEDGER if a directory removal
|
||||
* fails; tefINTERNAL if the source account SLE cannot be found.
|
||||
*/
|
||||
TER
|
||||
closeChannel(
|
||||
SLE::Ref slep,
|
||||
ApplyViewContext ctx,
|
||||
UInt256 const& key,
|
||||
AccountID const& txAccount,
|
||||
XRPAmount submitterPreFeeBalance,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Add two uint32_t values with saturation at UINT32_MAX.
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Serialize the PaymentChannelClaim authorization message:
|
||||
* HashPrefix::PaymentChannelClaim, the channel key and the drops as a
|
||||
* 64-bit integer.
|
||||
*/
|
||||
inline void
|
||||
serializePayChanAuthorization(Serializer& msg, UInt256 const& key, XRPAmount const& amt)
|
||||
{
|
||||
@@ -15,4 +21,23 @@ serializePayChanAuthorization(Serializer& msg, UInt256 const& key, XRPAmount con
|
||||
msg.add64(amt.drops());
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the PaymentChannelClaim authorization message for any asset: a
|
||||
* native amount uses the XRP layout; a token amount follows
|
||||
* HashPrefix::PaymentChannelClaim and the channel key with STAmount::add, the
|
||||
* Amount field value without its field header.
|
||||
*/
|
||||
inline void
|
||||
serializePayChanAuthorization(Serializer& msg, UInt256 const& key, STAmount const& amt)
|
||||
{
|
||||
if (amt.native())
|
||||
{
|
||||
serializePayChanAuthorization(msg, key, amt.xrp());
|
||||
return;
|
||||
}
|
||||
msg.add32(HashPrefix::PaymentChannelClaim);
|
||||
msg.addBitString(key);
|
||||
amt.add(msg);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -805,6 +805,39 @@ canAdd(STAmount const& amt1, STAmount const& amt2);
|
||||
bool
|
||||
canSubtract(STAmount const& amt1, STAmount const& amt2);
|
||||
|
||||
/**
|
||||
* Determine whether `amt1 + amt2` equals the mathematical sum.
|
||||
*
|
||||
* XRP and MPT sums are integer additions, so this is canAdd(). An IOU sum is
|
||||
* rounded to the mantissa width and is exact only if subtracting each
|
||||
* operand from it gives back the other.
|
||||
*
|
||||
* @param amt1 The first addend.
|
||||
* @param amt2 The second addend.
|
||||
* @return true if the sum is exact; false if it rounds, exceeds the largest
|
||||
* amount the asset can represent, or the amounts are not comparable.
|
||||
* Never throws.
|
||||
*/
|
||||
bool
|
||||
isExactSum(STAmount const& amt1, STAmount const& amt2);
|
||||
|
||||
/**
|
||||
* Determine whether `amt1 - amt2` equals the mathematical difference.
|
||||
*
|
||||
* XRP and MPT differences are integer subtractions, so this is canSubtract().
|
||||
* An IOU difference is rounded to the mantissa width and is exact only if
|
||||
* adding the subtrahend back gives the minuend and subtracting the difference
|
||||
* from the minuend gives back the subtrahend.
|
||||
*
|
||||
* @param amt1 The minuend.
|
||||
* @param amt2 The subtrahend.
|
||||
* @return true if the difference is exact; false if it rounds, underflows,
|
||||
* exceeds the largest amount the asset can represent, or the amounts are
|
||||
* not comparable. Never throws.
|
||||
*/
|
||||
bool
|
||||
isExactDifference(STAmount const& amt1, STAmount const& amt2);
|
||||
|
||||
/**
|
||||
* Get the scale of a Number for a given asset.
|
||||
*
|
||||
|
||||
@@ -23,6 +23,7 @@ XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultN
|
||||
XRPL_FEATURE(ConfidentialMPTKeyRotation, Supported::No, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchV1_2, Supported::Yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FIX (Cleanup3_4_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(TokenPaychan, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(Sponsor, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocolV1_1, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -376,6 +376,8 @@ LEDGER_ENTRY(ltPAYCHAN, 0x0078, PayChannel, payment_channel, ({
|
||||
{sfPreviousTxnID, SoeRequired},
|
||||
{sfPreviousTxnLgrSeq, SoeRequired},
|
||||
{sfDestinationNode, SoeOptional},
|
||||
{sfTransferRate, SoeOptional},
|
||||
{sfIssuerNode, SoeOptional},
|
||||
}))
|
||||
|
||||
/** The ledger object which tracks the AMM.
|
||||
|
||||
@@ -182,7 +182,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, ({.delegable = Delegation::Delegable}), ({
|
||||
{sfDestination, SoeRequired},
|
||||
{sfAmount, SoeRequired},
|
||||
{sfAmount, SoeRequired, SoeMptSupported},
|
||||
{sfSettleDelay, SoeRequired},
|
||||
{sfPublicKey, SoeRequired},
|
||||
{sfCancelAfter, SoeOptional},
|
||||
@@ -195,7 +195,7 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, ({.delegable = Delegatio
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, ({.delegable = Delegation::Delegable}), ({
|
||||
{sfChannel, SoeRequired},
|
||||
{sfAmount, SoeRequired},
|
||||
{sfAmount, SoeRequired, SoeMptSupported},
|
||||
{sfExpiration, SoeOptional},
|
||||
}))
|
||||
|
||||
@@ -205,8 +205,8 @@ TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, ({.delegable = Delegation::D
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, ({.delegable = Delegation::Delegable}), ({
|
||||
{sfChannel, SoeRequired},
|
||||
{sfAmount, SoeOptional},
|
||||
{sfBalance, SoeOptional},
|
||||
{sfAmount, SoeOptional, SoeMptSupported},
|
||||
{sfBalance, SoeOptional, SoeMptSupported},
|
||||
{sfSignature, SoeOptional},
|
||||
{sfPublicKey, SoeOptional},
|
||||
{sfCredentialIDs, SoeOptional},
|
||||
|
||||
@@ -287,6 +287,54 @@ public:
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfDestinationNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfTransferRate (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT32::type::value_type>
|
||||
getTransferRate() const
|
||||
{
|
||||
if (hasTransferRate())
|
||||
return this->sle_->at(sfTransferRate);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfTransferRate is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasTransferRate() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfTransferRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get sfIssuerNode (SoeOptional)
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
protocol_autogen::Optional<SF_UINT64::type::value_type>
|
||||
getIssuerNode() const
|
||||
{
|
||||
if (hasIssuerNode())
|
||||
return this->sle_->at(sfIssuerNode);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if sfIssuerNode is present.
|
||||
* @return True if the field is present, false otherwise.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
bool
|
||||
hasIssuerNode() const
|
||||
{
|
||||
return this->sle_->isFieldPresent(sfIssuerNode);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -508,6 +556,28 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfTransferRate (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
PayChannelBuilder&
|
||||
setTransferRate(std::decay_t<typename SF_UINT32::type::value_type> const& value)
|
||||
{
|
||||
object_[sfTransferRate] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sfIssuerNode (SoeOptional)
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
PayChannelBuilder&
|
||||
setIssuerNode(std::decay_t<typename SF_UINT64::type::value_type> const& value)
|
||||
{
|
||||
object_[sfIssuerNode] = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build and return the completed PayChannel wrapper.
|
||||
* @param index The ledger entry index.
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (SoeOptional)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -86,6 +87,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfBalance (SoeOptional)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value, or std::nullopt if not present.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -246,6 +248,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (SoeOptional)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
PaymentChannelClaimBuilder&
|
||||
@@ -257,6 +260,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfBalance (SoeOptional)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
PaymentChannelClaimBuilder&
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (SoeRequired)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -207,6 +208,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (SoeRequired)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
PaymentChannelCreateBuilder&
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Get sfAmount (SoeRequired)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return The field value.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
@@ -155,6 +156,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Set sfAmount (SoeRequired)
|
||||
* @note This field supports MPT (Multi-Purpose Token) amounts.
|
||||
* @return Reference to this builder for method chaining.
|
||||
*/
|
||||
PaymentChannelFundBuilder&
|
||||
|
||||
@@ -27,6 +27,8 @@ class ValidMPTIssuance
|
||||
|
||||
std::uint32_t mptokensCreated_ = 0;
|
||||
std::uint32_t mptokensDeleted_ = 0;
|
||||
// holder of the last MPToken created
|
||||
std::optional<AccountID> mptokenCreatedHolder_;
|
||||
// non-MPT transactions may attempt to create
|
||||
// MPToken by an issuer
|
||||
bool mptCreatedByIssuer_ = false;
|
||||
|
||||
@@ -23,6 +23,9 @@ public:
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -23,9 +23,15 @@ public:
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTAmount.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowLockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const& issue = amount.get<Issue>();
|
||||
auto const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the account does not have a trustline to the issuer, return tecNO_LINE
|
||||
auto const sleRippleState = view.read(keylet::trustLine(account, issuer, issue.currency));
|
||||
if (!sleRippleState)
|
||||
return tecNO_LINE;
|
||||
|
||||
STAmount const balance = (*sleRippleState)[sfBalance];
|
||||
|
||||
// If balance is positive, issuer must have higher address than account
|
||||
if (balance > beast::kZero && issuer < account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If balance is negative, issuer must have lower address than account
|
||||
if (balance < beast::kZero && issuer > account)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is authorized
|
||||
if (auto const ter = requireAuth(view, issue, account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, issue, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecFROZEN
|
||||
if (isFrozen(view, account, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
// If the issuer has frozen the destination, return tecFROZEN
|
||||
if (isFrozen(view, dest, issue))
|
||||
return tecFROZEN;
|
||||
|
||||
STAmount const spendableAmount =
|
||||
accountHolds(view, account, issue.currency, issuer, FreezeHandling::IgnoreFreeze, j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the amount is not addable to the balance, return tecPRECISION_LOSS
|
||||
if (!canAdd(spendableAmount, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowLockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
AccountID const& dest,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
AccountID const issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tecNO_PERMISSION
|
||||
if (issuer == account)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
|
||||
if (!sleIssuance->isFlag(lsfMPTCanEscrow))
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
// If the issuer is not the same as the issuer of the mpt, return
|
||||
// tecNO_PERMISSION
|
||||
if (sleIssuance->getAccountID(sfIssuer) != issuer)
|
||||
return tecNO_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
// If the account does not have the mpt, return tecOBJECT_NOT_FOUND
|
||||
if (!view.exists(keylet::mptoken(issuanceKey.key, account)))
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is
|
||||
// authorized
|
||||
if (auto const ter = requireAuth(view, mptIssue, dest, AuthType::WeakAuth); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the issuer has frozen the destination, return tecLOCKED
|
||||
if (isFrozen(view, dest, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
// If the mpt cannot be transferred, return tecNO_AUTH
|
||||
if (auto const ter = canTransfer(view, mptIssue, account, dest); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
STAmount const spendableAmount = accountHolds(
|
||||
view,
|
||||
account,
|
||||
amount.get<MPTIssue>(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
AuthHandling::IgnoreAuth,
|
||||
j);
|
||||
|
||||
// If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount <= beast::kZero)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
// If the spendable amount is less than the amount, return
|
||||
// tecINSUFFICIENT_FUNDS
|
||||
if (spendableAmount < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowLockApplyHelper<Issue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter =
|
||||
directSendNoFee(view, sender, issuer, amount, !amount.holds<MPTIssue>(), journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowLockApplyHelper<MPTIssue>(
|
||||
ApplyView& view,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
STAmount const& amount,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Defensive: Issuer cannot create an escrow
|
||||
if (issuer == sender)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ter = lockEscrowMPT(view, sender, amount, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowUnlockPreclaimHelper<Issue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the issuer has requireAuth set, check if the destination is authorized
|
||||
if (auto const ter = requireAuth(view, amount.get<Issue>(), account); !isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has deep frozen the destination, return tecFROZEN
|
||||
if (checkFreeze &&
|
||||
isDeepFrozen(view, account, amount.get<Issue>().currency, amount.getIssuer()))
|
||||
return tecFROZEN;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowUnlockPreclaimHelper<MPTIssue>(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
bool checkFreeze)
|
||||
{
|
||||
AccountID const& issuer = amount.getIssuer();
|
||||
// If the issuer is the same as the account, return tesSUCCESS
|
||||
if (issuer == account)
|
||||
return tesSUCCESS;
|
||||
|
||||
// If the mpt does not exist, return tecOBJECT_NOT_FOUND
|
||||
auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
|
||||
auto const sleIssuance = view.read(issuanceKey);
|
||||
if (!sleIssuance)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// If the issuer has requireAuth set, check if the account is
|
||||
// authorized
|
||||
auto const& mptIssue = amount.get<MPTIssue>();
|
||||
if (auto const ter = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
// If the issuer has frozen the account, return tecLOCKED
|
||||
if (checkFreeze && isFrozen(view, account, *sleIssuance))
|
||||
return tecLOCKED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowUnlockApplyHelper<Issue>(
|
||||
ApplyViewContext ctx,
|
||||
Rate lockedRate,
|
||||
SLE::Ref sleDest,
|
||||
XRPAmount xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
if (senderIssuer)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (receiverIssuer)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issue = amount.get<Issue>();
|
||||
Keylet const trustLineKey = keylet::trustLine(receiver, issue);
|
||||
bool const recvLow = issuer > receiver;
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx,
|
||||
sleDest,
|
||||
xrpBalance,
|
||||
*sponsorSle,
|
||||
{.ownerCountDelta = 1},
|
||||
journal,
|
||||
tecNO_LINE_INSUF_RESERVE);
|
||||
!isTesSuccess(ret))
|
||||
{
|
||||
JLOG(journal.trace()) << "Trust line does not exist. "
|
||||
"Insufficient reserve to create line.";
|
||||
return ret;
|
||||
}
|
||||
|
||||
Currency const currency = issue.currency;
|
||||
STAmount initialBalance(issue);
|
||||
initialBalance.get<Issue>().account = noAccount();
|
||||
|
||||
if (TER const ter = trustCreate(
|
||||
ctx.view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
!sleDest->isFlag(lsfDefaultRipple), //
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
*sponsorSle, // sponsor
|
||||
journal); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
ctx.view.update(sleDest);
|
||||
}
|
||||
|
||||
if (!ctx.view.exists(trustLineKey) && !receiverIssuer)
|
||||
return tecNO_LINE;
|
||||
|
||||
auto const xferRate = transferRate(ctx.view, amount);
|
||||
// update if issuer rate is less than locked rate
|
||||
if (xferRate < lockedRate)
|
||||
lockedRate = xferRate;
|
||||
|
||||
// Transfer Rate only applies when:
|
||||
// 1. Issuer is not involved in the transfer (senderIssuer or
|
||||
// receiverIssuer)
|
||||
// 2. The locked rate is different from the parity rate
|
||||
|
||||
// NOTE: Transfer fee in escrow works a bit differently from a normal
|
||||
// payment. In escrow, the fee is deducted from the locked/sending amount,
|
||||
// whereas in a normal payment, the transfer fee is taken on top of the
|
||||
// sending amount.
|
||||
auto finalAmt = amount;
|
||||
if ((!senderIssuer && !receiverIssuer) && lockedRate != kParityRate)
|
||||
{
|
||||
// compute transfer fee, if any
|
||||
auto const xferFee =
|
||||
amount.value() - divideRound(amount, lockedRate, amount.get<Issue>(), true);
|
||||
// compute balance to transfer
|
||||
finalAmt = amount.value() - xferFee;
|
||||
}
|
||||
|
||||
// validate the line limit if the account submitting txn is not the receiver
|
||||
// of the funds
|
||||
if (!createAsset)
|
||||
{
|
||||
auto const sleRippleState = ctx.view.peek(trustLineKey);
|
||||
if (!sleRippleState)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// if the issuer is the high, then we use the low limit
|
||||
// otherwise we use the high limit
|
||||
STAmount const lineLimit =
|
||||
sleRippleState->getFieldAmount(recvLow ? sfLowLimit : sfHighLimit);
|
||||
|
||||
STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
|
||||
// flip the sign of the line balance if the issuer is not high
|
||||
if (!recvLow)
|
||||
lineBalance.negate();
|
||||
|
||||
// add the final amount to the line balance
|
||||
lineBalance += finalAmt;
|
||||
|
||||
// if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
|
||||
if (lineLimit < lineBalance)
|
||||
return tecLIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
// if destination is not the issuer then transfer funds
|
||||
if (!receiverIssuer)
|
||||
{
|
||||
auto const ter = directSendNoFee(ctx.view, issuer, receiver, finalAmt, true, journal);
|
||||
if (!isTesSuccess(ter))
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
TER
|
||||
escrowUnlockApplyHelper<MPTIssue>(
|
||||
ApplyViewContext ctx,
|
||||
Rate lockedRate,
|
||||
SLE::Ref sleDest,
|
||||
XRPAmount xrpBalance,
|
||||
STAmount const& amount,
|
||||
AccountID const& issuer,
|
||||
AccountID const& sender,
|
||||
AccountID const& receiver,
|
||||
bool createAsset,
|
||||
beast::Journal journal)
|
||||
{
|
||||
bool const senderIssuer = issuer == sender;
|
||||
bool const receiverIssuer = issuer == receiver;
|
||||
|
||||
auto const mptID = amount.get<MPTIssue>().getMptID();
|
||||
auto const issuanceKey = keylet::mptokenIssuance(mptID);
|
||||
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
|
||||
if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
|
||||
{
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// update owner count.
|
||||
increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal);
|
||||
}
|
||||
|
||||
if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
auto const xferRate = transferRate(ctx.view, amount);
|
||||
// update if issuer rate is less than locked rate
|
||||
if (xferRate < lockedRate)
|
||||
lockedRate = xferRate;
|
||||
|
||||
// Transfer Rate only applies when:
|
||||
// 1. Issuer is not involved in the transfer (senderIssuer or
|
||||
// receiverIssuer)
|
||||
// 2. The locked rate is different from the parity rate
|
||||
|
||||
// NOTE: Transfer fee in escrow works a bit differently from a normal
|
||||
// payment. In escrow, the fee is deducted from the locked/sending amount,
|
||||
// whereas in a normal payment, the transfer fee is taken on top of the
|
||||
// sending amount.
|
||||
auto finalAmt = amount;
|
||||
if ((!senderIssuer && !receiverIssuer) && lockedRate != kParityRate)
|
||||
{
|
||||
if (ctx.view.rules().enabled(fixCleanup3_4_0))
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
lockedRate >= kParityRate,
|
||||
"xrpl::escrowUnlockApplyHelper<MPTIssue> : lockedRate is at least parity");
|
||||
// MPTs are integral, so round the delivered amount down and
|
||||
// charge any fractional transfer fee to the escrowed amount.
|
||||
auto const delivered =
|
||||
mulRatio(amount.mpt(), kParityRate.value, lockedRate.value, false);
|
||||
finalAmt = STAmount(amount.asset(), delivered.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
// compute transfer fee, if any
|
||||
auto const xferFee =
|
||||
amount.value() - divideRound(amount, lockedRate, amount.asset(), true);
|
||||
// compute balance to transfer
|
||||
finalAmt = amount.value() - xferFee;
|
||||
}
|
||||
}
|
||||
return unlockEscrowMPT(
|
||||
ctx.view,
|
||||
sender,
|
||||
receiver,
|
||||
finalAmt,
|
||||
ctx.view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
|
||||
journal);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -3,16 +3,26 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@@ -21,9 +31,76 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
TER
|
||||
closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal j)
|
||||
NotTEC
|
||||
checkTokenPayChanRules(Rules const& rules)
|
||||
{
|
||||
if (!rules.enabled(featureTokenPaychan))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (!rules.enabled(fixCleanup3_2_0) || !rules.enabled(fixTokenEscrowV1))
|
||||
return temDISABLED;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
NotTEC
|
||||
payChanAmountPreflightHelper<Issue>(STAmount const& amount)
|
||||
{
|
||||
if (amount.native() || amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (badCurrency() == amount.get<Issue>().currency)
|
||||
return temBAD_CURRENCY;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <>
|
||||
NotTEC
|
||||
payChanAmountPreflightHelper<MPTIssue>(STAmount const& amount)
|
||||
{
|
||||
if (amount.native() || amount <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
payChanLockPrecisionHelper(
|
||||
ReadView const& view,
|
||||
AccountID const& account,
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (!amount.holds<Issue>())
|
||||
return tesSUCCESS;
|
||||
|
||||
// The trust line debit rounds to the mantissa, so require an exact
|
||||
// difference rather than canAdd's relative tolerance.
|
||||
STAmount const spendable = accountHolds(
|
||||
view,
|
||||
account,
|
||||
amount.get<Issue>().currency,
|
||||
amount.getIssuer(),
|
||||
FreezeHandling::IgnoreFreeze,
|
||||
j);
|
||||
if (!isExactDifference(spendable, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
closeChannel(
|
||||
SLE::Ref slep,
|
||||
ApplyViewContext ctx,
|
||||
UInt256 const& key,
|
||||
AccountID const& txAccount,
|
||||
XRPAmount submitterPreFeeBalance,
|
||||
beast::Journal j)
|
||||
{
|
||||
ApplyView& view = ctx.view;
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
// Remove PayChan from owner directory
|
||||
{
|
||||
@@ -38,9 +115,9 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
|
||||
}
|
||||
|
||||
// Remove PayChan from recipient's owner directory, if present.
|
||||
AccountID const dst = (*slep)[sfDestination];
|
||||
if (auto const page = (*slep)[~sfDestinationNode])
|
||||
{
|
||||
auto const dst = (*slep)[sfDestination];
|
||||
if (!view.dirRemove(keylet::ownerDir(dst), *page, key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -50,15 +127,69 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer amount back to owner, decrement owner count
|
||||
auto const sle = view.peek(keylet::account(src));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Release the channel's reserve first: a token refund can re-create a
|
||||
// holding the owner deleted while the channel was open.
|
||||
decreaseOwnerCountForObject(view, sle, slep, 1, j);
|
||||
|
||||
XRPL_ASSERT(
|
||||
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
decreaseOwnerCountForObject(view, sle, slep, 1, j);
|
||||
XRPL_ASSERT(
|
||||
isExactDifference((*slep)[sfAmount], (*slep)[sfBalance]),
|
||||
"xrpl::closeChannel : exact refund");
|
||||
|
||||
auto const reqDelta = (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
auto const& issuer = reqDelta.getIssuer();
|
||||
|
||||
// Only update the balance if there is a positive delta.
|
||||
if (reqDelta > beast::kZero)
|
||||
{
|
||||
if (isXRP(reqDelta))
|
||||
{
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + reqDelta;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const ret = reqDelta.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockPreclaimHelper<T>(view, src, reqDelta, false);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
bool const createAsset = src == txAccount;
|
||||
if (auto const ret = reqDelta.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx,
|
||||
kParityRate,
|
||||
sle,
|
||||
submitterPreFeeBalance,
|
||||
reqDelta,
|
||||
issuer,
|
||||
src,
|
||||
src,
|
||||
createAsset,
|
||||
j);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove PayChan from issuer's owner directory, if present.
|
||||
if (auto const optPage = (*slep)[~sfIssuerNode])
|
||||
{
|
||||
if (!view.dirRemove(keylet::ownerDir(issuer), *optPage, key, true))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Could not remove paychan from issuer owner directory";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
view.update(sle);
|
||||
|
||||
// Remove PayChan from ledger
|
||||
|
||||
@@ -522,6 +522,64 @@ canAdd(STAmount const& a, STAmount const& b)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Integral types defer to canAdd(). For IOU amounts the rounding error of the
|
||||
* sum is a multiple of the finer operand's unit in the last place, so
|
||||
* subtracting the coarser operand back from the sum returns the finer one
|
||||
* only when that error is zero. Both differences are checked because either
|
||||
* operand may be the finer. IOUAmount throws std::overflow_error when a sum
|
||||
* or difference exceeds the largest IOU value; the exact result is then not
|
||||
* representable, so the sum is not exact.
|
||||
*/
|
||||
bool
|
||||
isExactSum(STAmount const& a, STAmount const& b)
|
||||
{
|
||||
if (!areComparable(a, b))
|
||||
return false;
|
||||
|
||||
if (a.integral())
|
||||
return canAdd(a, b);
|
||||
|
||||
try
|
||||
{
|
||||
STAmount const sum = a + b;
|
||||
return sum - a == b && sum - b == a;
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Integral types defer to canSubtract(). For IOU amounts each check misses
|
||||
* the case where one operand is dust against the other. With a dust
|
||||
* subtrahend, a - b rounds to a and adding b back rounds to a again, so only
|
||||
* a - diff != b shows the loss. With a dust minuend, a - b rounds to -b and
|
||||
* a - diff rounds to b, so only diff + b != a shows it. Both are checked.
|
||||
* IOUAmount throws std::overflow_error when a step rounds past the largest
|
||||
* IOU value; the difference is then not exact.
|
||||
*/
|
||||
bool
|
||||
isExactDifference(STAmount const& a, STAmount const& b)
|
||||
{
|
||||
if (!areComparable(a, b))
|
||||
return false;
|
||||
|
||||
if (a.integral())
|
||||
return canSubtract(a, b);
|
||||
|
||||
try
|
||||
{
|
||||
STAmount const diff = a - b;
|
||||
return diff + b == a && a - diff == b;
|
||||
}
|
||||
catch (std::overflow_error const&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines if it is safe to subtract one STAmount from another.
|
||||
*
|
||||
|
||||
@@ -150,7 +150,8 @@ XRPNotCreated::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef aft
|
||||
drops_ -= (*before)[sfBalance].xrp().drops();
|
||||
break;
|
||||
case ltPAYCHAN:
|
||||
drops_ -= ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops();
|
||||
if (isXRP((*before)[sfAmount]))
|
||||
drops_ -= ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops();
|
||||
break;
|
||||
case ltESCROW:
|
||||
if (isXRP((*before)[sfAmount]))
|
||||
@@ -183,7 +184,7 @@ XRPNotCreated::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef aft
|
||||
drops_ += (*after)[sfBalance].xrp().drops();
|
||||
break;
|
||||
case ltPAYCHAN:
|
||||
if (!isDelete)
|
||||
if (!isDelete && isXRP((*after)[sfAmount]))
|
||||
drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
|
||||
break;
|
||||
case ltESCROW:
|
||||
|
||||
@@ -125,6 +125,7 @@ ValidMPTIssuance::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef
|
||||
else if (!before)
|
||||
{
|
||||
mptokensCreated_++;
|
||||
mptokenCreatedHolder_ = after->at(sfAccount);
|
||||
MPTIssue const mptIssue{after->at(sfMPTokenIssuanceID)};
|
||||
if (mptIssue.getIssuer() == after->at(sfAccount))
|
||||
mptCreatedByIssuer_ = true;
|
||||
@@ -439,6 +440,13 @@ ValidMPTIssuance::finalize(
|
||||
return true;
|
||||
}
|
||||
|
||||
// A token paychan claim may auto-create the submitter's MPToken.
|
||||
if (rules.enabled(featureTokenPaychan) && txnType == ttPAYCHAN_CLAIM &&
|
||||
mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 0 && mptokensDeleted_ == 0 &&
|
||||
(mptokensCreated_ == 0 ||
|
||||
(mptokensCreated_ == 1 && mptokenCreatedHolder_ == tx[sfAccount])))
|
||||
return true;
|
||||
|
||||
if (hasPrivilege(tx, Privilege::MayDeleteMpt) &&
|
||||
((txnType == ttAMM_DELETE && mptokensDeleted_ <= 2) || mptokensDeleted_ == 1) &&
|
||||
mptokensCreated_ == 0 && mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 0)
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <variant>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
NotTEC
|
||||
@@ -45,11 +43,9 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, account, amount, false);
|
||||
},
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, account, amount, false);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
@@ -133,21 +129,19 @@ EscrowCancel::doApply()
|
||||
|
||||
auto const issuer = amount.getIssuer();
|
||||
bool const createAsset = account == accountID_;
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
kParityRate,
|
||||
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : escrow.mutableRawSle(),
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
account, // sender and receiver are the same
|
||||
account,
|
||||
createAsset,
|
||||
j_);
|
||||
},
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
kParityRate,
|
||||
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : escrow.mutableRawSle(),
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
account, // sender and receiver are the same
|
||||
account,
|
||||
createAsset,
|
||||
j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -153,9 +152,8 @@ EscrowCreate::preflight(PreflightContext const& ctx)
|
||||
if (!ctx.rules.enabled(featureTokenEscrow))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) { return escrowCreatePreflightHelper<T>(ctx); },
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit(
|
||||
[&]<typename T>(T const&) { return escrowCreatePreflightHelper<T>(ctx); });
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
@@ -256,11 +254,9 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (!ctx.view.rules().enabled(featureTokenEscrow))
|
||||
return temDISABLED; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowLockPreclaimHelper<T>(ctx.view, account, dest, amount, ctx.j);
|
||||
},
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowLockPreclaimHelper<T>(ctx.view, account, dest, amount, ctx.j);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
@@ -423,11 +419,9 @@ EscrowCreate::doApply()
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowLockApplyHelper<T>(ctx_.view(), issuer, accountID_, amount, j_);
|
||||
},
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowLockApplyHelper<T>(ctx_.view(), issuer, accountID_, amount, j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
{
|
||||
return ret; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -242,11 +241,9 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, dest, amount);
|
||||
},
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, dest, amount);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
@@ -431,21 +428,19 @@ EscrowFinish::doApply()
|
||||
: kParityRate;
|
||||
auto const issuer = amount.getIssuer();
|
||||
bool const createAsset = destID == accountID_;
|
||||
if (auto const ret = std::visit(
|
||||
[&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
lockedRate,
|
||||
sled,
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
account,
|
||||
destID,
|
||||
createAsset,
|
||||
j_);
|
||||
},
|
||||
amount.asset().value());
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
lockedRate,
|
||||
sled,
|
||||
preFeeBalance_,
|
||||
amount,
|
||||
issuer,
|
||||
account,
|
||||
destID,
|
||||
createAsset,
|
||||
j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -46,11 +50,29 @@ PaymentChannelClaim::preflight(PreflightContext const& ctx)
|
||||
return temMALFORMED;
|
||||
|
||||
auto const bal = ctx.tx[~sfBalance];
|
||||
if (bal && (!isXRP(*bal) || *bal <= beast::kZero))
|
||||
if (bal && *bal <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
auto const amt = ctx.tx[~sfAmount];
|
||||
if (amt && (!isXRP(*amt) || *amt <= beast::kZero))
|
||||
if (amt && *amt <= beast::kZero)
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if ((bal && !isXRP(*bal)) || (amt && !isXRP(*amt)))
|
||||
{
|
||||
if (auto const ret = checkTokenPayChanRules(ctx.rules); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
// PaymentChannelCreate and PaymentChannelFund gate an MPT-denominated
|
||||
// Amount on featureMPTokensV1 (checkExtraFeatures); apply the same
|
||||
// gate here for sfBalance and sfAmount.
|
||||
if (((bal && bal->holds<MPTIssue>()) || (amt && amt->holds<MPTIssue>())) &&
|
||||
!ctx.rules.enabled(featureMPTokensV1))
|
||||
return temDISABLED;
|
||||
}
|
||||
|
||||
// Both bal and amt must reference the same asset before comparing,
|
||||
// otherwise STAmount comparison throws.
|
||||
if (bal && amt && bal->asset() != amt->asset())
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (bal && amt && *bal > *amt)
|
||||
@@ -70,8 +92,8 @@ PaymentChannelClaim::preflight(PreflightContext const& ctx)
|
||||
// The signature isn't needed if txAccount == src, but if it's
|
||||
// present, check it
|
||||
|
||||
auto const reqBalance = bal->xrp();
|
||||
auto const authAmt = amt ? amt->xrp() : reqBalance;
|
||||
auto const reqBalance = *bal;
|
||||
auto const authAmt = amt ? *amt : reqBalance;
|
||||
|
||||
if (reqBalance > authAmt)
|
||||
return temBAD_AMOUNT;
|
||||
@@ -96,12 +118,42 @@ PaymentChannelClaim::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
PaymentChannelClaim::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (!ctx.view.rules().enabled(featureCredentials))
|
||||
return Transactor::preclaim(ctx);
|
||||
if (ctx.view.rules().enabled(featureCredentials))
|
||||
{
|
||||
if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
|
||||
if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
if (ctx.view.rules().enabled(featureTokenPaychan))
|
||||
{
|
||||
Keylet const k{ltPAYCHAN, ctx.tx[sfChannel]};
|
||||
auto const slep = ctx.view.read(k);
|
||||
if (!slep)
|
||||
return tecNO_TARGET;
|
||||
|
||||
AccountID const dest = (*slep)[sfDestination];
|
||||
auto const& chanFunds = slep->getFieldAmount(sfAmount);
|
||||
|
||||
// The requested balance and the optional amount must each match
|
||||
// the channel's asset; otherwise the STAmount comparisons and
|
||||
// subtractions in doApply would throw on mismatched issues. amount
|
||||
// is checked independently of balance: a claim can carry Amount
|
||||
// without Balance (the signature-authorized ceiling alone).
|
||||
if (auto const bal = ctx.tx[~sfBalance]; bal && bal->asset() != chanFunds.asset())
|
||||
return tecWRONG_ASSET;
|
||||
if (auto const amt = ctx.tx[~sfAmount]; amt && amt->asset() != chanFunds.asset())
|
||||
return tecWRONG_ASSET;
|
||||
|
||||
if (!isXRP(chanFunds) && ctx.tx.isFieldPresent(sfBalance))
|
||||
{
|
||||
if (auto const ret = chanFunds.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockPreclaimHelper<T>(ctx.view, dest, chanFunds);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -116,25 +168,36 @@ PaymentChannelClaim::doApply()
|
||||
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
AccountID const dst = (*slep)[sfDestination];
|
||||
AccountID const txAccount = ctx_.tx[sfAccount];
|
||||
|
||||
auto const curExpiration = (*slep)[~sfExpiration];
|
||||
if (isChannelExpired(ctx_.view(), (*slep)[~sfCancelAfter]) ||
|
||||
isChannelExpired(ctx_.view(), curExpiration))
|
||||
{
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View"));
|
||||
return closeChannel(
|
||||
slep,
|
||||
ctx_.getApplyViewContext(),
|
||||
k.key,
|
||||
accountID_,
|
||||
preFeeBalance_,
|
||||
ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
if (txAccount != src && txAccount != dst)
|
||||
if (accountID_ != src && accountID_ != dst)
|
||||
return tecNO_PERMISSION;
|
||||
|
||||
if (ctx_.tx[~sfBalance])
|
||||
{
|
||||
auto const chanBalance = slep->getFieldAmount(sfBalance).xrp();
|
||||
auto const chanFunds = slep->getFieldAmount(sfAmount).xrp();
|
||||
auto const reqBalance = ctx_.tx[sfBalance].xrp();
|
||||
auto const chanBalance = slep->getFieldAmount(sfBalance);
|
||||
auto const chanFunds = slep->getFieldAmount(sfAmount);
|
||||
auto const reqBalance = ctx_.tx[sfBalance];
|
||||
|
||||
if (txAccount == dst && !ctx_.tx[~sfSignature])
|
||||
// Asset consistency between the requested balance/amount and the
|
||||
// channel was validated in preclaim.
|
||||
XRPL_ASSERT(
|
||||
reqBalance.asset() == chanFunds.asset(),
|
||||
"xrpl::PaymentChannelClaim::doApply : balance matches channel asset");
|
||||
|
||||
if (accountID_ == dst && !ctx_.tx[~sfSignature])
|
||||
{
|
||||
return ctx_.view().rules().enabled(fixCleanup3_2_0) ? TER{tecNO_PERMISSION}
|
||||
: TER{temBAD_SIGNATURE};
|
||||
@@ -159,27 +222,64 @@ PaymentChannelClaim::doApply()
|
||||
return tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
|
||||
// The payout reqBalance - sfBalance and the unclaimed remainder
|
||||
// sfAmount - reqBalance must both be exact so that the amounts
|
||||
// unlocked by this claim and by the close equal what sfBalance and
|
||||
// sfAmount record.
|
||||
if (!isExactDifference(chanFunds, reqBalance) ||
|
||||
!isExactDifference(reqBalance, chanBalance))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
auto const sled = ctx_.view().peek(keylet::account(dst));
|
||||
if (!sled)
|
||||
return tecNO_DST;
|
||||
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal);
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dst, sled, ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
|
||||
(*slep)[sfBalance] = ctx_.tx[sfBalance];
|
||||
XRPAmount const reqDelta = reqBalance - chanBalance;
|
||||
STAmount const reqDelta = reqBalance - chanBalance;
|
||||
XRPL_ASSERT(
|
||||
reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
|
||||
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
|
||||
|
||||
// Transfer amount to destination
|
||||
if (isXRP(reqDelta))
|
||||
{
|
||||
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
|
||||
}
|
||||
else
|
||||
{
|
||||
Rate lockedRate = slep->isFieldPresent(sfTransferRate)
|
||||
? xrpl::Rate(slep->getFieldU32(sfTransferRate))
|
||||
: kParityRate;
|
||||
auto const& issuer = reqDelta.getIssuer();
|
||||
bool const createAsset = dst == accountID_;
|
||||
if (auto const ret = reqDelta.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowUnlockApplyHelper<T>(
|
||||
ctx_.getApplyViewContext(),
|
||||
lockedRate,
|
||||
sled,
|
||||
preFeeBalance_,
|
||||
reqDelta,
|
||||
issuer,
|
||||
src,
|
||||
dst,
|
||||
createAsset,
|
||||
j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx_.view().update(sled);
|
||||
ctx_.view().update(slep);
|
||||
}
|
||||
|
||||
if (ctx_.tx.isFlag(tfRenew))
|
||||
{
|
||||
if (src != txAccount)
|
||||
if (src != accountID_)
|
||||
return tecNO_PERMISSION;
|
||||
(*slep)[~sfExpiration] = std::nullopt;
|
||||
ctx_.view().update(slep);
|
||||
@@ -188,8 +288,16 @@ PaymentChannelClaim::doApply()
|
||||
if (ctx_.tx.isFlag(tfClose))
|
||||
{
|
||||
// Channel will close immediately if dry or the receiver closes
|
||||
if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount])
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View"));
|
||||
if (dst == accountID_ || (*slep)[sfBalance] == (*slep)[sfAmount])
|
||||
{
|
||||
return closeChannel(
|
||||
slep,
|
||||
ctx_.getApplyViewContext(),
|
||||
k.key,
|
||||
accountID_,
|
||||
preFeeBalance_,
|
||||
ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
auto const settleExpiration = saturatingAdd(
|
||||
ctx_.view().rules(),
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
#include <xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -51,14 +58,37 @@ namespace xrpl {
|
||||
TxConsequences
|
||||
PaymentChannelCreate::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()};
|
||||
return TxConsequences{ctx.tx, isXRP(ctx.tx[sfAmount]) ? ctx.tx[sfAmount].xrp() : beast::kZero};
|
||||
}
|
||||
|
||||
bool
|
||||
PaymentChannelCreate::checkExtraFeatures(PreflightContext const& ctx)
|
||||
{
|
||||
// Only require featureMPTokensV1 when the channel amount is an MPT and
|
||||
// fixCleanup3_2_0 is active; XRP/IOU channels are unaffected by this gate.
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfAmount].holds<MPTIssue>())
|
||||
return ctx.rules.enabled(featureMPTokensV1);
|
||||
return true;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
PaymentChannelCreate::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
|
||||
auto const amount = ctx.tx[sfAmount];
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
if (auto const ret = checkTokenPayChanRules(ctx.rules); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = amount.asset().visit(
|
||||
[&]<typename T>(T const&) { return payChanAmountPreflightHelper<T>(amount); });
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
else if (amount <= beast::kZero)
|
||||
{
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
|
||||
if (ctx.tx[sfAccount] == ctx.tx[sfDestination])
|
||||
return temDST_IS_SRC;
|
||||
@@ -77,6 +107,8 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (!sle)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const amount = ctx.tx[sfAmount];
|
||||
|
||||
// Check reserve and funds availability
|
||||
if (!ctx.view.rules().enabled(featureSponsor))
|
||||
{
|
||||
@@ -86,15 +118,15 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
if (balance < reserve + ctx.tx[sfAmount])
|
||||
if (isXRP(amount) && balance < reserve + ctx.tx[sfAmount])
|
||||
return tecUNFUNDED;
|
||||
}
|
||||
|
||||
auto const dst = ctx.tx[sfDestination];
|
||||
auto const dest = ctx.tx[sfDestination];
|
||||
|
||||
{
|
||||
// Check destination account
|
||||
auto const sled = ctx.view.read(keylet::account(dst));
|
||||
auto const sled = ctx.view.read(keylet::account(dest));
|
||||
if (!sled)
|
||||
return tecNO_DST;
|
||||
|
||||
@@ -115,6 +147,24 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
// Vault shares (an MPT issued by a pseudo-account) cannot be locked
|
||||
// in a channel.
|
||||
if (amount.holds<MPTIssue>() && isPseudoAccount(ctx.view, amount.getIssuer()))
|
||||
return tecWRONG_ASSET;
|
||||
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowLockPreclaimHelper<T>(ctx.view, account, dest, amount, ctx.j);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = payChanLockPrecisionHelper(ctx.view, account, amount, ctx.j);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -122,6 +172,7 @@ TER
|
||||
PaymentChannelCreate::doApply()
|
||||
{
|
||||
auto const account = ctx_.tx[sfAccount];
|
||||
auto const amount = ctx_.tx[sfAmount];
|
||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -159,25 +210,27 @@ PaymentChannelCreate::doApply()
|
||||
// - unsponsored: 1 — source owes reserve including the new increment.
|
||||
auto const sourceReserve = accountReserve(
|
||||
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
|
||||
if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve)
|
||||
// Token channels lock no XRP, so only an XRP channel checks the
|
||||
// source's post-lock balance, as EscrowCreate does.
|
||||
if (isXRP(amount) && preFeeBalance_ - amount.xrp() < sourceReserve)
|
||||
return tecUNFUNDED;
|
||||
}
|
||||
|
||||
auto const dst = ctx_.tx[sfDestination];
|
||||
auto const dest = ctx_.tx[sfDestination];
|
||||
|
||||
// Create PayChan in ledger.
|
||||
//
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
// payChan sequence. For more explanation see comments in SeqProxy.h.
|
||||
Keylet const payChanKeylet = keylet::payChannel(account, dst, ctx_.tx.getSeqProxy());
|
||||
Keylet const payChanKeylet = keylet::payChannel(account, dest, ctx_.tx.getSeqProxy());
|
||||
auto const slep = std::make_shared<SLE>(payChanKeylet);
|
||||
|
||||
// Funds held in this channel
|
||||
(*slep)[sfAmount] = ctx_.tx[sfAmount];
|
||||
(*slep)[sfAmount] = amount;
|
||||
// Amount channel has already paid
|
||||
(*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed();
|
||||
(*slep)[sfBalance] = amount.zeroed();
|
||||
(*slep)[sfAccount] = account;
|
||||
(*slep)[sfDestination] = dst;
|
||||
(*slep)[sfDestination] = dest;
|
||||
(*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay];
|
||||
(*slep)[sfPublicKey] = ctx_.tx[sfPublicKey];
|
||||
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
||||
@@ -188,6 +241,13 @@ PaymentChannelCreate::doApply()
|
||||
(*slep)[sfSequence] = ctx_.tx.getSeqProxy().value();
|
||||
}
|
||||
|
||||
if (ctx_.view().rules().enabled(featureTokenPaychan) && !isXRP(amount))
|
||||
{
|
||||
auto const xferRate = transferRate(ctx_.view(), amount);
|
||||
if (xferRate != kParityRate)
|
||||
(*slep)[sfTransferRate] = xferRate.value;
|
||||
}
|
||||
|
||||
ctx_.view().insert(slep);
|
||||
|
||||
// Add PayChan to owner directory
|
||||
@@ -202,14 +262,37 @@ PaymentChannelCreate::doApply()
|
||||
// Add PayChan to the recipient's owner directory
|
||||
{
|
||||
auto const page =
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dst), payChanKeylet, describeOwnerDir(dst));
|
||||
ctx_.view().dirInsert(keylet::ownerDir(dest), payChanKeylet, describeOwnerDir(dest));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfDestinationNode] = *page;
|
||||
}
|
||||
|
||||
// Add PayChan to the issuer's owner directory, if applicable
|
||||
auto const& issuer = amount.getIssuer();
|
||||
if (!isXRP(amount) && issuer != accountID_ && issuer != dest && !amount.holds<MPTIssue>())
|
||||
{
|
||||
auto page = ctx_.view().dirInsert(
|
||||
keylet::ownerDir(issuer), payChanKeylet, describeOwnerDir(issuer));
|
||||
if (!page)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*slep)[sfIssuerNode] = *page;
|
||||
}
|
||||
|
||||
// Deduct owner's balance, increment owner count
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
if (isXRP(amount))
|
||||
{
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowLockApplyHelper<T>(ctx_.view(), issuer, accountID_, amount, j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
|
||||
ctx_.view().update(sle);
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Concepts.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -20,12 +24,24 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
TxConsequences
|
||||
PaymentChannelFund::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()};
|
||||
return TxConsequences{ctx.tx, isXRP(ctx.tx[sfAmount]) ? ctx.tx[sfAmount].xrp() : beast::kZero};
|
||||
}
|
||||
|
||||
bool
|
||||
PaymentChannelFund::checkExtraFeatures(PreflightContext const& ctx)
|
||||
{
|
||||
// Only require featureMPTokensV1 when the funding amount is an MPT and
|
||||
// fixCleanup3_2_0 is active; XRP/IOU channels are unaffected by this gate.
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfAmount].holds<MPTIssue>())
|
||||
return ctx.rules.enabled(featureMPTokensV1);
|
||||
return true;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
@@ -34,8 +50,40 @@ PaymentChannelFund::preflight(PreflightContext const& ctx)
|
||||
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfChannel] == beast::kZero)
|
||||
return temMALFORMED;
|
||||
|
||||
if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
|
||||
auto const amount = ctx.tx[sfAmount];
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
if (auto const ret = checkTokenPayChanRules(ctx.rules); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = amount.asset().visit(
|
||||
[&]<typename T>(T const&) { return payChanAmountPreflightHelper<T>(amount); });
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
else if (amount <= beast::kZero)
|
||||
{
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
PaymentChannelFund::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (ctx.view.rules().enabled(featureTokenPaychan))
|
||||
{
|
||||
Keylet const k{ltPAYCHAN, ctx.tx[sfChannel]};
|
||||
auto const slep = ctx.view.read(k);
|
||||
if (!slep)
|
||||
return tecNO_ENTRY;
|
||||
|
||||
// The funded asset must match the channel's asset; otherwise the
|
||||
// STAmount accumulation in doApply would throw on mismatched issues.
|
||||
if (ctx.tx[sfAmount].asset() != (*slep)[sfAmount].asset())
|
||||
return tecWRONG_ASSET;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -49,16 +97,22 @@ PaymentChannelFund::doApply()
|
||||
return tecNO_ENTRY;
|
||||
|
||||
AccountID const src = (*slep)[sfAccount];
|
||||
auto const txAccount = ctx_.tx[sfAccount];
|
||||
AccountID const dst = (*slep)[sfDestination];
|
||||
auto const curExpiration = (*slep)[~sfExpiration];
|
||||
|
||||
if (isChannelExpired(ctx_.view(), (*slep)[~sfCancelAfter]) ||
|
||||
isChannelExpired(ctx_.view(), curExpiration))
|
||||
{
|
||||
return closeChannel(slep, ctx_.view(), k.key, ctx_.registry.get().getJournal("View"));
|
||||
return closeChannel(
|
||||
slep,
|
||||
ctx_.getApplyViewContext(),
|
||||
k.key,
|
||||
accountID_,
|
||||
preFeeBalance_,
|
||||
ctx_.registry.get().getJournal("View"));
|
||||
}
|
||||
|
||||
if (src != txAccount)
|
||||
if (src != accountID_)
|
||||
{
|
||||
// only the owner can add funds or extend
|
||||
return tecNO_PERMISSION;
|
||||
@@ -82,10 +136,22 @@ PaymentChannelFund::doApply()
|
||||
ctx_.view().update(slep);
|
||||
}
|
||||
|
||||
auto const sle = ctx_.view().peek(keylet::account(txAccount));
|
||||
auto const sle = ctx_.view().peek(keylet::account(accountID_));
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const amount = ctx_.tx[sfAmount];
|
||||
auto const& chanAmt = slep->getFieldAmount(sfAmount);
|
||||
|
||||
// Asset consistency between the funded amount and the channel was
|
||||
// validated in preclaim.
|
||||
XRPL_ASSERT(
|
||||
amount.asset() == chanAmt.asset(),
|
||||
"xrpl::PaymentChannelFund::doApply : amount matches channel asset");
|
||||
|
||||
// A token fund locks no XRP and adds no owned object, so only an XRP fund
|
||||
// checks the source's XRP reserve.
|
||||
if (isXRP(amount))
|
||||
{
|
||||
// Check reserve and funds availability
|
||||
STAmount const balance = (*sle)[sfBalance];
|
||||
@@ -100,20 +166,59 @@ PaymentChannelFund::doApply()
|
||||
// when a sponsor is present and would ignore the source's post-lock
|
||||
// balance entirely. Funding an existing channel adds no owned object,
|
||||
// so there is no owner-count delta.
|
||||
if (balance < accountReserve(ctx_.view(), sle, j_) + ctx_.tx[sfAmount])
|
||||
if (balance < accountReserve(ctx_.view(), sle, j_) + amount)
|
||||
return tecUNFUNDED;
|
||||
}
|
||||
|
||||
// do not allow adding funds if dst does not exist
|
||||
if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst)))
|
||||
if (!ctx_.view().read(keylet::account(dst)))
|
||||
{
|
||||
return tecNO_DST;
|
||||
}
|
||||
|
||||
(*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount];
|
||||
ctx_.view().update(slep);
|
||||
if (!isXRP(amount))
|
||||
{
|
||||
// Funding is subject to the same issuer controls (locking opt-in,
|
||||
// authorization, freeze/lock, transferability, spendable balance)
|
||||
// as channel creation.
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowLockPreclaimHelper<T>(ctx_.view(), accountID_, dst, amount, j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
|
||||
if (auto const ret = payChanLockPrecisionHelper(ctx_.view(), accountID_, amount, j_);
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
// sfAmount must become exactly chanAmt + amount: an IOU sum that
|
||||
// rounds or an MPT sum that overflows would record an increase other
|
||||
// than what the source paid.
|
||||
if (!isExactSum(chanAmt, amount))
|
||||
return tecPRECISION_LOSS;
|
||||
|
||||
// The unclaimed remainder sfAmount - sfBalance must stay exact so
|
||||
// that close refunds exactly what was not paid out.
|
||||
if (!isExactDifference(chanAmt + amount, (*slep)[sfBalance]))
|
||||
return tecPRECISION_LOSS;
|
||||
}
|
||||
|
||||
if (isXRP(amount))
|
||||
{
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] - amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const& issuer = amount.getIssuer();
|
||||
if (auto const ret = amount.asset().visit([&]<typename T>(T const&) {
|
||||
return escrowLockApplyHelper<T>(ctx_.view(), issuer, accountID_, amount, j_);
|
||||
});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*slep)[sfAmount] = chanAmt + amount;
|
||||
ctx_.view().update(slep);
|
||||
ctx_.view().update(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -4362,7 +4362,7 @@ private:
|
||||
settleDelay,
|
||||
pk,
|
||||
cancelAfter),
|
||||
Ter(telENV_RPC_FAILED));
|
||||
Ter(tecNO_PERMISSION));
|
||||
|
||||
env(create(carol_, ammAlice.ammAccount(), XRP(1'000), settleDelay, pk, cancelAfter),
|
||||
Ter(tecNO_PERMISSION));
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -895,9 +894,8 @@ public:
|
||||
auto const jNoCred = credentials::ledgerEntry(env, becky, carol, credType);
|
||||
BEAST_EXPECT(
|
||||
jNoCred.isObject() && jNoCred.isMember(jss::result) &&
|
||||
jNoCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jNoCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jNoCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jNoCred[jss::result].isMember(jss::error) &&
|
||||
jNoCred[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
testcase("Credentials that aren't required");
|
||||
@@ -920,9 +918,8 @@ public:
|
||||
|
||||
BEAST_EXPECT(
|
||||
jNoCred.isObject() && jNoCred.isMember(jss::result) &&
|
||||
jNoCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jNoCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jNoCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jNoCred[jss::result].isMember(jss::error) &&
|
||||
jNoCred[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -960,9 +957,8 @@ public:
|
||||
|
||||
BEAST_EXPECT(
|
||||
jNoCred.isObject() && jNoCred.isMember(jss::result) &&
|
||||
jNoCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jNoCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jNoCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jNoCred[jss::result].isMember(jss::error) &&
|
||||
jNoCred[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
testcase("Expired credentials");
|
||||
@@ -998,9 +994,8 @@ public:
|
||||
auto jv = credentials::ledgerEntry(env, john, carol, credType);
|
||||
BEAST_EXPECT(
|
||||
jv.isObject() && jv.isMember(jss::result) &&
|
||||
jv[jss::result][jss::error] == "entryNotFound" &&
|
||||
jv[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jv[jss::result][jss::error_message] == "Entry not found.");
|
||||
jv[jss::result].isMember(jss::error) &&
|
||||
jv[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1084,9 +1079,8 @@ public:
|
||||
auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
|
||||
BEAST_EXPECT(
|
||||
jv.isObject() && jv.isMember(jss::result) &&
|
||||
jv[jss::result][jss::error] == "entryNotFound" &&
|
||||
jv[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jv[jss::result][jss::error_message] == "Entry not found.");
|
||||
jv[jss::result].isMember(jss::error) &&
|
||||
jv[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,9 +1121,8 @@ public:
|
||||
auto const jv = credentials::ledgerEntry(env, becky, carol, credType);
|
||||
BEAST_EXPECT(
|
||||
jv.isObject() && jv.isMember(jss::result) &&
|
||||
jv[jss::result][jss::error] == "entryNotFound" &&
|
||||
jv[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jv[jss::result][jss::error_message] == "Entry not found.");
|
||||
jv[jss::result].isMember(jss::error) &&
|
||||
jv[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Batch.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -2801,7 +2800,6 @@ class Batch_test : public beast::unit_test::Suite
|
||||
auto const jrr = env.rpc("submit", strHex(slice))[jss::result];
|
||||
expect(
|
||||
jrr[jss::status] == "error" && jrr[jss::error] == "invalidTransaction" &&
|
||||
!jrr.isMember(jss::error_code) && jrr[jss::error_message].isNull() &&
|
||||
jrr[jss::error_exception] ==
|
||||
"fails local checks: Batch inner transactions are never "
|
||||
"considered validly signed.",
|
||||
@@ -5409,10 +5407,7 @@ class Batch_test : public beast::unit_test::Suite
|
||||
|
||||
// The delegated payment never applied.
|
||||
BEAST_EXPECT(env.balance(mallory) == preMallory);
|
||||
auto const txResult = env.rpc("tx", txIDs[1])[jss::result];
|
||||
BEAST_EXPECT(txResult[jss::error] == "txnNotFound");
|
||||
BEAST_EXPECT(txResult[jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(txResult[jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(env.rpc("tx", txIDs[1])[jss::result][jss::error] == "txnNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5823,10 +5818,7 @@ class Batch_test : public beast::unit_test::Suite
|
||||
validateClosedLedger(env, testCases);
|
||||
|
||||
// verify jv2 is not present in the closed ledger.
|
||||
auto const txResult = env.rpc("tx", txIDs[1])[jss::result];
|
||||
BEAST_EXPECT(txResult[jss::error] == "txnNotFound");
|
||||
BEAST_EXPECT(txResult[jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(txResult[jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(env.rpc("tx", txIDs[1])[jss::result][jss::error] == "txnNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ConfidentialTransfer.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -260,9 +259,8 @@ class ConfidentialTransferExtended_test : public ConfidentialTransferTestBase
|
||||
|
||||
auto const credentialDeleted = [&](Env& env, Account const& subject) -> bool {
|
||||
auto const credentials = credentials::ledgerEntry(env, subject, dpIssuer, credType);
|
||||
return credentials[jss::result][jss::error] == "entryNotFound" &&
|
||||
credentials[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
credentials[jss::result][jss::error_message] == "Entry not found.";
|
||||
return credentials[jss::result].isMember(jss::error) &&
|
||||
credentials[jss::result][jss::error] == "entryNotFound";
|
||||
};
|
||||
|
||||
// TEST 4: Expired credential with matching depositPreauth entry.
|
||||
|
||||
@@ -7944,8 +7944,6 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
|
||||
// Signature verification fails — rejected before ZKP check
|
||||
auto const jr = env.rpc("submit", strHex(tampered.slice()));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jr[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error_message].isNull());
|
||||
}
|
||||
|
||||
// Variant B: Re-signed with inflated ciphertext.
|
||||
@@ -8053,8 +8051,6 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
|
||||
|
||||
auto const jr = env.rpc("submit", strHex(tampered.slice()));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jr[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error_message].isNull());
|
||||
}
|
||||
|
||||
// Variant B: Re-signed with all negated ciphertexts.
|
||||
@@ -8158,8 +8154,6 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
|
||||
|
||||
auto const jr = env.rpc("submit", strHex(tampered.slice()));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jr[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error_message].isNull());
|
||||
}
|
||||
|
||||
// Variant B: Re-signed with combined ciphertext.
|
||||
@@ -8284,8 +8278,6 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
|
||||
// Signature verification fails
|
||||
auto const jr = env.rpc("submit", strHex(tampered.slice()));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jr[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error_message].isNull());
|
||||
}
|
||||
|
||||
// Variant B: Re-signed C1 substitution.
|
||||
@@ -8376,8 +8368,6 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
|
||||
// signed — transaction rejected before ZKP verification.
|
||||
auto const jr = env.rpc("submit", strHex(tampered.slice()));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jr[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(jr[jss::result][jss::error_message].isNull());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -125,9 +124,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,9 +173,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, issuer, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,9 +222,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
// resurrection
|
||||
@@ -263,9 +259,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
// resurrection
|
||||
@@ -299,9 +294,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
// resurrection
|
||||
@@ -337,9 +331,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
// resurrection
|
||||
@@ -375,9 +368,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,9 +390,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,9 +410,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jle = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jle.isObject() && jle.isMember(jss::result) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound" &&
|
||||
jle[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
jle[jss::result].isMember(jss::error) &&
|
||||
jle[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,9 +744,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jDelCred = credentials::ledgerEntry(env, subject, issuer, credType2);
|
||||
BEAST_EXPECT(
|
||||
jDelCred.isObject() && jDelCred.isMember(jss::result) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jDelCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jDelCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jDelCred[jss::result].isMember(jss::error) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound");
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, subject) == 1);
|
||||
@@ -792,9 +781,8 @@ struct Credentials_test : public beast::unit_test::Suite
|
||||
auto const jDelCred = credentials::ledgerEntry(env, subject, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jDelCred.isObject() && jDelCred.isMember(jss::result) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jDelCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jDelCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jDelCred[jss::result].isMember(jss::error) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/Dir.h>
|
||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -160,8 +159,6 @@ class Delegate_test : public beast::unit_test::Suite
|
||||
env.close();
|
||||
auto const jle = delegate::entry(env, gw, alice);
|
||||
BEAST_EXPECT(jle[jss::result][jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jle[jss::result][jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jle[jss::result][jss::error_message] == "Entry not found.");
|
||||
|
||||
// alice can delegate permissions to gw as well
|
||||
env(delegate::set(alice, gw, permissions));
|
||||
@@ -170,8 +167,6 @@ class Delegate_test : public beast::unit_test::Suite
|
||||
auto const response = delegate::entry(env, gw, alice);
|
||||
// alice has not been granted any permissions by gw
|
||||
BEAST_EXPECT(response[jss::result][jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(response[jss::result][jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(response[jss::result][jss::error_message] == "Entry not found.");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2785,11 +2780,9 @@ class Delegate_test : public beast::unit_test::Suite
|
||||
jv[jss::secret] = bob.name();
|
||||
auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"A Signer may not be the transaction's Account (" + bob.human() + ").");
|
||||
jrr[jss::error_message].asString().contains(
|
||||
"A Signer may not be the transaction's Account"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
@@ -1144,9 +1143,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite
|
||||
ledgerEntryDepositPreauth(env, bob, {{.issuer = issuer, .credType = credType}});
|
||||
BEAST_EXPECT(
|
||||
jDP.isObject() && jDP.isMember(jss::result) &&
|
||||
jDP[jss::result][jss::error] == "entryNotFound" &&
|
||||
jDP[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jDP[jss::result][jss::error_message] == "Entry not found.");
|
||||
jDP[jss::result].isMember(jss::error) &&
|
||||
jDP[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1234,9 +1232,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite
|
||||
auto const jDelCred = credentials::ledgerEntry(env, alice, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jDelCred.isObject() && jDelCred.isMember(jss::result) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jDelCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jDelCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jDelCred[jss::result].isMember(jss::error) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1286,9 +1283,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite
|
||||
auto const jDelCred = credentials::ledgerEntry(env, gw, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jDelCred.isObject() && jDelCred.isMember(jss::result) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jDelCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jDelCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jDelCred[jss::result].isMember(jss::error) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound");
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, gw) == 0);
|
||||
@@ -1362,9 +1358,8 @@ struct DepositPreauth_test : public beast::unit_test::Suite
|
||||
auto const jDelCred = credentials::ledgerEntry(env, zelda, issuer, credType);
|
||||
BEAST_EXPECT(
|
||||
jDelCred.isObject() && jDelCred.isMember(jss::result) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound" &&
|
||||
jDelCred[jss::result][jss::error_code] == RpcEntryNotFound &&
|
||||
jDelCred[jss::result][jss::error_message] == "Entry not found.");
|
||||
jDelCred[jss::result].isMember(jss::error) &&
|
||||
jDelCred[jss::result][jss::error] == "entryNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2295,6 +2295,83 @@ struct EscrowToken_test : public beast::unit_test::Suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testIOUUnlockCreditRoundsAtBalancePrecision(FeatureBitset features)
|
||||
{
|
||||
testcase("IOU Unlock Credit Rounds At Balance Precision");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
auto const gw = Account{"gateway"};
|
||||
auto const usd = gw["USD"];
|
||||
STAmount const limit{usd.issue(), 1, 21};
|
||||
STAmount const large{usd.issue(), 1, 20};
|
||||
|
||||
// The unlock credits 1 to a balance of exactly 1e20; the sum keeps 16
|
||||
// significant digits, so the balance stays 1e20
|
||||
BEAST_EXPECT(large + usd(1) == large);
|
||||
|
||||
// EscrowCancel: the refund of 1 to alice is lost to rounding
|
||||
{
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10'000), alice, bob, gw);
|
||||
env(fset(gw, asfAllowTrustLineLocking));
|
||||
env.close();
|
||||
env.trust(limit, alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(1)));
|
||||
env.close();
|
||||
|
||||
// The nonzero limit keeps alice's line after she escrows all of it
|
||||
auto const seq = env.seq(alice);
|
||||
env(escrow::create(alice, bob, usd(1)),
|
||||
escrow::kFinishTime(env.now() + 1s),
|
||||
escrow::kCancelTime(env.now() + 2s));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(alice, usd) == usd(0));
|
||||
|
||||
env(pay(gw, alice, large));
|
||||
env.close();
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(alice, usd) == large);
|
||||
|
||||
env(escrow::cancel(alice, alice, seq), Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
|
||||
BEAST_EXPECT(env.balance(alice, usd) == large);
|
||||
}
|
||||
|
||||
// EscrowFinish: the payout of 1 to bob is lost to rounding
|
||||
{
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10'000), alice, bob, gw);
|
||||
env(fset(gw, asfAllowTrustLineLocking));
|
||||
env.close();
|
||||
env.trust(usd(1), alice);
|
||||
env.trust(limit, bob);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(1)));
|
||||
env(pay(gw, bob, large));
|
||||
env.close();
|
||||
|
||||
auto const seq = env.seq(alice);
|
||||
env(escrow::create(alice, bob, usd(1)), escrow::kFinishTime(env.now() + 1s));
|
||||
env.close();
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(bob, usd) == large);
|
||||
|
||||
env(escrow::finish(bob, alice, seq), Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), SeqProxy::rawSequence(seq))));
|
||||
BEAST_EXPECT(env.balance(alice, usd) == usd(0));
|
||||
BEAST_EXPECT(env.balance(bob, usd) == large);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testMPTEnablement(FeatureBitset features)
|
||||
{
|
||||
@@ -4300,6 +4377,7 @@ struct EscrowToken_test : public beast::unit_test::Suite
|
||||
testIOUFreeze(features);
|
||||
testIOUInsufficientFunds(features);
|
||||
testIOUPrecisionLoss(features);
|
||||
testIOUUnlockCreditRoundsAtBalancePrecision(features);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -1021,7 +1020,6 @@ class MPToken_test : public beast::unit_test::Suite
|
||||
payment[jss::build_path] = true;
|
||||
auto jrr = env.rpc("json", "submit", to_string(payment));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::result][jss::error_message] ==
|
||||
"Field 'build_path' not allowed in this context.");
|
||||
@@ -1583,8 +1581,6 @@ class MPToken_test : public beast::unit_test::Suite
|
||||
jv[jss::tx_json][jss::Amount][jss::value] = std::to_string(kMaxMpTokenAmount + 1);
|
||||
auto const jrr = env.rpc("json", "submit", to_string(jv));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_message] == "Invalid field 'tx_json.Amount'.");
|
||||
}
|
||||
|
||||
// Pay maximum amount with the transfer fee, SendMax, and
|
||||
@@ -1949,8 +1945,6 @@ class MPToken_test : public beast::unit_test::Suite
|
||||
jtx.stx->add(s);
|
||||
auto jrr = env.rpc("submit", strHex(s.slice()));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jrr[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_message].isNull());
|
||||
|
||||
// tx is unsigned
|
||||
json::Value jv1;
|
||||
@@ -1958,13 +1952,9 @@ class MPToken_test : public beast::unit_test::Suite
|
||||
jv1[jss::tx_json] = jv;
|
||||
jrr = env.rpc("json", "submit", to_string(jv1));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_message] == "Amount can not be MPT.");
|
||||
|
||||
jrr = env.rpc("json", "sign", to_string(jv1));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_message] == "Amount can not be MPT.");
|
||||
};
|
||||
auto setMPTFields = [&](SField const& field, json::Value& jv, bool withAmount = true) {
|
||||
jv[jss::Asset] = toJson(xrpIssue());
|
||||
@@ -2020,35 +2010,6 @@ class MPToken_test : public beast::unit_test::Suite
|
||||
};
|
||||
ammBid(sfBidMin);
|
||||
ammBid(sfBidMax);
|
||||
// PaymentChannelCreate
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelCreate;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::Destination] = carol.human();
|
||||
jv[jss::SettleDelay] = 1;
|
||||
jv[sfPublicKey.fieldName] = strHex(alice.pk().slice());
|
||||
jv[jss::Amount] = mpt.getJson(JsonOptions::Values::None);
|
||||
test(jv, jss::Amount.cStr());
|
||||
}
|
||||
// PaymentChannelFund
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelFund;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[sfChannel.fieldName] = to_string(UInt256{1});
|
||||
jv[jss::Amount] = mpt.getJson(JsonOptions::Values::None);
|
||||
test(jv, jss::Amount.cStr());
|
||||
}
|
||||
// PaymentChannelClaim
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelClaim;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[sfChannel.fieldName] = to_string(UInt256{1});
|
||||
jv[jss::Amount] = mpt.getJson(JsonOptions::Values::None);
|
||||
test(jv, jss::Amount.cStr());
|
||||
}
|
||||
// NFTokenCreateOffer
|
||||
{
|
||||
json::Value jv;
|
||||
@@ -3208,9 +3169,6 @@ class MPToken_test : public beast::unit_test::Suite
|
||||
jv1[jss::tx_json] = jv;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(jv1));
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::result][jss::error_message] == "Field 'tx_json.Amount' has invalid data.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
@@ -587,7 +586,6 @@ public:
|
||||
auto jrr = env.rpc("json", "sign_for", to_string(jvOne))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"When multi-signing 'tx_json.SigningPubKey' must be empty.");
|
||||
@@ -615,7 +613,6 @@ public:
|
||||
jrr = env.rpc("json", "submit_multisigned", to_string(jvSubmit))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid Fee field. Fees must be greater than zero.");
|
||||
}
|
||||
@@ -642,7 +639,6 @@ public:
|
||||
jrr = env.rpc("json", "submit_multisigned", to_string(jvSubmit))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "internal");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInternal);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Internal error.");
|
||||
}
|
||||
|
||||
@@ -655,8 +651,6 @@ public:
|
||||
auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "masterDisabled");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcMasterDisabled);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Master key is disabled.");
|
||||
env.close();
|
||||
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||
}
|
||||
@@ -696,16 +690,12 @@ public:
|
||||
auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcActMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcActMalformed);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'tx_json.Account'.");
|
||||
|
||||
Account const jimmy{"jimmy"};
|
||||
jv[jss::tx_json][jss::Account] = jimmy.human();
|
||||
jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcActNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcActNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Source account not found.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -716,7 +706,6 @@ public:
|
||||
auto jrr = env.rpc("json", "submit_multisigned", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "tx_json.Signers array may not be empty.");
|
||||
env.close();
|
||||
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||
@@ -1079,9 +1068,6 @@ public:
|
||||
STTx local = *tx.stx;
|
||||
local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Empty SigningPubKey.");
|
||||
@@ -1096,9 +1082,6 @@ public:
|
||||
local.setFieldVL(sfTxnSignature, badSig);
|
||||
// Signature should fail.
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Invalid signature.");
|
||||
@@ -1112,9 +1095,6 @@ public:
|
||||
local.setFieldU32(sfSequence, seq + 1);
|
||||
// Signature should fail.
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Invalid signature.");
|
||||
@@ -1125,9 +1105,6 @@ public:
|
||||
STTx local = *tx.stx;
|
||||
local[sfSigningPubKey] = alice.pk(); // Insert sfSigningPubKey
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Cannot both single- and multi-sign.");
|
||||
@@ -1139,9 +1116,6 @@ public:
|
||||
local.sign(alice.pk(), alice.sk());
|
||||
local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Cannot both single- and multi-sign.");
|
||||
@@ -1157,9 +1131,6 @@ public:
|
||||
signer.setFieldVL(sfTxnSignature, badSig);
|
||||
// Signature should fail.
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception].asString().contains(
|
||||
"Invalid signature on account r"));
|
||||
@@ -1170,9 +1141,6 @@ public:
|
||||
STTx local = *tx.stx;
|
||||
local.peekFieldArray(sfSigners).clear(); // Empty Signers array.
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Invalid Signers array size.");
|
||||
@@ -1218,9 +1186,6 @@ public:
|
||||
bogie_));
|
||||
STTx const local = *tx.stx;
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Invalid Signers array size.");
|
||||
@@ -1230,9 +1195,6 @@ public:
|
||||
JTx const tx = env.jt(noop(alice), Fee(2 * baseFee), Msig(alice));
|
||||
STTx const local = *tx.stx;
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Invalid multisigner.");
|
||||
@@ -1242,9 +1204,6 @@ public:
|
||||
JTx const tx = env.jt(noop(alice), Fee(2 * baseFee), Msig(bogie_, bogie_));
|
||||
STTx const local = *tx.stx;
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Duplicate Signers not allowed.");
|
||||
@@ -1258,9 +1217,6 @@ public:
|
||||
std::ranges::reverse(signers);
|
||||
// Signature should fail.
|
||||
auto const info = submitSTTx(local);
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
info[jss::result][jss::error_exception] ==
|
||||
"fails local checks: Unsorted Signers array.");
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -4654,22 +4653,18 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite
|
||||
if (expect(
|
||||
nftOffers.isMember(jss::result), "expected \"result\"", __FILE__, line))
|
||||
{
|
||||
json::Value const& result = nftOffers[jss::result];
|
||||
expect(
|
||||
result[jss::error].asString() == "objectNotFound",
|
||||
"expected \"objectNotFound\"",
|
||||
__FILE__,
|
||||
line);
|
||||
expect(
|
||||
result[jss::error_code] == RpcObjectNotFound,
|
||||
"expected RpcObjectNotFound",
|
||||
__FILE__,
|
||||
line);
|
||||
expect(
|
||||
result[jss::error_message] == "The requested object was not found.",
|
||||
"expected \"The requested object was not found.\"",
|
||||
__FILE__,
|
||||
line);
|
||||
if (expect(
|
||||
nftOffers[jss::result].isMember(jss::error),
|
||||
"expected \"error\"",
|
||||
__FILE__,
|
||||
line))
|
||||
{
|
||||
expect(
|
||||
nftOffers[jss::result][jss::error].asString() == "objectNotFound",
|
||||
"expected \"objectNotFound\"",
|
||||
__FILE__,
|
||||
line);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4865,15 +4860,11 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite
|
||||
// Sell-side marker passed to nft_buy_offers.
|
||||
json::Value const result = queryWithMarker("nft_buy_offers", sellOfferIndex);
|
||||
BEAST_EXPECT(result[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
{
|
||||
// Buy-side marker passed to nft_sell_offers.
|
||||
json::Value const result = queryWithMarker("nft_sell_offers", buyOfferIndex);
|
||||
BEAST_EXPECT(result[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
// A same-side marker is still accepted. With a single offer on each
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -2756,8 +2755,6 @@ public:
|
||||
|
||||
jro = ledgerEntryOffer(env, bob, bobOfferSeq);
|
||||
BEAST_EXPECT(jro[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jro[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jro[jss::error_message] == "Entry not found.");
|
||||
|
||||
env.require(Owners(alice, 1), Owners(bob, 1));
|
||||
}
|
||||
@@ -2791,8 +2788,6 @@ public:
|
||||
|
||||
auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
|
||||
BEAST_EXPECT(jro[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jro[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jro[jss::error_message] == "Entry not found.");
|
||||
};
|
||||
testHelper3TokensMix(test);
|
||||
}
|
||||
@@ -2852,8 +2847,6 @@ public:
|
||||
// Verify the offer was consumed
|
||||
jro = ledgerEntryOffer(env, bob, bobOfferSeq);
|
||||
BEAST_EXPECT(jro[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jro[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jro[jss::error_message] == "Entry not found.");
|
||||
|
||||
// verify balances look right after the partial payment
|
||||
// only 300 XRP should have been payed since that's all
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -1708,8 +1707,6 @@ public:
|
||||
|
||||
jro = ledgerEntryOffer(env, bob, bobOfferSeq);
|
||||
BEAST_EXPECT(jro[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jro[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jro[jss::error_message] == "Entry not found.");
|
||||
|
||||
env.require(Owners(alice, 1), Owners(bob, 1));
|
||||
}
|
||||
@@ -1741,8 +1738,6 @@ public:
|
||||
|
||||
auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
|
||||
BEAST_EXPECT(jro[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jro[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jro[jss::error_message] == "Entry not found.");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1803,8 +1798,6 @@ public:
|
||||
// Verify the offer was consumed
|
||||
jro = ledgerEntryOffer(env, bob, bobOfferSeq);
|
||||
BEAST_EXPECT(jro[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jro[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jro[jss::error_message] == "Entry not found.");
|
||||
|
||||
// verify balances look right after the partial payment
|
||||
// only 300 XRP should be have been payed since that's all
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/PathAsset.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -163,9 +162,7 @@ public:
|
||||
g.signal();
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
BEAST_EXPECT(result[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcSrcCurMalformed);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Source currency is malformed.");
|
||||
BEAST_EXPECT(result.isMember(jss::error));
|
||||
|
||||
// Test rpc::tuning::max_auto_src_cur source currencies.
|
||||
numSrc.clear();
|
||||
@@ -192,9 +189,7 @@ public:
|
||||
g.signal();
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
BEAST_EXPECT(result[jss::error] == "internal");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInternal);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Internal error.");
|
||||
BEAST_EXPECT(result.isMember(jss::error));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -319,9 +318,7 @@ public:
|
||||
g.signal();
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
BEAST_EXPECT(result[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcSrcCurMalformed);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Source currency is malformed.");
|
||||
BEAST_EXPECT(result.isMember(jss::error));
|
||||
|
||||
// Test rpc::tuning::max_auto_src_cur source currencies.
|
||||
for (auto i = 0; i < (rpc::tuning::kMaxAutoSrcCur - 1); ++i)
|
||||
@@ -344,9 +341,7 @@ public:
|
||||
g.signal();
|
||||
});
|
||||
BEAST_EXPECT(g.waitFor(5s));
|
||||
BEAST_EXPECT(result[jss::error] == "internal");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInternal);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Internal error.");
|
||||
BEAST_EXPECT(result.isMember(jss::error));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+126
-186
@@ -99,27 +99,31 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == XRP(1000));
|
||||
|
||||
{
|
||||
auto const preAlice = env.balance(alice);
|
||||
env(fund(alice, chan, XRP(1000)));
|
||||
env(paychan::fund(alice, chan, XRP(1000)));
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
|
||||
}
|
||||
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
BEAST_EXPECT(chanBal == XRP(0));
|
||||
BEAST_EXPECT(chanAmt == XRP(2000));
|
||||
|
||||
{
|
||||
// bad amounts (non-xrp, negative amounts)
|
||||
env(create(alice, bob, usda(1000), settleDelay, pk), Ter(temBAD_AMOUNT));
|
||||
env(fund(alice, chan, usda(1000)), Ter(temBAD_AMOUNT));
|
||||
// A token amount needs TokenPaychan (temBAD_AMOUNT) and
|
||||
// fixCleanup3_2_0, which this suite disables (temDISABLED).
|
||||
auto const tokenResult =
|
||||
features[featureTokenPaychan] ? Ter(temDISABLED) : Ter(temBAD_AMOUNT);
|
||||
env(create(alice, bob, usda(1000), settleDelay, pk), tokenResult);
|
||||
env(fund(alice, chan, usda(1000)), tokenResult);
|
||||
env(create(alice, bob, XRP(-1000), settleDelay, pk), Ter(temBAD_AMOUNT));
|
||||
env(fund(alice, chan, XRP(-1000)), Ter(temBAD_AMOUNT));
|
||||
}
|
||||
@@ -140,9 +144,13 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const iou = usda(100).value();
|
||||
auto const negXRP = XRP(-100).value();
|
||||
auto const posXRP = XRP(100).value();
|
||||
env(claim(alice, chan, iou, iou), Ter(temBAD_AMOUNT));
|
||||
env(claim(alice, chan, posXRP, iou), Ter(temBAD_AMOUNT));
|
||||
env(claim(alice, chan, iou, posXRP), Ter(temBAD_AMOUNT));
|
||||
// A token Balance or Amount needs TokenPaychan (temBAD_AMOUNT) and
|
||||
// fixCleanup3_2_0, which this suite disables (temDISABLED).
|
||||
auto const tokenResult =
|
||||
features[featureTokenPaychan] ? Ter(temDISABLED) : Ter(temBAD_AMOUNT);
|
||||
env(claim(alice, chan, iou, iou), tokenResult);
|
||||
env(claim(alice, chan, posXRP, iou), tokenResult);
|
||||
env(claim(alice, chan, iou, posXRP), tokenResult);
|
||||
env(claim(alice, chan, negXRP, negXRP), Ter(temBAD_AMOUNT));
|
||||
env(claim(alice, chan, posXRP, negXRP), Ter(temBAD_AMOUNT));
|
||||
env(claim(alice, chan, negXRP, posXRP), Ter(temBAD_AMOUNT));
|
||||
@@ -162,9 +170,9 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const reqBal = chanBal + delta;
|
||||
auto const authAmt = reqBal + XRP(100);
|
||||
assert(reqBal <= chanAmt);
|
||||
env(claim(alice, chan, reqBal, authAmt));
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
|
||||
env(paychan::claim(alice, chan, reqBal, authAmt));
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == reqBal);
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + delta);
|
||||
chanBal = reqBal;
|
||||
}
|
||||
@@ -324,14 +332,14 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
NetClock::time_point const cancelAfter =
|
||||
env.current()->header().parentCloseTime + 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
env.close(cancelAfter);
|
||||
{
|
||||
// dst cannot claim after cancelAfter
|
||||
auto const chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
auto const preAlice = env.balance(alice);
|
||||
auto const preBob = env.balance(bob);
|
||||
auto const delta = XRP(500);
|
||||
@@ -341,7 +349,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const sig = signClaimAuth(alice.pk(), alice.sk(), chan, authAmt);
|
||||
env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()));
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
BEAST_EXPECT(!channelExists(*env.current(), chan));
|
||||
BEAST_EXPECT(!paychan::channelExists(*env.current(), chan));
|
||||
BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
|
||||
BEAST_EXPECT(env.balance(alice) == preAlice + channelFunds);
|
||||
}
|
||||
@@ -355,9 +363,9 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
NetClock::time_point const cancelAfter =
|
||||
env.current()->header().parentCloseTime + 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
// third party close before cancelAfter
|
||||
env(claim(carol, chan), Txflags(tfClose), Ter(tecNO_PERMISSION));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
@@ -424,9 +432,9 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const minExpiration = closeTime + settleDelay;
|
||||
NetClock::time_point const cancelAfter = closeTime + 7200s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk, cancelAfter));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
BEAST_EXPECT(!channelExpiration(*env.current(), chan));
|
||||
// Owner closes, will close after settleDelay
|
||||
env(claim(alice, chan), Txflags(tfClose));
|
||||
@@ -455,7 +463,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration - 50s}),
|
||||
Ter(temBAD_EXPIRATION));
|
||||
BEAST_EXPECT(!channelExpiration(*env.current(), chan));
|
||||
env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration}));
|
||||
env(paychan::fund(alice, chan, XRP(1), NetClock::time_point{minExpiration}));
|
||||
env.close(minExpiration);
|
||||
// Try to extend the expiration after the expiration has already passed
|
||||
env(fund(alice, chan, XRP(1), NetClock::time_point{minExpiration + 1000s}));
|
||||
@@ -477,17 +485,17 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
NetClock::time_point const settleTimepoint =
|
||||
env.current()->header().parentCloseTime + settleDelay;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
// Owner closes, will close after settleDelay
|
||||
env(claim(alice, chan), Txflags(tfClose));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
env.close(settleTimepoint - (settleDelay / 2));
|
||||
{
|
||||
// receiver can still claim
|
||||
auto const chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
auto const preBob = env.balance(bob);
|
||||
auto const delta = XRP(500);
|
||||
auto const reqBal = chanBal + delta;
|
||||
@@ -503,8 +511,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.close(settleTimepoint);
|
||||
{
|
||||
// past settleTime, channel will close
|
||||
auto const chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto const chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
auto const preAlice = env.balance(alice);
|
||||
auto const preBob = env.balance(bob);
|
||||
auto const delta = XRP(500);
|
||||
@@ -533,17 +541,17 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
// Owner tries to close channel, but it will remain open (settle delay)
|
||||
env(claim(alice, chan), Txflags(tfClose));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
{
|
||||
// claim the entire amount
|
||||
auto const preBob = env.balance(bob);
|
||||
env(claim(alice, chan, channelFunds.value(), channelFunds.value()));
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == channelFunds);
|
||||
env(paychan::claim(alice, chan, channelFunds.value(), channelFunds.value()));
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == channelFunds);
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + channelFunds);
|
||||
}
|
||||
auto const preAlice = env.balance(alice);
|
||||
@@ -568,15 +576,15 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
// Owner tries to close channel, but it will remain open (settle delay)
|
||||
env(claim(alice, chan), Txflags(tfClose));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
{
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
auto const preBob = env.balance(bob);
|
||||
|
||||
auto const delta = XRP(500);
|
||||
@@ -591,8 +599,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
}
|
||||
{
|
||||
// Claim again
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
auto const preBob = env.balance(bob);
|
||||
|
||||
auto const delta = XRP(500);
|
||||
@@ -623,9 +631,9 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env(fset(bob, asfDisallowXRP));
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), 3600s, alice.pk()));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -634,13 +642,13 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// since it is just advisory.
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), 3600s, alice.pk()));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), 3600s, alice.pk()));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
|
||||
env(fset(bob, asfDisallowXRP));
|
||||
auto const reqBal = XRP(500).value();
|
||||
env(claim(alice, chan, reqBal, reqBal));
|
||||
env(paychan::claim(alice, chan, reqBal, reqBal));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,16 +700,16 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == XRP(1000));
|
||||
|
||||
// alice can add more funds to the channel even though bob has
|
||||
// asfDepositAuth set.
|
||||
env(fund(alice, chan, XRP(1000)));
|
||||
env(paychan::fund(alice, chan, XRP(1000)));
|
||||
env.close();
|
||||
|
||||
// alice claims. Fails because bob's lsfDepositAuth flag is set.
|
||||
@@ -729,7 +737,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// bob claims with signature. Succeeds even though bob's
|
||||
// lsfDepositAuth flag is set since bob submitted the
|
||||
// transaction.
|
||||
env(claim(bob, chan, delta, delta, Slice(sig), pk));
|
||||
env(paychan::claim(bob, chan, delta, delta, Slice(sig), pk));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + delta - baseFee);
|
||||
}
|
||||
@@ -760,7 +768,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env(deposit::auth(bob, alice));
|
||||
env.close();
|
||||
|
||||
env(claim(alice, chan, delta, delta, Slice(sig), pk));
|
||||
env(paychan::claim(alice, chan, delta, delta, Slice(sig), pk));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + delta - (3 * baseFee));
|
||||
@@ -782,7 +790,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.close();
|
||||
|
||||
// alice claims successfully.
|
||||
env(claim(alice, chan, delta, delta));
|
||||
env(paychan::claim(alice, chan, delta, delta));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + XRP(800) - (5 * baseFee));
|
||||
}
|
||||
@@ -810,12 +818,12 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
// alice add funds to the channel
|
||||
env(fund(alice, chan, XRP(1000)));
|
||||
env(paychan::fund(alice, chan, XRP(1000)));
|
||||
env.close();
|
||||
|
||||
std::string const credBadIdx =
|
||||
@@ -906,12 +914,12 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
// alice add funds to the channel
|
||||
env(fund(alice, chan, XRP(1000)));
|
||||
env(paychan::fund(alice, chan, XRP(1000)));
|
||||
env.close();
|
||||
|
||||
auto const delta = XRP(500).value();
|
||||
@@ -939,11 +947,11 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
env(fund(alice, chan, XRP(1000)));
|
||||
env(paychan::fund(alice, chan, XRP(1000)));
|
||||
env.close();
|
||||
std::string const credIdx =
|
||||
"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
|
||||
@@ -975,12 +983,12 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan1 = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan1));
|
||||
auto const chan2 = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(channelExists(*env.current(), chan2));
|
||||
auto const chan1 = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan1));
|
||||
auto const chan2 = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan2));
|
||||
BEAST_EXPECT(chan1 != chan2);
|
||||
}
|
||||
|
||||
@@ -999,8 +1007,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
auto const chan1Str = to_string(paychan::channel(alice, bob, env.seq(alice)));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
env.close();
|
||||
{
|
||||
// test account non-string
|
||||
@@ -1058,7 +1066,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const jrr = env.rpc("json", "account_channels", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Account malformed.");
|
||||
BEAST_EXPECT(!jrr.isMember(jss::ledger_index));
|
||||
BEAST_EXPECT(!jrr.isMember(jss::ledger_current_index));
|
||||
}
|
||||
@@ -1107,8 +1114,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(r[jss::result][jss::channels].size() == 0);
|
||||
BEAST_EXPECT(r[jss::result][jss::validated]);
|
||||
}
|
||||
auto const chan2Str = to_string(channel(alice, bob, env.seq(alice)));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
auto const chan2Str = to_string(paychan::channel(alice, bob, env.seq(alice)));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
env.close();
|
||||
{
|
||||
auto const r = env.rpc("account_channels", alice.human(), bob.human());
|
||||
@@ -1158,7 +1165,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const channelFunds = XRP(1);
|
||||
for (auto const& b : bobs)
|
||||
{
|
||||
env(create(alice, b, channelFunds, settleDelay, alice.pk()));
|
||||
env(paychan::create(alice, b, channelFunds, settleDelay, alice.pk()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,9 +1240,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
{
|
||||
// degenerate case
|
||||
auto const r = testLimit(env, alice, 0);
|
||||
BEAST_EXPECT(r[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(r[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(r[jss::error_message] == "Invalid field 'limit'.");
|
||||
BEAST_EXPECT(r.isMember(jss::error_message));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1284,8 +1289,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// channels where alice is the source, not the destination
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
env(create(alice, bob, channelFunds, settleDelay, alice.pk()));
|
||||
env(create(bob, alice, channelFunds, settleDelay, bob.pk()));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, alice.pk()));
|
||||
env(paychan::create(bob, alice, channelFunds, settleDelay, bob.pk()));
|
||||
|
||||
auto const r = [&] {
|
||||
json::Value jvc;
|
||||
@@ -1312,8 +1317,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
auto const chan1Str = to_string(paychan::channel(alice, bob, env.seq(alice)));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
env.close();
|
||||
|
||||
json::Value args{json::ValueType::Object};
|
||||
@@ -1329,12 +1334,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const rs =
|
||||
env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
auto const error = apiVersion < 2u ? "invalidParams" : "badKeyType";
|
||||
auto const errorCode = apiVersion < 2u ? RpcInvalidParams : RpcBadKeyType;
|
||||
auto const errorMessage =
|
||||
apiVersion < 2u ? "Invalid field 'key_type'." : "Bad key type.";
|
||||
BEAST_EXPECT(rs[jss::error] == error);
|
||||
BEAST_EXPECT(rs[jss::error_code] == errorCode);
|
||||
BEAST_EXPECT(rs[jss::error_message] == errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1352,8 +1352,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 3600s;
|
||||
auto const channelFunds = XRP(1000);
|
||||
auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
auto const chan1Str = to_string(paychan::channel(alice, bob, env.seq(alice)));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
env.close();
|
||||
std::string chan1PkStr;
|
||||
{
|
||||
@@ -1375,8 +1375,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(r[jss::result][jss::channels].size() == 0);
|
||||
BEAST_EXPECT(r[jss::result][jss::validated]);
|
||||
}
|
||||
auto const chan2Str = to_string(channel(alice, bob, env.seq(alice)));
|
||||
env(create(alice, bob, channelFunds, settleDelay, pk));
|
||||
auto const chan2Str = to_string(paychan::channel(alice, bob, env.seq(alice)));
|
||||
env(paychan::create(alice, bob, channelFunds, settleDelay, pk));
|
||||
env.close();
|
||||
{
|
||||
auto const r = env.rpc("account_channels", alice.human(), bob.human());
|
||||
@@ -1423,40 +1423,22 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pkAsHex = sliceToHex(pk.slice());
|
||||
auto rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000x", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 ", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x1000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, " ", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 1000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1,000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, " 1000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel amount is malformed.");
|
||||
}
|
||||
{
|
||||
// malformed channel
|
||||
@@ -1465,34 +1447,22 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
chan1StrBad.pop_back();
|
||||
auto rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel is malformed.");
|
||||
rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
|
||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel is malformed.");
|
||||
|
||||
chan1StrBad = chan1Str;
|
||||
chan1StrBad.push_back('0');
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel is malformed.");
|
||||
rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
|
||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel is malformed.");
|
||||
|
||||
chan1StrBad = chan1Str;
|
||||
chan1StrBad.back() = 'x';
|
||||
rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
|
||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel is malformed.");
|
||||
rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
|
||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Payment channel is malformed.");
|
||||
}
|
||||
{
|
||||
// channel_id is not a string.
|
||||
@@ -1505,8 +1475,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const ra =
|
||||
env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(ra[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(ra[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ra[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
{
|
||||
// channel_id is not a string.
|
||||
@@ -1519,8 +1487,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const rv =
|
||||
env.rpc("json", "channel_verify", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rv[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(rv[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(rv[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
{
|
||||
// give an ill formed base 58 public key
|
||||
@@ -1579,8 +1545,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// Try to authorize using an unknown key type, except an error:
|
||||
auto const rs1 = env.rpc("channel_authorize", "charlie", "nyx", chan, "1000");
|
||||
BEAST_EXPECT(rs1[jss::error] == "badKeyType");
|
||||
BEAST_EXPECT(rs1[jss::error_code] == RpcBadKeyType);
|
||||
BEAST_EXPECT(rs1[jss::error_message] == "Bad key type.");
|
||||
|
||||
// Try to authorize using secp256k1; the authorization _should_
|
||||
// succeed but the verification should fail:
|
||||
@@ -1606,16 +1570,10 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// send malformed amounts rpc requests
|
||||
auto rs = env.rpc("channel_authorize", "alice", chan1Str, "1000x");
|
||||
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rs = env.rpc("channel_authorize", "alice", chan1Str, "x1000");
|
||||
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Payment channel amount is malformed.");
|
||||
rs = env.rpc("channel_authorize", "alice", chan1Str, "x");
|
||||
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Payment channel amount is malformed.");
|
||||
{
|
||||
// Missing channel_id
|
||||
json::Value args{json::ValueType::Object};
|
||||
@@ -1624,8 +1582,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::passphrase] = "passphrase_can_be_anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Missing field 'channel_id'.");
|
||||
}
|
||||
{
|
||||
// Missing amount
|
||||
@@ -1635,8 +1591,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::passphrase] = "passphrase_can_be_anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Missing field 'amount'.");
|
||||
}
|
||||
{
|
||||
// Missing key_type and no secret.
|
||||
@@ -1646,8 +1600,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::passphrase] = "passphrase_can_be_anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Missing field 'secret'.");
|
||||
}
|
||||
{
|
||||
// Both passphrase and seed specified.
|
||||
@@ -1659,11 +1611,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::seed] = "seed can be anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
rs[jss::error_message] ==
|
||||
"Exactly one of the following must be specified: passphrase, secret, seed or "
|
||||
"seed_hex");
|
||||
}
|
||||
{
|
||||
// channel_id is not exact hex.
|
||||
@@ -1674,8 +1621,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::passphrase] = "passphrase_can_be_anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "channelMalformed");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcChannelMalformed);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Payment channel is malformed.");
|
||||
}
|
||||
{
|
||||
// amount is not a string
|
||||
@@ -1686,8 +1631,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::passphrase] = "passphrase_can_be_anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Payment channel amount is malformed.");
|
||||
}
|
||||
{
|
||||
// Amount is not a decimal string.
|
||||
@@ -1698,8 +1641,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
args[jss::passphrase] = "passphrase_can_be_anything";
|
||||
rs = env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
|
||||
BEAST_EXPECT(rs[jss::error_code] == RpcChannelAmtMalformed);
|
||||
BEAST_EXPECT(rs[jss::error_message] == "Payment channel amount is malformed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1903,8 +1844,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
auto jv = create(alice, bob, XRP(1000), settleDelay, pk);
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
auto jv = paychan::create(alice, bob, XRP(1000), settleDelay, pk);
|
||||
auto const pkHex = strHex(pk.slice());
|
||||
jv["PublicKey"] = pkHex.substr(2, pkHex.size() - 2);
|
||||
env(jv, Ter(temMALFORMED));
|
||||
@@ -1950,8 +1891,6 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
)*";
|
||||
auto const r = env.rpc("json", "channel_verify", txn);
|
||||
BEAST_EXPECT(r["result"]["error"] == "publicMalformed");
|
||||
BEAST_EXPECT(r["result"]["error_code"] == RpcPublicMalformed);
|
||||
BEAST_EXPECT(r["result"]["error_message"] == "Public key is malformed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1984,7 +1923,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// Test with adding the paychan to the recipient's owner directory
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
auto const [chan, chanSle] = channelKeyAndSle(*env.current(), alice, bob);
|
||||
BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
|
||||
@@ -2006,7 +1945,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
// create the channel before the amendment activates
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
auto const [chan, chanSle] = channelKeyAndSle(*env.current(), alice, bob);
|
||||
BEAST_EXPECT(inOwnerDir(*env.current(), alice, chanSle));
|
||||
@@ -2059,18 +1998,18 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
// Create a channel from alice to bob
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, env.seq(alice));
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, XRP(1000), settleDelay, pk));
|
||||
env.close();
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == XRP(1000));
|
||||
|
||||
rmAccount(env, alice, carol, tecHAS_OBLIGATIONS);
|
||||
rmAccount(env, bob, carol, TER(tecHAS_OBLIGATIONS));
|
||||
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto chanAmt = channelAmount(*env.current(), chan);
|
||||
auto chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
BEAST_EXPECT(chanBal == XRP(0));
|
||||
BEAST_EXPECT(chanAmt == XRP(1000));
|
||||
|
||||
@@ -2134,15 +2073,15 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
|
||||
auto const pk = alice.pk();
|
||||
auto const settleDelay = 100s;
|
||||
auto const chan = channel(alice, bob, aliceTicketSeq);
|
||||
auto const chan = paychan::channel(alice, bob, aliceTicketSeq);
|
||||
|
||||
env(create(alice, bob, XRP(1000), settleDelay, pk), ticket::Use(aliceTicketSeq++));
|
||||
|
||||
env.require(Tickets(alice, env.seq(alice) - aliceTicketSeq));
|
||||
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == XRP(1000));
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == XRP(0));
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == XRP(1000));
|
||||
|
||||
{
|
||||
auto const preAlice = env.balance(alice);
|
||||
@@ -2155,8 +2094,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(env.balance(alice) == preAlice - XRP(1000) - feeDrops);
|
||||
}
|
||||
|
||||
auto chanBal = channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = channelAmount(*env.current(), chan);
|
||||
auto chanBal = paychan::channelBalance(*env.current(), chan);
|
||||
auto const chanAmt = paychan::channelAmount(*env.current(), chan);
|
||||
BEAST_EXPECT(chanBal == XRP(0));
|
||||
BEAST_EXPECT(chanAmt == XRP(2000));
|
||||
|
||||
@@ -2172,8 +2111,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.require(Tickets(alice, env.seq(alice) - aliceTicketSeq));
|
||||
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == reqBal);
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + delta);
|
||||
chanBal = reqBal;
|
||||
}
|
||||
@@ -2191,8 +2130,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
|
||||
BEAST_EXPECT(env.seq(bob) == bobSeq);
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal);
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == reqBal);
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == chanAmt);
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
BEAST_EXPECT(env.balance(bob) == preBob + delta - feeDrops);
|
||||
chanBal = reqBal;
|
||||
@@ -2207,8 +2146,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
|
||||
BEAST_EXPECT(env.seq(bob) == bobSeq);
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == chanBal);
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(env.balance(bob) == preBob - feeDrops);
|
||||
}
|
||||
{
|
||||
@@ -2227,8 +2166,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
|
||||
BEAST_EXPECT(env.seq(bob) == bobSeq);
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == chanBal);
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(env.balance(bob) == preBob);
|
||||
}
|
||||
|
||||
@@ -2238,8 +2177,8 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
|
||||
BEAST_EXPECT(env.seq(bob) == bobSeq);
|
||||
|
||||
BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal);
|
||||
BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt);
|
||||
BEAST_EXPECT(paychan::channelBalance(*env.current(), chan) == chanBal);
|
||||
BEAST_EXPECT(paychan::channelAmount(*env.current(), chan) == chanAmt);
|
||||
|
||||
{
|
||||
// Dst closes channel
|
||||
@@ -2250,7 +2189,7 @@ struct PayChan_test : public beast::unit_test::Suite
|
||||
env.require(Tickets(bob, env.seq(bob) - bobTicketSeq));
|
||||
BEAST_EXPECT(env.seq(bob) == bobSeq);
|
||||
|
||||
BEAST_EXPECT(!channelExists(*env.current(), chan));
|
||||
BEAST_EXPECT(!paychan::channelExists(*env.current(), chan));
|
||||
auto const feeDrops = env.current()->fees().base;
|
||||
auto const delta = chanAmt - chanBal;
|
||||
assert(delta > beast::kZero);
|
||||
@@ -2301,6 +2240,7 @@ public:
|
||||
// pre-amendment behavior, so run it with the amendment disabled.
|
||||
FeatureBitset const all{testableAmendments() - fixCleanup3_2_0};
|
||||
testWithFeats(all);
|
||||
testWithFeats(all - featureTokenPaychan);
|
||||
testDepositAuthCreds();
|
||||
testMetaAndOwnership(all - fixIncludeKeyletFields);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -43,9 +42,8 @@ class ReducedOffer_test : public beast::unit_test::Suite
|
||||
{
|
||||
json::Value ledgerOffer = ledgerEntryOffer(env, acct, offerSeq);
|
||||
return !(
|
||||
ledgerOffer[jss::error].asString() == "entryNotFound" &&
|
||||
ledgerOffer[jss::error_code] == RpcEntryNotFound &&
|
||||
ledgerOffer[jss::error_message] == "Entry not found.");
|
||||
ledgerOffer.isMember(jss::error) &&
|
||||
ledgerOffer[jss::error].asString() == "entryNotFound");
|
||||
}
|
||||
|
||||
// Common code to clean up unneeded offers.
|
||||
|
||||
@@ -302,14 +302,10 @@ class SHAMapStore_test : public beast::unit_test::Suite
|
||||
}
|
||||
|
||||
static bool
|
||||
bad(json::Value const& json,
|
||||
std::string const& token,
|
||||
ErrorCodeI error,
|
||||
std::string const& message)
|
||||
bad(json::Value const& json, ErrorCodeI error = RpcLgrNotFound)
|
||||
{
|
||||
return json.isMember(jss::result) && json[jss::result][jss::error] == token &&
|
||||
json[jss::result][jss::error_code] == error &&
|
||||
json[jss::result][jss::error_message] == message;
|
||||
return json.isMember(jss::result) && rpc::containsError(json[jss::result]) &&
|
||||
json[jss::result][jss::error_code] == error;
|
||||
}
|
||||
|
||||
std::string
|
||||
@@ -887,7 +883,7 @@ public:
|
||||
std::map<std::uint32_t, json::Value const> ledgers;
|
||||
|
||||
auto ledgerTmp = env.rpc("ledger", "0");
|
||||
BEAST_EXPECT(bad(ledgerTmp, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound"));
|
||||
BEAST_EXPECT(bad(ledgerTmp));
|
||||
|
||||
ledgers.emplace(1, env.rpc("ledger", "1"));
|
||||
BEAST_EXPECT(goodLedger(env, ledgers[1], "1"));
|
||||
@@ -899,10 +895,10 @@ public:
|
||||
BEAST_EXPECT(goodLedger(env, ledgerTmp, "3"));
|
||||
|
||||
ledgerTmp = env.rpc("ledger", "4");
|
||||
BEAST_EXPECT(bad(ledgerTmp, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound"));
|
||||
BEAST_EXPECT(bad(ledgerTmp));
|
||||
|
||||
ledgerTmp = env.rpc("ledger", "100");
|
||||
BEAST_EXPECT(bad(ledgerTmp, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound"));
|
||||
BEAST_EXPECT(bad(ledgerTmp));
|
||||
|
||||
auto const firstSeq = waitForReady(env);
|
||||
auto lastRotated = firstSeq - 1;
|
||||
@@ -991,7 +987,7 @@ public:
|
||||
// Because advisory_delete is unset,
|
||||
// "can_delete" is disabled.
|
||||
auto const canDelete = env.rpc("can_delete");
|
||||
BEAST_EXPECT(bad(canDelete, "notEnabled", RpcNotEnabled, "Not enabled in configuration."));
|
||||
BEAST_EXPECT(bad(canDelete, RpcNotEnabled));
|
||||
|
||||
// Close ledgers without triggering a rotate
|
||||
for (; ledgerSeq < lastRotated + kDeleteInterval; ++ledgerSeq)
|
||||
|
||||
@@ -4353,6 +4353,42 @@ public:
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
}
|
||||
|
||||
// A sponsored token channel locks no XRP, so a source below its own
|
||||
// reserve before the fee may create one, as it may a token escrow.
|
||||
if (cosigning)
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
Account const gw("gw");
|
||||
auto const usd = gw["USD"];
|
||||
env.fund(XRP(1000000), alice, bob, sponsor, gw);
|
||||
env.close();
|
||||
env(fset(gw, asfAllowTrustLineLocking));
|
||||
env.trust(usd(10000), alice);
|
||||
env.close();
|
||||
env(pay(gw, alice, usd(1000)));
|
||||
env.close();
|
||||
|
||||
adjustAccountXRPBalance(env, alice, reserve(env, ownerCount(env, alice)));
|
||||
env(noop(alice));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(alice) < reserve(env, ownerCount(env, alice)));
|
||||
|
||||
env(escrow::create(alice, bob, usd(100)),
|
||||
escrow::kFinishTime(env.now() + 100s),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
auto const chan = paychan::channel(alice, bob, env.seq(alice));
|
||||
env(paychan::create(alice, bob, usd(100), 10s, alice.pk()),
|
||||
sponsor::As(sponsor, spfSponsorReserve),
|
||||
Sig(sfSponsorSignature, sponsor),
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
BEAST_EXPECT(paychan::channelExists(*env.current(), chan));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
@@ -1033,25 +1032,15 @@ struct TransactionProposalCancel_test : public beast::unit_test::Suite
|
||||
{
|
||||
json::Value noTicket;
|
||||
noTicket[jss::account] = target.human();
|
||||
auto const noTicketResult = ledgerEntry(noTicket);
|
||||
BEAST_EXPECT(noTicketResult[jss::error] == "malformedRequest");
|
||||
BEAST_EXPECT(noTicketResult[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(noTicketResult[jss::error_message] == "Missing field 'ticket_seq'.");
|
||||
BEAST_EXPECT(ledgerEntry(noTicket)[jss::error] == "malformedRequest");
|
||||
|
||||
json::Value noAccount;
|
||||
noAccount[jss::ticket_seq] = ticketSeq;
|
||||
auto const noAccountResult = ledgerEntry(noAccount);
|
||||
BEAST_EXPECT(noAccountResult[jss::error] == "malformedRequest");
|
||||
BEAST_EXPECT(noAccountResult[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(noAccountResult[jss::error_message] == "Missing field 'account'.");
|
||||
BEAST_EXPECT(ledgerEntry(noAccount)[jss::error] == "malformedRequest");
|
||||
|
||||
json::Value badAccount = byPair;
|
||||
badAccount[jss::account] = "not an account";
|
||||
auto const badAccountResult = ledgerEntry(badAccount);
|
||||
BEAST_EXPECT(badAccountResult[jss::error] == "malformedAddress");
|
||||
BEAST_EXPECT(badAccountResult[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
badAccountResult[jss::error_message] == "Invalid field 'account', not AccountID.");
|
||||
BEAST_EXPECT(ledgerEntry(badAccount)[jss::error] == "malformedAddress");
|
||||
}
|
||||
|
||||
// Listed for the proposer, who owns it, and not for the target.
|
||||
@@ -1068,8 +1057,6 @@ struct TransactionProposalCancel_test : public beast::unit_test::Suite
|
||||
{
|
||||
auto const jrr = ledgerEntry(byPair);
|
||||
BEAST_EXPECT(jrr[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Entry not found.");
|
||||
BEAST_EXPECT(!jrr.isMember(jss::node));
|
||||
BEAST_EXPECT(jrr[jss::index] == to_string(proposalID));
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -67,8 +66,6 @@ class TrustAndBalance_test : public beast::unit_test::Suite
|
||||
// reflects this
|
||||
auto jrr = ledgerEntryState(env, gw, alice, "USD");
|
||||
BEAST_EXPECT(jrr[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Entry not found.");
|
||||
|
||||
// now create a credit limit
|
||||
env(trust(alice, gw["USD"](800)));
|
||||
@@ -103,8 +100,6 @@ class TrustAndBalance_test : public beast::unit_test::Suite
|
||||
// ensure line is deleted
|
||||
jrr = ledgerEntryState(env, gw, alice, "USD");
|
||||
BEAST_EXPECT(jrr[jss::error] == "entryNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcEntryNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Entry not found.");
|
||||
|
||||
// TODO Check in both owner books.
|
||||
|
||||
|
||||
@@ -3231,9 +3231,7 @@ public:
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", to_string(prevLedgerWithQueue));
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(info.isMember(jss::result) && rpc::containsError(info[jss::result]));
|
||||
}
|
||||
|
||||
env.close();
|
||||
|
||||
@@ -970,6 +970,48 @@ class InvariantsMPT_test : public InvariantsBase
|
||||
});
|
||||
}
|
||||
|
||||
// A PaymentChannelClaim may create only the submitter's (A1) MPToken.
|
||||
{
|
||||
STTx const claim{ttPAYCHAN_CLAIM, [](STObject&) {}};
|
||||
MPTID id;
|
||||
Preclose const precloseIssuance = [&](Account const&, Account const&, Env& env) {
|
||||
Account const gw("gw");
|
||||
env.fund(XRP(1'000), gw);
|
||||
MPTTester const mpt({.env = env, .issuer = gw});
|
||||
id = mpt.issuanceID();
|
||||
return true;
|
||||
};
|
||||
auto const insertToken = [&](Account const& holder, ApplyContext& ac) {
|
||||
auto const sleNew = std::make_shared<SLE>(keylet::mptoken(id, holder));
|
||||
(*sleNew)[sfAccount] = holder.id();
|
||||
(*sleNew)[sfMPTokenIssuanceID] = id;
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
};
|
||||
|
||||
doInvariantCheck(
|
||||
{{"a MPToken was created"}},
|
||||
[&](Account const&, Account const& a2, ApplyContext& ac) {
|
||||
return insertToken(a2, ac);
|
||||
},
|
||||
XRPAmount{},
|
||||
claim,
|
||||
{tecINVARIANT_FAILED, tefINVARIANT_FAILED},
|
||||
precloseIssuance,
|
||||
TxAccount::A1);
|
||||
|
||||
doInvariantCheck(
|
||||
{},
|
||||
[&](Account const& a1, Account const&, ApplyContext& ac) {
|
||||
return insertToken(a1, ac);
|
||||
},
|
||||
XRPAmount{},
|
||||
claim,
|
||||
{tesSUCCESS, tesSUCCESS},
|
||||
precloseIssuance,
|
||||
TxAccount::A1);
|
||||
}
|
||||
|
||||
// LoanSet / VaultWithdraw MayAuthorizeMpt caps (fixCleanup3_4_0) and
|
||||
// the LoanBrokerDelete cap (fixCleanup3_5_0): LoanSet allows at most
|
||||
// two creates and no deletes; VaultWithdraw and LoanBrokerDelete allow
|
||||
|
||||
@@ -1154,8 +1154,6 @@ class LoanBroker_test : public beast::unit_test::Suite
|
||||
auto const jrr = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||
// fails in doSubmit() on STTx construction
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jrr.isMember(jss::error_code));
|
||||
BEAST_EXPECT(jrr[jss::error_message].isNull());
|
||||
BEAST_EXPECT(jrr[jss::error_exception] == "invalid native account");
|
||||
}
|
||||
|
||||
|
||||
@@ -190,8 +190,6 @@ private:
|
||||
BEAST_EXPECT(jr.isMember(jss::result));
|
||||
auto const jResult = jr[jss::result];
|
||||
BEAST_EXPECT(jResult[jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!jResult.isMember(jss::error_code));
|
||||
BEAST_EXPECT(jResult[jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
jResult[jss::error_exception] ==
|
||||
"fails local checks: Transaction has bad signature.");
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
@@ -127,9 +126,12 @@ private:
|
||||
return params;
|
||||
}();
|
||||
auto const jSignBorrower = env.rpc("json", "sign", to_string(borrowerSignParams));
|
||||
BEAST_EXPECT(jSignBorrower[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jSignBorrower[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jSignBorrower[jss::result][jss::error_message] == target);
|
||||
BEAST_EXPECT(
|
||||
jSignBorrower.isMember(jss::result) &&
|
||||
jSignBorrower[jss::result].isMember(jss::error) &&
|
||||
jSignBorrower[jss::result][jss::error] == "invalidParams" &&
|
||||
jSignBorrower[jss::result].isMember(jss::error_message) &&
|
||||
jSignBorrower[jss::result][jss::error_message] == target);
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
@@ -196,8 +196,7 @@ private:
|
||||
jvResult[jss::tx_blob] = strHex(local.getSerializer().slice());
|
||||
auto res = env.rpc("json", "submit", to_string(jvResult))["result"];
|
||||
BEAST_EXPECT(
|
||||
res[jss::error] == "invalidTransaction" && !res.isMember(jss::error_code) &&
|
||||
res[jss::error_message].isNull() &&
|
||||
res[jss::error] == "invalidTransaction" &&
|
||||
res[jss::error_exception] ==
|
||||
"fails local checks: Counterparty: Invalid signature.");
|
||||
});
|
||||
|
||||
@@ -172,7 +172,7 @@ private:
|
||||
jvParams[jss::ledger_index] = jss::validated;
|
||||
jvParams[jss::vault] = to_string(UInt256(42));
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(jvVault[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "entryNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -182,7 +182,7 @@ private:
|
||||
jvParams[jss::vault][jss::owner] = issuer.human();
|
||||
jvParams[jss::vault][jss::seq] = 1'000'000;
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(jvVault[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "entryNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -191,11 +191,7 @@ private:
|
||||
jvParams[jss::ledger_index] = jss::validated;
|
||||
jvParams[jss::vault] = 42;
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(
|
||||
jvVault[jss::result],
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'vault', not hex string or object.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -205,11 +201,7 @@ private:
|
||||
jvParams[jss::vault][jss::owner] = 42;
|
||||
jvParams[jss::vault][jss::seq] = sequence;
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(
|
||||
jvVault[jss::result],
|
||||
"malformedOwner",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'owner', not AccountID.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedOwner");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -219,11 +211,7 @@ private:
|
||||
jvParams[jss::vault][jss::owner] = issuer.human();
|
||||
jvParams[jss::vault][jss::seq] = "foo";
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(
|
||||
jvVault[jss::result],
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'seq', not number.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -233,11 +221,7 @@ private:
|
||||
jvParams[jss::vault][jss::owner] = issuer.human();
|
||||
jvParams[jss::vault][jss::seq] = -1;
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(
|
||||
jvVault[jss::result],
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'seq', not number.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -247,11 +231,7 @@ private:
|
||||
jvParams[jss::vault][jss::owner] = issuer.human();
|
||||
jvParams[jss::vault][jss::seq] = 1e20;
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(
|
||||
jvVault[jss::result],
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'seq', not number.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -261,11 +241,7 @@ private:
|
||||
jvParams[jss::vault][jss::owner] = issuer.human();
|
||||
jvParams[jss::vault][jss::seq] = true;
|
||||
auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams));
|
||||
checkError(
|
||||
jvVault[jss::result],
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'seq', not number.");
|
||||
BEAST_EXPECT(jvVault[jss::result][jss::error].asString() == "malformedRequest");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -524,8 +500,8 @@ private:
|
||||
|
||||
{
|
||||
testcase("RPC vault_info command line invalid index");
|
||||
json::Value const jv = env.rpc("vault_info", "foobar", "validated");
|
||||
checkError(jv, "invalidParams", RpcInvalidParams, "Invalid parameters.");
|
||||
json::Value jv = env.rpc("vault_info", "foobar", "validated");
|
||||
BEAST_EXPECT(jv[jss::error].asString() == "invalidParams");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -543,7 +519,7 @@ private:
|
||||
{
|
||||
testcase("RPC vault_info command line invalid ledger");
|
||||
json::Value jv = env.rpc("vault_info", strHex(keylet.key), "0");
|
||||
checkError(jv[jss::result], "lgrNotFound", RpcLgrNotFound, "ledgerNotFound");
|
||||
BEAST_EXPECT(jv[jss::result][jss::error].asString() == "lgrNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <test/jtx/paychan.h>
|
||||
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -12,11 +15,13 @@
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/SeqProxy.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -39,6 +44,7 @@ create(
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelCreate;
|
||||
jv[jss::Flags] = tfFullyCanonicalSig;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::Destination] = to_string(to);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
|
||||
@@ -60,6 +66,7 @@ fund(
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelFund;
|
||||
jv[jss::Flags] = tfFullyCanonicalSig;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfChannel.fieldName] = to_string(channel);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::Values::None);
|
||||
@@ -79,6 +86,7 @@ claim(
|
||||
{
|
||||
json::Value jv;
|
||||
jv[jss::TransactionType] = jss::PaymentChannelClaim;
|
||||
jv[jss::Flags] = tfFullyCanonicalSig;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv["Channel"] = to_string(channel);
|
||||
if (amount)
|
||||
@@ -133,8 +141,20 @@ signClaimAuth(
|
||||
STAmount const& authAmt)
|
||||
{
|
||||
Serializer msg;
|
||||
serializePayChanAuthorization(msg, channel, authAmt.xrp());
|
||||
serializePayChanAuthorization(msg, channel, authAmt);
|
||||
return sign(pk, sk, msg.slice());
|
||||
}
|
||||
|
||||
Rate
|
||||
rate(Env const& env, Account const& account, Account const& dest, std::uint32_t const& seq)
|
||||
{
|
||||
auto const sle =
|
||||
env.le(keylet::payChannel(account.id(), dest.id(), SeqProxy::rawSequence(seq)));
|
||||
if (!sle)
|
||||
return Rate{0};
|
||||
if (sle->isFieldPresent(sfTransferRate))
|
||||
return xrpl::Rate((*sle)[sfTransferRate]);
|
||||
return kParityRate;
|
||||
}
|
||||
|
||||
} // namespace xrpl::test::jtx::paychan
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
@@ -85,4 +87,7 @@ signClaimAuth(
|
||||
uint256 const& channel,
|
||||
STAmount const& authAmt);
|
||||
|
||||
Rate
|
||||
rate(Env const& env, Account const& account, Account const& dest, std::uint32_t const& seq);
|
||||
|
||||
} // namespace xrpl::test::jtx::paychan
|
||||
@@ -1001,6 +1001,222 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testIsExactSum()
|
||||
{
|
||||
testcase("is exact sum");
|
||||
|
||||
Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
|
||||
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
|
||||
// Exact IOU sum
|
||||
{
|
||||
STAmount const amt1(usd, 500);
|
||||
STAmount const amt2(usd, 1500);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// Exact IOU sum with a zero operand
|
||||
{
|
||||
STAmount const amt1(usd, 0);
|
||||
STAmount const amt2(usd, 15, -19);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// Exact IOU sum whose exponent exceeds both operands'
|
||||
{
|
||||
STAmount const amt1(usd, std::uint64_t{9999999999999999});
|
||||
STAmount const amt2(usd, 1);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// IOU sum that drops the finer operand: 1.5e-18 + 1 rounds to 1, and
|
||||
// 1 - 1.5e-18 rounds back to 1, so only the difference with the
|
||||
// coarser operand exposes the loss
|
||||
{
|
||||
STAmount const dust(usd, 15, -19);
|
||||
STAmount const one(usd, 1);
|
||||
BEAST_EXPECT(isExactSum(dust, one) == false);
|
||||
BEAST_EXPECT(isExactSum(one, dust) == false);
|
||||
}
|
||||
|
||||
// IOU sum that rounds the smaller operand away entirely
|
||||
{
|
||||
STAmount const amt1(usd, 1, 15);
|
||||
STAmount const amt2(usd, 1, -3);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// IOU sum that rounds to a different nonzero increase
|
||||
{
|
||||
STAmount const amt1(usd, std::uint64_t{1234567890123456});
|
||||
STAmount const amt2(usd, 600025, -2);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// IOU sum past the largest IOU value: the sum throws, so it is not
|
||||
// exact
|
||||
{
|
||||
STAmount const max(usd, STAmount::kMaxValue, STAmount::kMaxOffset);
|
||||
BEAST_EXPECT(max.mantissa() == STAmount::kMaxValue);
|
||||
BEAST_EXPECT(max.exponent() == STAmount::kMaxOffset);
|
||||
try
|
||||
{
|
||||
auto const _ = max + max;
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("value overflow"));
|
||||
}
|
||||
BEAST_EXPECT(isExactSum(max, max) == false);
|
||||
}
|
||||
|
||||
// Exact XRP sum
|
||||
{
|
||||
STAmount const amt1(XRPAmount(500));
|
||||
STAmount const amt2(XRPAmount(1500));
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// XRP overflow
|
||||
{
|
||||
STAmount const amt1(std::numeric_limits<XRPAmount::value_type>::max());
|
||||
STAmount const amt2(XRPAmount(1));
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// Exact MPT sum
|
||||
{
|
||||
STAmount const amt1(mpt, 500);
|
||||
STAmount const amt2(mpt, 1500);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// MPT overflow
|
||||
{
|
||||
STAmount const amt1(mpt, std::numeric_limits<MPTAmount::value_type>::max());
|
||||
STAmount const amt2(mpt, 1);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// Not comparable
|
||||
{
|
||||
STAmount const amt1(XRPAmount(1));
|
||||
STAmount const amt2(usd, 1);
|
||||
BEAST_EXPECT(isExactSum(amt1, amt2) == false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testIsExactDifference()
|
||||
{
|
||||
testcase("is exact difference");
|
||||
|
||||
Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
|
||||
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
|
||||
// Exact IOU difference
|
||||
{
|
||||
STAmount const amt1(usd, 1500);
|
||||
STAmount const amt2(usd, 500);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// Exact IOU difference with a zero subtrahend
|
||||
{
|
||||
STAmount const amt1(usd, 15, -19);
|
||||
STAmount const amt2(usd, 0);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// Exact IOU difference of equal operands
|
||||
{
|
||||
STAmount const amt1(usd, 4000);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt1) == true);
|
||||
}
|
||||
|
||||
// IOU difference that rounds to a different nonzero decrease
|
||||
{
|
||||
STAmount const amt1(usd, std::uint64_t{1234567890123456});
|
||||
STAmount const amt2(usd, 600025, -2);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// IOU difference that drops a dust subtrahend: 1 - 1.5e-18 rounds to
|
||||
// 1 and 1 + 1.5e-18 rounds back to 1, so only the minuend minus the
|
||||
// difference exposes the loss
|
||||
{
|
||||
STAmount const amt1(usd, 1);
|
||||
STAmount const amt2(usd, 15, -19);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// IOU difference that drops a dust minuend: 1.5e-18 - 1 rounds to -1
|
||||
// and 1.5e-18 - (-1) rounds back to 1, so only the difference plus
|
||||
// the subtrahend exposes the loss
|
||||
{
|
||||
STAmount const amt1(usd, 15, -19);
|
||||
STAmount const amt2(usd, 1);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// IOU difference whose check overflows: max - 1.5e80 rounds half to
|
||||
// even to 9999999999999998e80, and adding 1.5e80 back rounds past the
|
||||
// largest IOU value, so the difference is not exact
|
||||
{
|
||||
STAmount const max(usd, STAmount::kMaxValue, STAmount::kMaxOffset);
|
||||
STAmount const claw(usd, 15, 79);
|
||||
STAmount const diff = max - claw;
|
||||
BEAST_EXPECT(diff == STAmount(usd, std::uint64_t{9999999999999998}, 80));
|
||||
try
|
||||
{
|
||||
auto const _ = diff + claw;
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (std::overflow_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(e.what() == std::string("value overflow"));
|
||||
}
|
||||
BEAST_EXPECT(isExactDifference(max, claw) == false);
|
||||
}
|
||||
|
||||
// Exact XRP difference
|
||||
{
|
||||
STAmount const amt1(XRPAmount(1500));
|
||||
STAmount const amt2(XRPAmount(500));
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// XRP underflow
|
||||
{
|
||||
STAmount const amt1(XRPAmount(1));
|
||||
STAmount const amt2(XRPAmount(2));
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// Exact MPT difference
|
||||
{
|
||||
STAmount const amt1(mpt, 1500);
|
||||
STAmount const amt2(mpt, 500);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == true);
|
||||
}
|
||||
|
||||
// MPT underflow
|
||||
{
|
||||
STAmount const amt1(mpt, 1);
|
||||
STAmount const amt2(mpt, 2);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == false);
|
||||
}
|
||||
|
||||
// Not comparable
|
||||
{
|
||||
STAmount const amt1(XRPAmount(1));
|
||||
STAmount const amt2(usd, 1);
|
||||
BEAST_EXPECT(isExactDifference(amt1, amt2) == false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testMPTRateRounding()
|
||||
{
|
||||
@@ -1577,6 +1793,8 @@ public:
|
||||
testCanAddXRP();
|
||||
testCanAddIOU();
|
||||
testCanAddMPT();
|
||||
testIsExactSum();
|
||||
testIsExactDifference();
|
||||
testMPTRateRounding();
|
||||
testMPTOperandIOUResult();
|
||||
testMPTGetRate();
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
@@ -181,7 +180,6 @@ public:
|
||||
auto const result = env.rpc("json", "sign", to_string(req))[jss::result];
|
||||
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Field 'tx_json.Asset' has invalid data.");
|
||||
}
|
||||
|
||||
@@ -255,7 +253,6 @@ public:
|
||||
auto const result = env.rpc("json", "sign", to_string(req))[jss::result];
|
||||
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Field 'tx_json.Asset' has invalid data.");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -57,16 +56,12 @@ public:
|
||||
Account const gw("gw");
|
||||
auto const usd = gw["USD"];
|
||||
auto const jv = ammAlice.ammRpcInfo({}, {}, usd, usd);
|
||||
BEAST_EXPECT(jv[jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Account not found.");
|
||||
});
|
||||
|
||||
// Invalid LP account id
|
||||
testAMM([&](AMM& ammAlice, Env&) {
|
||||
auto const jv = ammAlice.ammRpcInfo(bogie.id());
|
||||
BEAST_EXPECT(jv[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
|
||||
});
|
||||
|
||||
@@ -74,8 +69,6 @@ public:
|
||||
testAMM([&](AMM& ammAlice, Env&) {
|
||||
auto const jv =
|
||||
ammAlice.ammRpcInfo(json::Value{42}, std::nullopt, XRP, USD, std::nullopt, true, 3);
|
||||
BEAST_EXPECT(jv[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
|
||||
});
|
||||
|
||||
@@ -89,8 +82,6 @@ public:
|
||||
json::Value{42},
|
||||
false,
|
||||
3);
|
||||
BEAST_EXPECT(jv[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
|
||||
});
|
||||
|
||||
@@ -115,8 +106,6 @@ public:
|
||||
iss2,
|
||||
accountId(ammAlice, acct),
|
||||
ignoreParams);
|
||||
BEAST_EXPECT(jv[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
});
|
||||
@@ -132,8 +121,6 @@ public:
|
||||
iss2,
|
||||
accountId(ammAlice, acct),
|
||||
ignoreParams);
|
||||
BEAST_EXPECT(jv[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
});
|
||||
@@ -150,8 +137,6 @@ public:
|
||||
accountId(ammAlice, acct),
|
||||
ignoreParams,
|
||||
3);
|
||||
BEAST_EXPECT(jv[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
});
|
||||
@@ -160,8 +145,6 @@ public:
|
||||
testAMM([&](AMM& ammAlice, Env&) {
|
||||
auto const jv = ammAlice.ammRpcInfo(
|
||||
std::nullopt, std::nullopt, std::nullopt, std::nullopt, bogie.id());
|
||||
BEAST_EXPECT(jv[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Account malformed.");
|
||||
});
|
||||
|
||||
@@ -186,8 +169,6 @@ public:
|
||||
iss2,
|
||||
accountId(ammAlice, acct),
|
||||
ignoreParams);
|
||||
BEAST_EXPECT(jv[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jv[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jv[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
});
|
||||
@@ -204,13 +185,6 @@ public:
|
||||
accountId(ammAlice, acct),
|
||||
ignoreParams,
|
||||
3);
|
||||
BEAST_EXPECT(
|
||||
jv[jss::error] ==
|
||||
(acct == TestAccount::Bogie ? std::string("actMalformed")
|
||||
: std::string("invalidParams")));
|
||||
BEAST_EXPECT(
|
||||
jv[jss::error_code] ==
|
||||
(acct == TestAccount::Bogie ? RpcActMalformed : RpcInvalidParams));
|
||||
BEAST_EXPECT(
|
||||
jv[jss::error_message] ==
|
||||
(acct == TestAccount::Bogie ? std::string("Account malformed.")
|
||||
@@ -409,9 +383,7 @@ public:
|
||||
testAMM([&](AMM& amm, Env&) {
|
||||
auto const resp = amm.ammRpcInfo(
|
||||
std::nullopt, jss::validated.cStr(), std::nullopt, std::nullopt, gw_);
|
||||
BEAST_EXPECT(resp[jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(resp[jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(resp[jss::error_message] == "Account not found.");
|
||||
BEAST_EXPECT(resp.isMember("error") && resp["error"] == "actNotFound");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
{
|
||||
// account_info with no account.
|
||||
auto const info = env.rpc("json", "account_info", "{ }");
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Missing field 'account'.");
|
||||
}
|
||||
@@ -46,7 +45,6 @@ public:
|
||||
"account_info",
|
||||
"{\"account\": "
|
||||
"\"n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV\"}");
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Account malformed.");
|
||||
BEAST_EXPECT(info[jss::result].isMember(jss::ledger_current_index));
|
||||
@@ -58,7 +56,6 @@ public:
|
||||
json::Value params;
|
||||
params[jss::account] = bogie.human();
|
||||
auto const info = env.rpc("json", "account_info", to_string(params));
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Account not found.");
|
||||
BEAST_EXPECT(info[jss::result][jss::account] == bogie.human());
|
||||
@@ -68,7 +65,6 @@ public:
|
||||
{
|
||||
// Cannot use a seed as account
|
||||
auto const info = env.rpc("json", "account_info", R"JSON({"account": "foo"})JSON");
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Account malformed.");
|
||||
}
|
||||
@@ -338,7 +334,6 @@ public:
|
||||
BEAST_EXPECT(info[jss::status] == "error");
|
||||
BEAST_EXPECT(info[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(info[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(info[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(info.isMember(jss::account_data));
|
||||
BEAST_EXPECT(info[jss::account_data][sfAccount.jsonName] == alice.human());
|
||||
BEAST_EXPECT(info.isMember(jss::ledger_current_index));
|
||||
|
||||
@@ -55,7 +55,6 @@ public:
|
||||
{
|
||||
// account_lines with no account.
|
||||
auto const lines = env.rpc("json", "account_lines", "{ }");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -66,7 +65,6 @@ public:
|
||||
json::Value params;
|
||||
params[jss::account] = "n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj";
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -120,7 +118,6 @@ public:
|
||||
json::Value params;
|
||||
params[jss::account] = alice.human();
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -164,7 +161,6 @@ public:
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::ledger_index] = "nonsense";
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -176,7 +172,6 @@ public:
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::ledger_index] = 50000;
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
@@ -310,7 +305,6 @@ public:
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::peer] = "n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj";
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -350,7 +344,6 @@ public:
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::limit] = -1;
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -389,7 +382,6 @@ public:
|
||||
paramsD[jss::account] = alice.human();
|
||||
paramsD[jss::marker] = marker;
|
||||
auto const linesD = env.rpc("json", "account_lines", to_string(paramsD));
|
||||
BEAST_EXPECT(linesD[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(linesD[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
linesD[jss::result][jss::error_message] ==
|
||||
@@ -401,7 +393,6 @@ public:
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::marker] = true;
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -413,7 +404,6 @@ public:
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::marker] = "NOT_A_MARKER";
|
||||
auto const lines = env.rpc("json", "account_lines", to_string(params));
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] ==
|
||||
@@ -558,11 +548,7 @@ public:
|
||||
beckyLinesParams[jss::account] = becky.human();
|
||||
beckyLinesParams[jss::marker] = aliceMarker;
|
||||
auto const beckyLines = env.rpc("json", "account_lines", to_string(beckyLinesParams));
|
||||
BEAST_EXPECT(beckyLines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(beckyLines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
beckyLines[jss::result][jss::error_message] ==
|
||||
rpc::makeError(RpcInvalidParams)[jss::error_message]);
|
||||
BEAST_EXPECT(beckyLines[jss::result].isMember(jss::error_message));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -630,7 +616,6 @@ public:
|
||||
linesEndParams[jss::account] = alice.human();
|
||||
linesEndParams[jss::marker] = linesBeg[jss::result][jss::marker];
|
||||
auto const linesEnd = env.rpc("json", "account_lines", to_string(linesEndParams));
|
||||
BEAST_EXPECT(linesEnd[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(linesEnd[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
linesEnd[jss::result][jss::error_message] ==
|
||||
@@ -929,7 +914,6 @@ public:
|
||||
request[jss::ripplerpc] = "2.0";
|
||||
request[jss::id] = 5;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -949,7 +933,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -970,7 +953,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -1043,7 +1025,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -1064,7 +1045,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(lines[jss::error][jss::message] == "ledgerNotFound");
|
||||
BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0");
|
||||
@@ -1240,7 +1220,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -1261,7 +1240,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -1336,7 +1314,6 @@ public:
|
||||
requestD[jss::id] = 5;
|
||||
requestD[jss::params] = paramsD;
|
||||
auto const linesD = env.rpc("json2", to_string(requestD));
|
||||
BEAST_EXPECT(linesD[jss::error][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(linesD[jss::error][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
linesD[jss::error][jss::message] ==
|
||||
@@ -1357,7 +1334,6 @@ public:
|
||||
request[jss::id] = 5;
|
||||
request[jss::params] = params;
|
||||
auto const lines = env.rpc("json2", to_string(request));
|
||||
BEAST_EXPECT(lines[jss::error][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::error][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::error][jss::message] ==
|
||||
@@ -1516,7 +1492,6 @@ public:
|
||||
linesEndRequest[jss::id] = 5;
|
||||
linesEndRequest[jss::params] = linesEndParams;
|
||||
auto const linesEnd = env.rpc("json2", to_string(linesEndRequest));
|
||||
BEAST_EXPECT(linesEnd[jss::error][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(linesEnd[jss::error][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
linesEnd[jss::error][jss::message] ==
|
||||
|
||||
@@ -140,8 +140,6 @@ public:
|
||||
{
|
||||
json::Value const params;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Missing field 'account'.");
|
||||
}
|
||||
// test account non-string
|
||||
@@ -151,7 +149,6 @@ public:
|
||||
params[jss::account] = param;
|
||||
auto jrr = env.rpc("json", "account_objects", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
|
||||
};
|
||||
|
||||
@@ -167,8 +164,6 @@ public:
|
||||
json::Value params;
|
||||
params[jss::account] = "n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV";
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Account malformed.");
|
||||
}
|
||||
// test error on account that's not in the ledger.
|
||||
@@ -176,8 +171,6 @@ public:
|
||||
json::Value params;
|
||||
params[jss::account] = Account{"bogie"}.human();
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Account not found.");
|
||||
}
|
||||
Account const bob{"bob"};
|
||||
@@ -187,8 +180,6 @@ public:
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::ledger_index] = 10;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
@@ -199,8 +190,6 @@ public:
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::type] = 10;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Invalid field 'type', not string.");
|
||||
}
|
||||
@@ -210,8 +199,6 @@ public:
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::type] = "expedited";
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'type'.");
|
||||
}
|
||||
// test error on limit -ve
|
||||
@@ -220,8 +207,6 @@ public:
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::limit] = -1;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"Invalid field 'limit', not unsigned integer.");
|
||||
@@ -232,8 +217,6 @@ public:
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::sponsored] = "true";
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Invalid field 'sponsored', not boolean.");
|
||||
}
|
||||
@@ -255,39 +238,27 @@ public:
|
||||
std::string mark = to_string(resumeMarker);
|
||||
params[jss::marker] = 10;
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Invalid field 'marker', not string.");
|
||||
|
||||
params[jss::marker] = "This is a string with no comma";
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = "This string has a comma, but is not hex";
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = std::string(&mark[1U], 64);
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = std::string(&mark[1U], 65);
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = std::string(&mark[1U], 65) + "not hex";
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
// Should this be an error?
|
||||
@@ -632,8 +603,7 @@ public:
|
||||
|
||||
// Make a lambda that checks if the response has error for invalid type
|
||||
auto const acctObjsTypeIsInvalid = [](json::Value const& resp) {
|
||||
return resp[jss::result][jss::error] == "invalidParams" &&
|
||||
resp[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
return resp[jss::result].isMember(jss::error) &&
|
||||
resp[jss::result][jss::error_message] == "Invalid field \'type\'.";
|
||||
};
|
||||
|
||||
@@ -1204,9 +1174,7 @@ public:
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::marker] = marker;
|
||||
json::Value const resp = env.rpc("json", "account_nfts", to_string(params));
|
||||
return resp[jss::result][jss::error] == "invalidParams" &&
|
||||
resp[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
resp[jss::result][jss::error_message] == errorMessage;
|
||||
return resp[jss::result][jss::error_message] == errorMessage;
|
||||
};
|
||||
|
||||
// test an invalid marker that is not a string
|
||||
@@ -1459,9 +1427,7 @@ public:
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::marker] = marker;
|
||||
json::Value const resp = env.rpc("json", "account_objects", to_string(params));
|
||||
return resp[jss::result][jss::error] == "invalidParams" &&
|
||||
resp[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
resp[jss::result][jss::error_message] == "Invalid field \'marker\'.";
|
||||
return resp[jss::result][jss::error_message] == "Invalid field \'marker\'.";
|
||||
};
|
||||
|
||||
auto const markerStr = marker.asString();
|
||||
|
||||
@@ -173,9 +173,7 @@ public:
|
||||
jvParams[jss::limit] = 0u;
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit'.");
|
||||
BEAST_EXPECT(jrr.isMember(jss::error_message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,9 +195,9 @@ public:
|
||||
{
|
||||
// no account field
|
||||
auto const jrr = env.rpc("account_offers");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "badSyntax");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcBadSyntax);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Syntax error.");
|
||||
}
|
||||
|
||||
@@ -228,9 +226,9 @@ public:
|
||||
jvParams[jss::account] = "";
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Account malformed.");
|
||||
BEAST_EXPECT(jrr.isMember(jss::ledger_current_index));
|
||||
BEAST_EXPECT(jrr[jss::validated] == false);
|
||||
@@ -239,9 +237,9 @@ public:
|
||||
{
|
||||
// bogus account value
|
||||
auto const jrr = env.rpc("account_offers", Account("bogus").human())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Account not found.");
|
||||
}
|
||||
|
||||
@@ -252,9 +250,9 @@ public:
|
||||
jvParams[jss::limit] = "0"; // NOT an integer
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not unsigned integer.");
|
||||
}
|
||||
|
||||
@@ -265,9 +263,9 @@ public:
|
||||
jvParams[jss::marker] = "NOT_A_MARKER";
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECTS(
|
||||
jrr[jss::error_message] == "Invalid field 'marker'.", jrr.toStyledString());
|
||||
}
|
||||
@@ -279,9 +277,9 @@ public:
|
||||
jvParams[jss::marker] = 1;
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not string.");
|
||||
}
|
||||
|
||||
@@ -292,9 +290,9 @@ public:
|
||||
jvParams[jss::ledger_index] = 10u;
|
||||
auto const jrr =
|
||||
env.rpc("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
|
||||
+62
-129
@@ -195,27 +195,19 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
(j[jss::result][jss::transactions].size() == 0);
|
||||
};
|
||||
|
||||
auto const isErr = [](json::Value const& j, ErrorCodeI code, std::string const& message) {
|
||||
return j.isMember(jss::result) &&
|
||||
j[jss::result][jss::error] == rpc::getErrorInfo(code).token &&
|
||||
j[jss::result][jss::error_code] == code &&
|
||||
j[jss::result][jss::error_message] == message;
|
||||
auto const isErr = [](json::Value const& j, ErrorCodeI code) {
|
||||
return j.isMember(jss::result) && j[jss::result].isMember(jss::error) &&
|
||||
j[jss::result][jss::error] == rpc::getErrorInfo(code).token;
|
||||
};
|
||||
|
||||
json::Value jParams;
|
||||
jParams[jss::api_version] = apiVersion;
|
||||
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(jParams)),
|
||||
RpcInvalidParams,
|
||||
"Missing field 'account'."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(jParams)), RpcInvalidParams));
|
||||
|
||||
jParams[jss::account] = "0xDEADBEEF";
|
||||
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(jParams)),
|
||||
RpcActMalformed,
|
||||
"Account malformed."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(jParams)), RpcActMalformed));
|
||||
|
||||
jParams[jss::account] = a1.human();
|
||||
BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(jParams))));
|
||||
@@ -235,10 +227,8 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrIdxMalformed,
|
||||
"Ledger index malformed."));
|
||||
BEAST_EXPECT(
|
||||
isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrIdxMalformed));
|
||||
}
|
||||
|
||||
p[jss::ledger_index_min] = 1;
|
||||
@@ -249,18 +239,15 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrIdxMalformed,
|
||||
"Ledger index malformed."));
|
||||
BEAST_EXPECT(
|
||||
isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrIdxMalformed));
|
||||
}
|
||||
|
||||
p[jss::ledger_index_min] = 2;
|
||||
p[jss::ledger_index_max] = 1;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
(apiVersion == 1 ? RpcLgrIdxsInvalid : RpcInvalidLgrRange),
|
||||
(apiVersion == 1 ? "Ledger indexes invalid." : "Ledger range is invalid.")));
|
||||
(apiVersion == 1 ? RpcLgrIdxsInvalid : RpcInvalidLgrRange)));
|
||||
}
|
||||
// Ledger index min only
|
||||
{
|
||||
@@ -275,17 +262,14 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrIdxMalformed,
|
||||
"Ledger index malformed."));
|
||||
BEAST_EXPECT(
|
||||
isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrIdxMalformed));
|
||||
}
|
||||
|
||||
p[jss::ledger_index_min] = env.current()->header().seq;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
(apiVersion == 1 ? RpcLgrIdxsInvalid : RpcInvalidLgrRange),
|
||||
(apiVersion == 1 ? "Ledger indexes invalid." : "Ledger range is invalid.")));
|
||||
(apiVersion == 1 ? RpcLgrIdxsInvalid : RpcInvalidLgrRange)));
|
||||
}
|
||||
|
||||
// Ledger index max only
|
||||
@@ -301,10 +285,8 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrIdxMalformed,
|
||||
"Ledger index malformed."));
|
||||
BEAST_EXPECT(
|
||||
isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrIdxMalformed));
|
||||
}
|
||||
|
||||
p[jss::ledger_index_max] = 3;
|
||||
@@ -328,14 +310,10 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(noTxs(env.rpc("json", "account_tx", to_string(p))));
|
||||
|
||||
p[jss::ledger_index] = env.current()->header().seq;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrNotValidated,
|
||||
"Ledger not validated."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
|
||||
|
||||
p[jss::ledger_index] = env.current()->header().seq + 1;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)), RpcLgrNotFound, "ledgerNotFound"));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotFound));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -348,16 +326,10 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(hasTxs(env.rpc(apiVersion, "json", "account_tx", to_string(p))));
|
||||
|
||||
p[jss::ledger_index] = "current";
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrNotValidated,
|
||||
"Ledger not validated."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
|
||||
|
||||
p[jss::ledger_index] = "";
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrNotValidated,
|
||||
"Ledger not validated."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrNotValidated));
|
||||
}
|
||||
|
||||
// Ledger Hash
|
||||
@@ -388,10 +360,7 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
"invalidParams"));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcInvalidParams));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,10 +374,8 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcLgrIdxMalformed,
|
||||
"Ledger index malformed."));
|
||||
BEAST_EXPECT(
|
||||
isErr(env.rpc("json", "account_tx", to_string(p)), RpcLgrIdxMalformed));
|
||||
}
|
||||
}
|
||||
// test account non-string
|
||||
@@ -418,7 +385,6 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
params[jss::account] = param;
|
||||
auto jrr = env.rpc("json", "account_tx", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
|
||||
};
|
||||
|
||||
@@ -440,10 +406,7 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'binary'."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcInvalidParams));
|
||||
}
|
||||
|
||||
p[jss::binary] = true;
|
||||
@@ -457,10 +420,7 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'forward'."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcInvalidParams));
|
||||
}
|
||||
|
||||
p[jss::forward] = false;
|
||||
@@ -473,83 +433,70 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
|
||||
// Test case: limit = 0 should fail (below minimum)
|
||||
p[jss::limit] = 0;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'limit'."));
|
||||
BEAST_EXPECT(isErr(env.rpc("json", "account_tx", to_string(p)), RpcInvalidParams));
|
||||
|
||||
// Test case: limit = 1.2 should fail (not an integer)
|
||||
p[jss::limit] = 1.2;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = "10" should fail (string instead of integer)
|
||||
p[jss::limit] = "10";
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = true should fail (boolean instead of integer)
|
||||
p[jss::limit] = true;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = false should fail (boolean instead of integer)
|
||||
p[jss::limit] = false;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = -1 should fail (negative number)
|
||||
p[jss::limit] = -1;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = [] should fail (array instead of integer)
|
||||
p[jss::limit] = json::Value(json::ValueType::Array);
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = {} should fail (object instead of integer)
|
||||
p[jss::limit] = json::Value(json::ValueType::Object);
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = "malformed" should fail (malformed string)
|
||||
p[jss::limit] = "malformed";
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = ["limit"] should fail (array with string)
|
||||
p[jss::limit] = json::Value(json::ValueType::Array);
|
||||
p[jss::limit].append("limit");
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = {"limit": 10} should fail (object with
|
||||
// property)
|
||||
p[jss::limit] = json::Value(json::ValueType::Object);
|
||||
p[jss::limit][jss::limit] = 10;
|
||||
BEAST_EXPECT(isErr(
|
||||
env.rpc("json", "account_tx", to_string(p)),
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer")));
|
||||
BEAST_EXPECT(
|
||||
env.rpc("json", "account_tx", to_string(p))[jss::result][jss::error_message] ==
|
||||
rpc::expectedFieldMessage(jss::limit, "unsigned integer"));
|
||||
|
||||
// Test case: limit = 10 should succeed (valid integer)
|
||||
p[jss::limit] = 10;
|
||||
@@ -1038,16 +985,12 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
};
|
||||
|
||||
auto const checkError = [&](json::Value const& delegateParams,
|
||||
std::string const& errToken,
|
||||
ErrorCodeI code,
|
||||
std::string const& errMessage) {
|
||||
std::string const& errToken) {
|
||||
json::Value params;
|
||||
params[jss::account] = alice.human();
|
||||
params[jss::delegate] = delegateParams;
|
||||
auto res = env.rpc("json", "account_tx", to_string(params));
|
||||
BEAST_EXPECT(res[jss::result][jss::error] == errToken);
|
||||
BEAST_EXPECT(res[jss::result][jss::error_code] == code);
|
||||
BEAST_EXPECT(res[jss::result][jss::error_message] == errMessage);
|
||||
};
|
||||
|
||||
// Filter: Delegatee. Expects TX #2 (Signed by Bob)
|
||||
@@ -1118,27 +1061,27 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
// "delegate" is not an object (e.g., string)
|
||||
{
|
||||
json::Value const p = "not_an_object";
|
||||
checkError(p, "invalidParams", RpcInvalidParams, "Invalid field 'delegate'.");
|
||||
checkError(p, "invalidParams");
|
||||
}
|
||||
|
||||
// Missing "delegate_filter" inside object
|
||||
{
|
||||
json::Value const p(json::ValueType::Object);
|
||||
checkError(p, "invalidParams", RpcInvalidParams, "Invalid field 'delegate_filter'.");
|
||||
checkError(p, "invalidParams");
|
||||
}
|
||||
|
||||
// "delegate_filter" is not a string (e.g., int)
|
||||
{
|
||||
json::Value p;
|
||||
p[jss::delegate_filter] = 123;
|
||||
checkError(p, "invalidParams", RpcInvalidParams, "Invalid field 'delegate_filter'.");
|
||||
checkError(p, "invalidParams");
|
||||
}
|
||||
|
||||
// "delegate_filter" has invalid value
|
||||
{
|
||||
json::Value p;
|
||||
p[jss::delegate_filter] = "random_string";
|
||||
checkError(p, "invalidParams", RpcInvalidParams, "Invalid field 'delegate_filter'.");
|
||||
checkError(p, "invalidParams");
|
||||
}
|
||||
|
||||
// "counterparty" is not a string
|
||||
@@ -1146,7 +1089,7 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
json::Value p;
|
||||
p[jss::delegate_filter] = "actor";
|
||||
p[jss::counter_party] = 123;
|
||||
checkError(p, "invalidParams", RpcInvalidParams, "Invalid field 'counter_party'.");
|
||||
checkError(p, "invalidParams");
|
||||
}
|
||||
|
||||
// "counterparty" is malformed base58
|
||||
@@ -1154,7 +1097,7 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
json::Value p;
|
||||
p[jss::delegate_filter] = "actor";
|
||||
p[jss::counter_party] = "not_an_account";
|
||||
checkError(p, "actMalformed", RpcActMalformed, "Account malformed.");
|
||||
checkError(p, "actMalformed");
|
||||
}
|
||||
|
||||
// Multi-signed non-delegated TX: Alice pays Carol via multi-sig.
|
||||
@@ -1238,11 +1181,6 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
p[jss::marker] = delegateMarker;
|
||||
auto const r = env.rpc("json", "account_tx", to_string(p));
|
||||
BEAST_EXPECT(r[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(r[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
r[jss::result][jss::error_message] ==
|
||||
"Do not mix delegate and non-delegate pagination markers in account_tx; "
|
||||
"repeat the same `delegate` object when using a delegate marker.");
|
||||
}
|
||||
|
||||
// Obtain a non-delegate marker; it must not carry the flag.
|
||||
@@ -1267,11 +1205,6 @@ class AccountTx_test : public beast::unit_test::Suite
|
||||
p[jss::marker] = normalMarker;
|
||||
auto const r = env.rpc("json", "account_tx", to_string(p));
|
||||
BEAST_EXPECT(r[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(r[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
r[jss::result][jss::error_message] ==
|
||||
"Do not mix delegate and non-delegate pagination markers in account_tx; "
|
||||
"repeat the same `delegate` object when using a delegate marker.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -187,42 +186,32 @@ class AmendmentBlocked_test : public beast::unit_test::Suite
|
||||
|
||||
// ledger_accept
|
||||
jr = env.rpc("ledger_accept")[jss::result];
|
||||
BEAST_EXPECT(jr.isMember(jss::error) && jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::status] == "error");
|
||||
BEAST_EXPECT(jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcAmendmentBlocked);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Amendment blocked, need upgrade.");
|
||||
BEAST_EXPECT(!jr.isMember(jss::warnings));
|
||||
|
||||
// ledger_current
|
||||
jr = env.rpc("ledger_current")[jss::result];
|
||||
BEAST_EXPECT(jr.isMember(jss::error) && jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::status] == "error");
|
||||
BEAST_EXPECT(jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcAmendmentBlocked);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Amendment blocked, need upgrade.");
|
||||
BEAST_EXPECT(!jr.isMember(jss::warnings));
|
||||
|
||||
// owner_info
|
||||
jr = env.rpc("owner_info", alice.human())[jss::result];
|
||||
BEAST_EXPECT(jr.isMember(jss::error) && jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::status] == "error");
|
||||
BEAST_EXPECT(jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcAmendmentBlocked);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Amendment blocked, need upgrade.");
|
||||
BEAST_EXPECT(!jr.isMember(jss::warnings));
|
||||
|
||||
// path_find
|
||||
jr = wsc->invoke("path_find", pfReq)[jss::result];
|
||||
BEAST_EXPECT(jr.isMember(jss::error) && jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::status] == "error");
|
||||
BEAST_EXPECT(jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcAmendmentBlocked);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Amendment blocked, need upgrade.");
|
||||
BEAST_EXPECT(!jr.isMember(jss::warnings));
|
||||
|
||||
// submit
|
||||
jr = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||
BEAST_EXPECT(jr.isMember(jss::error) && jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::status] == "error");
|
||||
BEAST_EXPECT(jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcAmendmentBlocked);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Amendment blocked, need upgrade.");
|
||||
BEAST_EXPECT(!jr.isMember(jss::warnings));
|
||||
|
||||
// submit_multisigned
|
||||
@@ -232,9 +221,7 @@ class AmendmentBlocked_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECT(jr[jss::status] == "success");
|
||||
msReq[jss::tx_json] = jr[jss::tx_json];
|
||||
jr = env.rpc("json", "submit_multisigned", to_string(msReq))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcAmendmentBlocked);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Amendment blocked, need upgrade.");
|
||||
BEAST_EXPECT(jr.isMember(jss::error) && jr[jss::error] == "amendmentBlocked");
|
||||
BEAST_EXPECT(!jr.isMember(jss::warnings));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/Ledger.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -78,12 +77,8 @@ public:
|
||||
// non-conventional ledger input should throw an error
|
||||
params["ledger"] = "non_conventional_ledger_input";
|
||||
resp = env.rpc("json", "book_changes", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::status] == "error");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"Invalid field 'ledger', not string or number.");
|
||||
BEAST_EXPECT(resp[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(resp[jss::result][jss::status] != "success");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
@@ -1226,7 +1225,6 @@ public:
|
||||
jvParams[jss::ledger_index] = 10u;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
@@ -1235,7 +1233,6 @@ public:
|
||||
jvParams[jss::ledger_index] = "validated";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'taker_pays'.");
|
||||
}
|
||||
|
||||
@@ -1245,7 +1242,6 @@ public:
|
||||
jvParams[jss::taker_pays] = json::ValueType::Object;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'taker_gets'.");
|
||||
}
|
||||
|
||||
@@ -1256,7 +1252,6 @@ public:
|
||||
jvParams[jss::taker_gets] = json::ValueType::Object;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_pays', not object.");
|
||||
}
|
||||
|
||||
@@ -1267,7 +1262,6 @@ public:
|
||||
jvParams[jss::taker_gets] = "not an object";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker_gets', not object.");
|
||||
}
|
||||
|
||||
@@ -1278,7 +1272,6 @@ public:
|
||||
jvParams[jss::taker_gets] = json::ValueType::Object;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'taker_pays.currency'.");
|
||||
}
|
||||
|
||||
@@ -1289,7 +1282,6 @@ public:
|
||||
jvParams[jss::taker_gets] = json::ValueType::Object;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_pays.currency', not string.");
|
||||
}
|
||||
@@ -1301,7 +1293,6 @@ public:
|
||||
jvParams[jss::taker_gets] = json::ValueType::Object;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'taker_gets.currency'.");
|
||||
}
|
||||
|
||||
@@ -1312,7 +1303,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = 1;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_gets.currency', not string.");
|
||||
}
|
||||
@@ -1324,7 +1314,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = "XRP";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcCurMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_pays.currency', bad currency.");
|
||||
}
|
||||
@@ -1336,7 +1325,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = "NOT_VALID";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "dstAmtMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcDstAmtMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_gets.currency', bad currency.");
|
||||
}
|
||||
@@ -1349,7 +1337,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = 1;
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', not string.");
|
||||
}
|
||||
@@ -1362,7 +1349,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = "USD";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', not string.");
|
||||
}
|
||||
@@ -1375,7 +1361,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = "USD";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_pays.issuer', bad issuer.");
|
||||
}
|
||||
@@ -1388,7 +1373,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = "USD";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"Invalid field 'taker_pays.issuer', bad issuer account one.");
|
||||
@@ -1402,7 +1386,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human() + "DEAD";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcDstIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'taker_gets.issuer', bad issuer.");
|
||||
}
|
||||
@@ -1415,7 +1398,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = toBase58(noAccount());
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcDstIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"Invalid field 'taker_gets.issuer', bad issuer account one.");
|
||||
@@ -1430,7 +1412,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"Unneeded field 'taker_pays.issuer' "
|
||||
@@ -1446,7 +1427,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcSrcIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
|
||||
@@ -1461,7 +1441,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker', not string.");
|
||||
}
|
||||
|
||||
@@ -1474,7 +1453,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'taker'.");
|
||||
}
|
||||
|
||||
@@ -1488,7 +1466,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "badMarket");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcBadMarket);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "No such market.");
|
||||
}
|
||||
|
||||
@@ -1502,7 +1479,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not unsigned integer.");
|
||||
}
|
||||
|
||||
@@ -1516,7 +1492,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit'.");
|
||||
}
|
||||
|
||||
@@ -1528,7 +1503,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::currency] = "USD";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcDstIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"Invalid field 'taker_gets.issuer', "
|
||||
@@ -1544,7 +1518,6 @@ public:
|
||||
jvParams[jss::taker_gets][jss::issuer] = gw.human();
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcDstIsrMalformed);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] ==
|
||||
"Unneeded field 'taker_gets.issuer' "
|
||||
@@ -1560,7 +1533,6 @@ public:
|
||||
jvParams[jss::domain] = "badString";
|
||||
auto const jrr = env.rpc("json", "book_offers", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "domainMalformed");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcDomainMalformed);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Unable to parse domain.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -23,8 +22,8 @@ class Connect_test : public beast::unit_test::Suite
|
||||
|
||||
auto const result = env.rpc("json", "connect", "{}");
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == "notSynced");
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcNotSynced);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Not synced to the network.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Protocol.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -155,16 +154,12 @@ public:
|
||||
Account const becky{"becky"};
|
||||
|
||||
// Lambda that checks the (error) result of deposit_authorized.
|
||||
auto const verifyErr = [this](
|
||||
json::Value const& result,
|
||||
char const* error,
|
||||
ErrorCodeI code,
|
||||
char const* errorMsg) {
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == code);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == errorMsg);
|
||||
};
|
||||
auto const verifyErr =
|
||||
[this](json::Value const& result, char const* error, char const* errorMsg) {
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == errorMsg);
|
||||
};
|
||||
|
||||
Env env(*this);
|
||||
{
|
||||
@@ -172,33 +167,28 @@ public:
|
||||
json::Value args{depositAuthArgs(alice, becky)};
|
||||
args.removeMember(jss::source_account);
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(result, "invalidParams", RpcInvalidParams, "Missing field 'source_account'.");
|
||||
verifyErr(result, "invalidParams", "Missing field 'source_account'.");
|
||||
}
|
||||
{
|
||||
// Non-string source_account field.
|
||||
json::Value args{depositAuthArgs(alice, becky)};
|
||||
args[jss::source_account] = 7.3;
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(
|
||||
result,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'source_account', not a string.");
|
||||
verifyErr(result, "invalidParams", "Invalid field 'source_account', not a string.");
|
||||
}
|
||||
{
|
||||
// Corrupt source_account field.
|
||||
json::Value args{depositAuthArgs(alice, becky)};
|
||||
args[jss::source_account] = "rG1QQv2nh2gr7RCZ!P8YYcBUKCCN633jCn";
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(result, "actMalformed", RpcActMalformed, "Account malformed.");
|
||||
verifyErr(result, "actMalformed", "Account malformed.");
|
||||
}
|
||||
{
|
||||
// Missing destination_account field.
|
||||
json::Value args{depositAuthArgs(alice, becky)};
|
||||
args.removeMember(jss::destination_account);
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(
|
||||
result, "invalidParams", RpcInvalidParams, "Missing field 'destination_account'.");
|
||||
verifyErr(result, "invalidParams", "Missing field 'destination_account'.");
|
||||
}
|
||||
{
|
||||
// Non-string destination_account field.
|
||||
@@ -206,46 +196,40 @@ public:
|
||||
args[jss::destination_account] = 7.3;
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(
|
||||
result,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'destination_account', not a string.");
|
||||
result, "invalidParams", "Invalid field 'destination_account', not a string.");
|
||||
}
|
||||
{
|
||||
// Corrupt destination_account field.
|
||||
json::Value args{depositAuthArgs(alice, becky)};
|
||||
args[jss::destination_account] = "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas";
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(result, "actMalformed", RpcActMalformed, "Account malformed.");
|
||||
verifyErr(result, "actMalformed", "Account malformed.");
|
||||
}
|
||||
{
|
||||
// Request an invalid ledger.
|
||||
json::Value const args{depositAuthArgs(alice, becky, "-1")};
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(
|
||||
result,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'ledger_index', not string or number.");
|
||||
result, "invalidParams", "Invalid field 'ledger_index', not string or number.");
|
||||
}
|
||||
{
|
||||
// Request a ledger that doesn't exist yet as a string.
|
||||
json::Value const args{depositAuthArgs(alice, becky, "17")};
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(result, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound");
|
||||
verifyErr(result, "lgrNotFound", "ledgerNotFound");
|
||||
}
|
||||
{
|
||||
// Request a ledger that doesn't exist yet.
|
||||
json::Value args{depositAuthArgs(alice, becky)};
|
||||
args[jss::ledger_index] = 17;
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(result, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound");
|
||||
verifyErr(result, "lgrNotFound", "ledgerNotFound");
|
||||
}
|
||||
{
|
||||
// alice is not yet funded.
|
||||
json::Value const args{depositAuthArgs(alice, becky)};
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(result, "srcActNotFound", RpcSrcActNotFound, "Source account not found.");
|
||||
verifyErr(result, "srcActNotFound", "Source account not found.");
|
||||
}
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
@@ -253,8 +237,7 @@ public:
|
||||
// becky is not yet funded.
|
||||
json::Value const args{depositAuthArgs(alice, becky)};
|
||||
json::Value const result{env.rpc("json", "deposit_authorized", args.toStyledString())};
|
||||
verifyErr(
|
||||
result, "dstActNotFound", RpcDstActNotFound, "Destination account not found.");
|
||||
verifyErr(result, "dstActNotFound", "Destination account not found.");
|
||||
}
|
||||
env.fund(XRP(1000), becky);
|
||||
env.close();
|
||||
@@ -273,11 +256,9 @@ public:
|
||||
jtx::Account const& dst,
|
||||
bool authorized,
|
||||
std::vector<std::string> credentialIDs = {},
|
||||
std::string_view error = "",
|
||||
ErrorCodeI code = RpcSuccess,
|
||||
std::string_view errorMessage = "")
|
||||
std::string_view error = "")
|
||||
{
|
||||
BEAST_EXPECT(result[jss::status] == (authorized ? jss::success : jss::error));
|
||||
BEAST_EXPECT(result[jss::status] == authorized ? jss::success : jss::error);
|
||||
if (result.isMember(jss::deposit_authorized))
|
||||
BEAST_EXPECT(result[jss::deposit_authorized] == authorized);
|
||||
if (authorized)
|
||||
@@ -289,11 +270,7 @@ public:
|
||||
|
||||
BEAST_EXPECT(result.isMember(jss::error) == !error.empty());
|
||||
if (!error.empty())
|
||||
{
|
||||
BEAST_EXPECT(result[jss::error].asString() == error);
|
||||
BEAST_EXPECT(result[jss::error_code] == code);
|
||||
BEAST_EXPECT(result[jss::error_message].asString() == errorMessage);
|
||||
}
|
||||
|
||||
if (authorized)
|
||||
{
|
||||
@@ -357,15 +334,7 @@ public:
|
||||
args[jss::credentials] = json::ValueType::Array;
|
||||
|
||||
auto const jv = env.rpc("json", "deposit_authorized", args.toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{},
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'credentials', not is non-empty array of CredentialID(hash256).");
|
||||
checkCredentialsResponse(jv[jss::result], alice, becky, false, {}, "invalidParams");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -379,15 +348,7 @@ public:
|
||||
args[jss::credentials].append(3);
|
||||
|
||||
auto const jv = env.rpc("json", "deposit_authorized", args.toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{},
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'credentials', not an array of CredentialID(hash256).");
|
||||
checkCredentialsResponse(jv[jss::result], alice, becky, false, {}, "invalidParams");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -401,14 +362,7 @@ public:
|
||||
|
||||
auto const jv = env.rpc("json", "deposit_authorized", args.toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{"hello world"},
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'credentials', not an array of CredentialID(hash256).");
|
||||
jv[jss::result], alice, becky, false, {"hello world"}, "invalidParams");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -435,9 +389,7 @@ public:
|
||||
"0127AB8B4B29CCDBB61AA51C0799A8A6BB80B86A9899807C11ED576AF8516"
|
||||
"473",
|
||||
},
|
||||
"badCredentials",
|
||||
RpcBadCredentials,
|
||||
"credentials don't exist");
|
||||
"badCredentials");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -451,14 +403,7 @@ public:
|
||||
|
||||
auto const jv = env.rpc("json", "deposit_authorized", args.toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{zero},
|
||||
"badCredentials",
|
||||
RpcBadCredentials,
|
||||
"credentials don't exist");
|
||||
jv[jss::result], alice, becky, false, {zero}, "badCredentials");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,14 +416,7 @@ public:
|
||||
"deposit_authorized",
|
||||
depositAuthArgs(alice, becky, "validated", {credIdx}).toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{credIdx},
|
||||
"badCredentials",
|
||||
RpcBadCredentials,
|
||||
"credentials aren't accepted");
|
||||
jv[jss::result], alice, becky, false, {credIdx}, "badCredentials");
|
||||
}
|
||||
|
||||
// alice accept credentials
|
||||
@@ -492,14 +430,7 @@ public:
|
||||
"deposit_authorized",
|
||||
depositAuthArgs(alice, becky, "validated", {credIdx, credIdx}).toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{credIdx, credIdx},
|
||||
"badCredentials",
|
||||
RpcBadCredentials,
|
||||
"duplicates in credentials");
|
||||
jv[jss::result], alice, becky, false, {credIdx, credIdx}, "badCredentials");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -522,14 +453,7 @@ public:
|
||||
"deposit_authorized",
|
||||
depositAuthArgs(alice, becky, "validated", kCredIds).toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
kCredIds,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'credentials', not array too long.");
|
||||
jv[jss::result], alice, becky, false, kCredIds, "invalidParams");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -576,14 +500,7 @@ public:
|
||||
"deposit_authorized",
|
||||
depositAuthArgs(becky, alice, "validated", {credDiana}).toStyledString());
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
becky,
|
||||
alice,
|
||||
false,
|
||||
{credDiana},
|
||||
"badCredentials",
|
||||
RpcBadCredentials,
|
||||
"credentials doesn't belong to the root account");
|
||||
jv[jss::result], becky, alice, false, {credDiana}, "badCredentials");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -632,14 +549,7 @@ public:
|
||||
depositAuthArgs(alice, becky, "validated", {credIdx2}).toStyledString());
|
||||
|
||||
checkCredentialsResponse(
|
||||
jv[jss::result],
|
||||
alice,
|
||||
becky,
|
||||
false,
|
||||
{credIdx2},
|
||||
"badCredentials",
|
||||
RpcBadCredentials,
|
||||
"credentials are expired");
|
||||
jv[jss::result], alice, becky, false, {credIdx2}, "badCredentials");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/AmendmentTable.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -207,7 +206,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
// feature names are case-sensitive - expect error here
|
||||
jrr = env.rpc("feature", "fMM")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "badFeature");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcBadFeature);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
|
||||
|
||||
// Test feature name size checks
|
||||
@@ -255,7 +253,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
params[jss::feature] = param;
|
||||
auto jrr = env.rpc("json", "feature", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
|
||||
};
|
||||
|
||||
@@ -269,7 +266,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
{
|
||||
auto jrr = env.rpc("feature", "AllTheThings")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "badFeature");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcBadFeature);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
|
||||
}
|
||||
}
|
||||
@@ -324,7 +320,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
"EF";
|
||||
auto const result = env.rpc("json", "feature", to_string(params))[jss::result];
|
||||
BEAST_EXPECTS(result[jss::error] == "badFeature", result.toStyledString());
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcBadFeature);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Feature unknown or invalid.");
|
||||
}
|
||||
|
||||
@@ -337,7 +332,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
params[jss::vetoed] = true;
|
||||
auto const result = env.rpc("json", "feature", to_string(params))[jss::result];
|
||||
BEAST_EXPECTS(result[jss::error] == "noPermission", result[jss::error].asString());
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcNoPermission);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] == "You don't have permission for this command.");
|
||||
}
|
||||
@@ -519,7 +513,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
|
||||
auto const result = env.rpc("json", "feature", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
};
|
||||
|
||||
@@ -528,7 +521,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
// anything other than accept or reject is an error
|
||||
jrr = env.rpc("feature", kFeatureName, "maybe");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -592,7 +584,6 @@ class Feature_test : public beast::unit_test::Suite
|
||||
// anything other than accept or reject is an error
|
||||
jrr = env.rpc("feature", featureName, "maybe");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -173,12 +172,7 @@ public:
|
||||
|
||||
auto response = jv[jss::result];
|
||||
auto const error = apiVersion < 2u ? "invalidHotWallet" : "invalidParams";
|
||||
auto const errorCode = apiVersion < 2u ? RpcInvalidHotwallet : RpcInvalidParams;
|
||||
auto const errorMessage =
|
||||
apiVersion < 2u ? "Invalid hotwallet." : "Invalid parameters.";
|
||||
BEAST_EXPECT(response[jss::error] == error);
|
||||
BEAST_EXPECT(response[jss::error_code] == errorCode);
|
||||
BEAST_EXPECT(response[jss::error_message] == errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,8 +201,6 @@ public:
|
||||
auto jv = wsc->invoke("gateway_balances", qry);
|
||||
expect(jv[jss::status] == "error");
|
||||
BEAST_EXPECT(jv[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jv[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jv[jss::result][jss::error_message] == "Invalid field 'account'.");
|
||||
});
|
||||
|
||||
// The same applies to a non-string "ident".
|
||||
@@ -220,8 +212,6 @@ public:
|
||||
auto jv = wsc->invoke("gateway_balances", qry2);
|
||||
expect(jv[jss::status] == "error");
|
||||
BEAST_EXPECT(jv[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jv[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jv[jss::result][jss::error_message] == "Invalid field 'ident'.");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -43,14 +42,10 @@ public:
|
||||
auto const baseFee = env.current()->fees().base;
|
||||
// missing base_asset
|
||||
auto ret = Oracle::aggregatePrice(env, std::nullopt, "USD", kOracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Missing field 'base_asset'.");
|
||||
|
||||
// missing quote_asset
|
||||
ret = Oracle::aggregatePrice(env, "XRP", std::nullopt, kOracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Missing field 'quote_asset'.");
|
||||
|
||||
// invalid base_asset, quote_asset
|
||||
@@ -74,44 +69,28 @@ public:
|
||||
{
|
||||
ret = Oracle::aggregatePrice(env, "USD", v, kOracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Invalid parameters.");
|
||||
ret = Oracle::aggregatePrice(env, v, "USD", kOracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Invalid parameters.");
|
||||
ret = Oracle::aggregatePrice(env, v, v, kOracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Invalid parameters.");
|
||||
}
|
||||
|
||||
// missing oracles array
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD");
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Missing field 'oracles'.");
|
||||
|
||||
// empty oracles array
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD", OraclesData{});
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "oracleMalformed");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcOracleMalformed);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Oracle request is malformed.");
|
||||
|
||||
// no token pairs found
|
||||
ret = Oracle::aggregatePrice(env, "YAN", "USD", kOracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcObjectNotFound);
|
||||
BEAST_EXPECT(
|
||||
ret[jss::error_message].asString() == "The requested object was not found.");
|
||||
|
||||
// invalid oracle document id
|
||||
// id doesn't exist
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, 2}}});
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcObjectNotFound);
|
||||
BEAST_EXPECT(
|
||||
ret[jss::error_message].asString() == "The requested object was not found.");
|
||||
// invalid values
|
||||
std::vector<AnyValue> const invalidDocument = {kNoneTag, 1.2, -1, "", "none", "1.2"};
|
||||
for (auto const& v : invalidDocument)
|
||||
@@ -120,26 +99,17 @@ public:
|
||||
json::Value jv;
|
||||
toJson(jv, v);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Invalid parameters.");
|
||||
}
|
||||
// missing document id
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, std::nullopt}}});
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "oracleMalformed");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcOracleMalformed);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Oracle request is malformed.");
|
||||
|
||||
// invalid owner
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{some, 1}}});
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcObjectNotFound);
|
||||
BEAST_EXPECT(
|
||||
ret[jss::error_message].asString() == "The requested object was not found.");
|
||||
// missing account
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{std::nullopt, 1}}});
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "oracleMalformed");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcOracleMalformed);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Oracle request is malformed.");
|
||||
|
||||
// oracles have wrong asset pair
|
||||
env.fund(XRP(1'000), owner);
|
||||
@@ -152,9 +122,6 @@ public:
|
||||
});
|
||||
ret = Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, oracle.documentID()}}});
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcObjectNotFound);
|
||||
BEAST_EXPECT(
|
||||
ret[jss::error_message].asString() == "The requested object was not found.");
|
||||
|
||||
// invalid trim value
|
||||
std::vector<AnyValue> const invalidTrim = {kNoneTag, 0, 26, -1, 1.2, "", "none", "1.2"};
|
||||
@@ -163,8 +130,6 @@ public:
|
||||
ret =
|
||||
Oracle::aggregatePrice(env, "XRP", "USD", {{{owner, oracle.documentID()}}}, v);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Invalid parameters.");
|
||||
}
|
||||
|
||||
// invalid time threshold value
|
||||
@@ -174,8 +139,6 @@ public:
|
||||
ret = Oracle::aggregatePrice(
|
||||
env, "XRP", "USD", {{{owner, oracle.documentID()}}}, std::nullopt, v);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Invalid parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +157,6 @@ public:
|
||||
}
|
||||
auto const ret = Oracle::aggregatePrice(env, "XRP", "USD", oracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "oracleMalformed");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcOracleMalformed);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "Oracle request is malformed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,8 +457,6 @@ public:
|
||||
// a nullptr dereference.
|
||||
auto const ret = Oracle::aggregatePrice(env, "XRP", "USD", oracles);
|
||||
BEAST_EXPECT(ret[jss::error].asString() == "objectNotFound");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcObjectNotFound);
|
||||
BEAST_EXPECT(ret[jss::error_message].asString() == "The requested object was not found.");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+343
-638
File diff suppressed because it is too large.
Load diff
@@ -209,12 +209,7 @@ public:
|
||||
params[jss::seed_hex] = strings.masterSeedHex;
|
||||
|
||||
// Secret fields are mutually exclusive.
|
||||
auto const result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] ==
|
||||
"Exactly one of the following must be specified: passphrase, seed or seed_hex");
|
||||
BEAST_EXPECT(containsError(walletPropose(params)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -228,8 +223,7 @@ public:
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = 20160506;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'passphrase', not string.");
|
||||
}
|
||||
|
||||
@@ -238,8 +232,7 @@ public:
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = json::ValueType::Object;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'seed', not string.");
|
||||
}
|
||||
|
||||
@@ -248,8 +241,7 @@ public:
|
||||
params[jss::key_type] = "ed25519";
|
||||
params[jss::seed_hex] = json::ValueType::Array;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'seed_hex', not string.");
|
||||
}
|
||||
|
||||
@@ -261,8 +253,7 @@ public:
|
||||
params[jss::seed_hex] = common::gMasterSeedHex;
|
||||
params[jss::seed] = common::gMasterSeed;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] ==
|
||||
"Exactly one of the following must be specified: passphrase, "
|
||||
@@ -275,8 +266,7 @@ public:
|
||||
params[jss::key_type] = "prime256v1";
|
||||
params[jss::passphrase] = common::gMasterKey;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -285,8 +275,7 @@ public:
|
||||
params[jss::key_type] = json::ValueType::Object;
|
||||
params[jss::seed_hex] = common::gMasterSeedHex;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'key_type', not string.");
|
||||
}
|
||||
|
||||
@@ -295,8 +284,7 @@ public:
|
||||
params[jss::key_type] = json::ValueType::Array;
|
||||
params[jss::seed] = common::gMasterSeed;
|
||||
auto result = walletPropose(params);
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(containsError(result));
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'key_type', not string.");
|
||||
}
|
||||
}
|
||||
@@ -414,9 +402,8 @@ public:
|
||||
json::Value error;
|
||||
params[jss::secret] = 314159265;
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'secret', not string.");
|
||||
}
|
||||
|
||||
@@ -427,9 +414,8 @@ public:
|
||||
params[jss::secret].append("array:0");
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'secret', not string.");
|
||||
}
|
||||
|
||||
@@ -441,9 +427,8 @@ public:
|
||||
params[jss::secret]["number"] = 702;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'secret', not string.");
|
||||
}
|
||||
|
||||
@@ -455,9 +440,8 @@ public:
|
||||
params[jss::secret] = common::gMasterSeed;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
error[jss::error_message] ==
|
||||
"The secret field is not allowed if key_type is used.");
|
||||
@@ -471,9 +455,8 @@ public:
|
||||
params[jss::passphrase] = common::gMasterKey;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'key_type'.");
|
||||
}
|
||||
|
||||
@@ -484,9 +467,8 @@ public:
|
||||
params[jss::seed_hex] = common::gMasterSeedHex;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'key_type', not string.");
|
||||
}
|
||||
|
||||
@@ -497,9 +479,8 @@ public:
|
||||
params[jss::seed] = common::gMasterSeed;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'key_type', not string.");
|
||||
}
|
||||
|
||||
@@ -511,9 +492,8 @@ public:
|
||||
params[jss::passphrase] = 1234567890;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'passphrase', not string.");
|
||||
}
|
||||
|
||||
@@ -524,9 +504,8 @@ public:
|
||||
params[jss::passphrase] = json::ValueType::Object;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'passphrase', not string.");
|
||||
}
|
||||
|
||||
@@ -537,9 +516,8 @@ public:
|
||||
params[jss::passphrase] = json::ValueType::Array;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'passphrase', not string.");
|
||||
}
|
||||
|
||||
@@ -550,9 +528,8 @@ public:
|
||||
params[jss::passphrase] = "";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -564,9 +541,8 @@ public:
|
||||
params[jss::seed] = 443556;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'seed', not string.");
|
||||
}
|
||||
|
||||
@@ -577,9 +553,8 @@ public:
|
||||
params[jss::seed] = json::ValueType::Object;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'seed', not string.");
|
||||
}
|
||||
|
||||
@@ -590,9 +565,8 @@ public:
|
||||
params[jss::seed] = json::ValueType::Array;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'seed', not string.");
|
||||
}
|
||||
|
||||
@@ -603,9 +577,8 @@ public:
|
||||
params[jss::seed] = "";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -616,9 +589,8 @@ public:
|
||||
params[jss::seed] = "s M V s h z D F p t Z E m h s";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -629,9 +601,8 @@ public:
|
||||
params[jss::seed] = "pnnjkbnobnml43679nbvjdsklnbjs";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -643,9 +614,8 @@ public:
|
||||
params[jss::seed_hex] = 443556;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'seed_hex', not string.");
|
||||
}
|
||||
|
||||
@@ -656,9 +626,8 @@ public:
|
||||
params[jss::seed_hex] = json::ValueType::Object;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'seed_hex', not string.");
|
||||
}
|
||||
|
||||
@@ -669,9 +638,8 @@ public:
|
||||
params[jss::seed_hex] = json::ValueType::Array;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Invalid field 'seed_hex', not string.");
|
||||
}
|
||||
|
||||
@@ -682,9 +650,8 @@ public:
|
||||
params[jss::seed_hex] = "";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -695,9 +662,8 @@ public:
|
||||
params[jss::seed_hex] = "A670A19B";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -708,9 +674,8 @@ public:
|
||||
params[jss::seed_hex] = common::gPassphrase;
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
@@ -721,9 +686,8 @@ public:
|
||||
params[jss::seed_hex] = "BE6A670A19B209E112146D0A7ED2AAD72567D0FC913";
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(!ret);
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(error[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
}
|
||||
@@ -759,8 +723,7 @@ public:
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(
|
||||
error[jss::error_message] == "Specified seed is for an Ed25519 wallet.");
|
||||
}
|
||||
@@ -791,8 +754,7 @@ public:
|
||||
|
||||
auto const ret = keypairForSignature(params, error);
|
||||
|
||||
BEAST_EXPECT(error[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(error[jss::error_code] == RpcBadSeed);
|
||||
BEAST_EXPECT(containsError(error));
|
||||
BEAST_EXPECT(
|
||||
error[jss::error_message] == "Specified seed is for an Ed25519 wallet.");
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
@@ -132,9 +131,8 @@ public:
|
||||
json::Value jvParams;
|
||||
jvParams[jss::limit] = "0"; // NOT an integer
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not integer.");
|
||||
}
|
||||
|
||||
@@ -143,9 +141,8 @@ public:
|
||||
json::Value jvParams;
|
||||
jvParams[jss::marker] = "NOT_A_MARKER";
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not valid.");
|
||||
}
|
||||
|
||||
@@ -154,9 +151,8 @@ public:
|
||||
json::Value jvParams;
|
||||
jvParams[jss::marker] = 1;
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not valid.");
|
||||
}
|
||||
|
||||
@@ -165,9 +161,8 @@ public:
|
||||
json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = 10u;
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
@@ -176,9 +171,8 @@ public:
|
||||
json::Value jvParams;
|
||||
jvParams[jss::binary] = "true";
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'binary', not boolean.");
|
||||
}
|
||||
}
|
||||
@@ -465,8 +459,8 @@ public:
|
||||
jvParams[jss::ledger_index] = "current";
|
||||
jvParams[jss::type] = "misspelling";
|
||||
auto const jrr = env.rpc("json", "ledger_data", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr.isMember("error"));
|
||||
BEAST_EXPECT(jrr["error"] == "invalidParams");
|
||||
BEAST_EXPECT(jrr["error_code"] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr["error_message"] == "Invalid field 'type'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
json::Value const& jv,
|
||||
std::string const& err,
|
||||
ErrorCodeI code,
|
||||
std::string const& msg,
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
@@ -166,14 +165,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
"Expected error " + err + ", received " + jv[jss::error].asString() + ", at line " +
|
||||
std::to_string(location.line()) + ", " + jv.toStyledString());
|
||||
}
|
||||
if (BEAST_EXPECT(jv.isMember(jss::error_code)))
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
jv[jss::error_code] == code,
|
||||
"Expected error code " + std::to_string(code) + ", received " +
|
||||
jv[jss::error_code].asString() + ", at line " +
|
||||
std::to_string(location.line()) + ", " + jv.toStyledString());
|
||||
}
|
||||
if (msg.empty())
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
@@ -349,21 +340,12 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result];
|
||||
if (apiVersion < 2u)
|
||||
{
|
||||
BEAST_EXPECTS(jrr[jss::status] == "error", std::to_string(location.line()));
|
||||
BEAST_EXPECTS(
|
||||
jrr[jss::error] == "unknownOption", std::to_string(location.line()));
|
||||
BEAST_EXPECTS(!jrr.isMember(jss::error_code), std::to_string(location.line()));
|
||||
BEAST_EXPECTS(
|
||||
jrr[jss::error_message].isNull(), std::to_string(location.line()));
|
||||
checkErrorValue(jrr, "unknownOption", "", location);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"No ledger_entry params provided.",
|
||||
location);
|
||||
jrr, "invalidParams", "No ledger_entry params provided.", location);
|
||||
}
|
||||
}
|
||||
auto const tryField = [&](json::Value fieldValue) -> void {
|
||||
@@ -372,7 +354,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result];
|
||||
auto const expectedErrMsg = rpc::expectedFieldMessage(
|
||||
fieldName, typeNameOverride.value_or(getTypeName(typeID)));
|
||||
checkErrorValue(jrr, expectedError, RpcInvalidParams, expectedErrMsg, location);
|
||||
checkErrorValue(jrr, expectedError, expectedErrMsg, location);
|
||||
};
|
||||
|
||||
auto const& badValues = getBadValues(typeID);
|
||||
@@ -405,21 +387,13 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
json::Value const jrr = env.rpc(
|
||||
apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::missingFieldMessage(fieldName.cStr()),
|
||||
location);
|
||||
jrr, "malformedRequest", rpc::missingFieldMessage(fieldName.cStr()), location);
|
||||
|
||||
correctRequest[parentFieldName][fieldName] = json::ValueType::Null;
|
||||
json::Value const jrr2 = env.rpc(
|
||||
apiVersion, "json", "ledger_entry", to_string(correctRequest))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr2,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::missingFieldMessage(fieldName.cStr()),
|
||||
location);
|
||||
jrr2, "malformedRequest", rpc::missingFieldMessage(fieldName.cStr()), location);
|
||||
}
|
||||
auto const tryField = [&](json::Value fieldValue) -> void {
|
||||
correctRequest[parentFieldName][fieldName] = fieldValue;
|
||||
@@ -429,7 +403,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
expectedError,
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(fieldName, getTypeName(typeID)),
|
||||
location);
|
||||
};
|
||||
@@ -524,7 +497,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AA";
|
||||
auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound");
|
||||
checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
|
||||
}
|
||||
{
|
||||
// Missing ledger_entry ledger_hash
|
||||
@@ -538,10 +511,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
json::Value const jrr = env.rpc(
|
||||
apiVersion, "json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'ledger_hash', not hex string.");
|
||||
jrr, "invalidParams", "Invalid field 'ledger_hash', not hex string.");
|
||||
};
|
||||
|
||||
auto const& badValues = getBadValues(typeId);
|
||||
@@ -560,7 +530,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
"00000000000000000000000000000000000000000000000000000000000000"
|
||||
"00";
|
||||
auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
|
||||
forAllApiVersions([&, this](unsigned apiVersion) {
|
||||
@@ -576,15 +546,11 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
|
||||
if (apiVersion < 2u)
|
||||
{
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error] == "unknownOption");
|
||||
BEAST_EXPECT(!jrr.isMember(jss::error_code));
|
||||
BEAST_EXPECT(jrr[jss::error_message].isNull());
|
||||
checkErrorValue(jrr, "unknownOption", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
checkErrorValue(
|
||||
jrr, "invalidParams", RpcInvalidParams, "No ledger_entry params provided.");
|
||||
checkErrorValue(jrr, "invalidParams", "No ledger_entry params provided.");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -689,7 +655,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -888,7 +854,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Request an index that is not a check. We'll use alice's
|
||||
@@ -906,8 +872,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr, "unexpectedLedgerType", RpcUnexpectedLedgerType, "Unexpected ledger type.");
|
||||
checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
|
||||
}
|
||||
{
|
||||
// Check malformed cases
|
||||
@@ -966,7 +931,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
env,
|
||||
"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
|
||||
"E4");
|
||||
checkErrorValue(jv[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jv[jss::result], "entryNotFound", "Entry not found.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1161,8 +1126,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
auto const expectedErrMsg = fieldValue.isNull()
|
||||
? rpc::missingFieldMessage(jss::issuer.cStr())
|
||||
: rpc::expectedFieldMessage(jss::issuer, "AccountID");
|
||||
checkErrorValue(
|
||||
jrr, "malformedAuthorizedCredentials", RpcInvalidParams, expectedErrMsg);
|
||||
checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg);
|
||||
};
|
||||
|
||||
auto const& badValues = getBadValues(FieldType::AccountField);
|
||||
@@ -1191,7 +1155,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr[jss::result],
|
||||
"malformedAuthorizedCredentials",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::authorized_credentials, "array"));
|
||||
}
|
||||
|
||||
@@ -1214,8 +1177,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
auto const expectedErrMsg = fieldValue.isNull()
|
||||
? rpc::missingFieldMessage(jss::credential_type.cStr())
|
||||
: rpc::expectedFieldMessage(jss::credential_type, "hex string");
|
||||
checkErrorValue(
|
||||
jrr, "malformedAuthorizedCredentials", RpcInvalidParams, expectedErrMsg);
|
||||
checkErrorValue(jrr, "malformedAuthorizedCredentials", expectedErrMsg);
|
||||
};
|
||||
|
||||
auto const& badValues = getBadValues(FieldType::BlobField);
|
||||
@@ -1245,7 +1207,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr[jss::result],
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Must have exactly one of `authorized` and "
|
||||
"`authorized_credentials`.");
|
||||
}
|
||||
@@ -1278,7 +1239,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr[jss::result],
|
||||
"malformedAuthorizedCredentials",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'authorized_credentials', not array of objects.");
|
||||
}
|
||||
|
||||
@@ -1297,7 +1257,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr[jss::result],
|
||||
"malformedAuthorizedCredentials",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'authorized_credentials', not array of objects.");
|
||||
}
|
||||
|
||||
@@ -1312,7 +1271,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr[jss::result],
|
||||
"malformedAuthorizedCredentials",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'authorized_credentials', array empty.");
|
||||
}
|
||||
|
||||
@@ -1350,7 +1308,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr[jss::result],
|
||||
"malformedAuthorizedCredentials",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'authorized_credentials', array too long.");
|
||||
}
|
||||
}
|
||||
@@ -1486,10 +1443,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Must have exactly one of `owner` and `dir_root` fields.");
|
||||
jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
|
||||
}
|
||||
{
|
||||
// Incomplete directory object. Missing both dir_root and owner.
|
||||
@@ -1500,10 +1454,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Must have exactly one of `owner` and `dir_root` fields.");
|
||||
jrr, "malformedRequest", "Must have exactly one of `owner` and `dir_root` fields.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1666,7 +1617,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::nft_offer][jss::seq] = offerSeq + 1000;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
|
||||
// negative tests
|
||||
@@ -1884,7 +1835,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Request an index that is not a payment channel.
|
||||
@@ -1893,7 +1844,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1968,7 +1919,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'accounts', not length-2 array of "
|
||||
"Accounts.");
|
||||
}
|
||||
@@ -1987,7 +1937,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'accounts', not length-2 array of "
|
||||
"Accounts.");
|
||||
}
|
||||
@@ -2008,7 +1957,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedAddress",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::accounts, "array of Accounts"));
|
||||
}
|
||||
|
||||
@@ -2024,7 +1972,6 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"malformedAddress",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::accounts, "array of Accounts"));
|
||||
}
|
||||
};
|
||||
@@ -2047,8 +1994,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr, "malformedRequest", RpcInvalidParams, "Cannot have a trustline to self.");
|
||||
checkErrorValue(jrr, "malformedRequest", "Cannot have a trustline to self.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2102,7 +2048,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Request an index that is not a signer list.
|
||||
@@ -2111,7 +2057,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2202,7 +2148,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// First real ticket requested by index.
|
||||
@@ -2238,7 +2184,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Request a ticket using an account root entry.
|
||||
@@ -2247,8 +2193,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr, "unexpectedLedgerType", RpcUnexpectedLedgerType, "Unexpected ledger type.");
|
||||
checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2316,7 +2261,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::transaction_proposal][jss::ticket_seq] = ticketSeq + 1;
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Lookup by an index of the wrong entry type.
|
||||
@@ -2324,8 +2269,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::transaction_proposal] = to_string(keylet::account(target).key);
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr, "unexpectedLedgerType", RpcUnexpectedLedgerType, "Unexpected ledger type.");
|
||||
checkErrorValue(jrr, "unexpectedLedgerType", "Unexpected ledger type.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2389,7 +2333,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Malformed DID index
|
||||
@@ -2511,7 +2455,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Request the MPToken using its owner + mptIssuanceID.
|
||||
@@ -2534,7 +2478,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
json::Value const jrr =
|
||||
env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// Malformed MPTIssuance index
|
||||
@@ -2602,8 +2546,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
"12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
|
||||
"DE";
|
||||
auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkErrorValue(
|
||||
jrr[jss::result], "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr[jss::result], "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// test basic malformed scenarios
|
||||
@@ -2649,9 +2592,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
auto checkResult = [&](bool good,
|
||||
json::Value const& jv,
|
||||
json::StaticString const& expectedType,
|
||||
std::optional<std::string> const& expectedError = {},
|
||||
ErrorCodeI expectedCode = RpcEntryNotFound,
|
||||
std::string const& expectedMessage = "Entry not found.") {
|
||||
std::optional<std::string> const& expectedError = {}) {
|
||||
if (good)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
@@ -2666,10 +2607,9 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
jv.isObject() && jv.isMember(jss::result) &&
|
||||
jv[jss::result].isMember(jss::error) &&
|
||||
!jv[jss::result].isMember(jss::node) &&
|
||||
jv[jss::result][jss::error] == expectedError.value_or("entryNotFound") &&
|
||||
jv[jss::result][jss::error_code] == expectedCode &&
|
||||
jv[jss::result][jss::error_message] == expectedMessage,
|
||||
jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"),
|
||||
to_string(jv));
|
||||
}
|
||||
};
|
||||
@@ -2700,13 +2640,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[field] = json::ValueType::Null;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
expectedType,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(field, "hex string"));
|
||||
checkResult(false, jv, expectedType, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2716,13 +2650,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[field] = "arbitrary string";
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
expectedType,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(field, "hex string"));
|
||||
checkResult(false, jv, expectedType, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2732,13 +2660,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[field] = false;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
expectedType,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(field, "true"));
|
||||
checkResult(false, jv, expectedType, "invalidParams");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2750,8 +2672,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[field] = badKey;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false, jv, expectedType, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkResult(false, jv, expectedType, "entryNotFound");
|
||||
BEAST_EXPECTS(jv[jss::result][jss::index] == badKey, to_string(jv));
|
||||
}
|
||||
|
||||
@@ -2762,13 +2683,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::index] = field;
|
||||
params[jss::api_version] = 2;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
expectedType,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::index, "hex string"));
|
||||
checkResult(false, jv, expectedType, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2807,13 +2722,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::index] = field;
|
||||
params[jss::api_version] = 2;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
expectedType,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::index, "hex string"));
|
||||
checkResult(false, jv, expectedType, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2878,9 +2787,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
auto checkResult = [&](bool good,
|
||||
json::Value const& jv,
|
||||
int expectedCount,
|
||||
std::optional<std::string> const& expectedError = {},
|
||||
ErrorCodeI expectedCode = RpcEntryNotFound,
|
||||
std::string const& expectedMessage = "Entry not found.") {
|
||||
std::optional<std::string> const& expectedError = {}) {
|
||||
if (good)
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
@@ -2900,10 +2807,9 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
jv.isObject() && jv.isMember(jss::result) &&
|
||||
jv[jss::result].isMember(jss::error) &&
|
||||
!jv[jss::result].isMember(jss::node) &&
|
||||
jv[jss::result][jss::error] == expectedError.value_or("entryNotFound") &&
|
||||
jv[jss::result][jss::error_code] == expectedCode &&
|
||||
jv[jss::result][jss::error_message] == expectedMessage,
|
||||
jv[jss::result][jss::error] == expectedError.value_or("entryNotFound"),
|
||||
to_string(jv));
|
||||
}
|
||||
};
|
||||
@@ -2933,13 +2839,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::hashes] = json::ValueType::Null;
|
||||
auto jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
0,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::hashes, "hex string"));
|
||||
checkResult(false, jv, 0, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2949,13 +2849,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::hashes] = "arbitrary string";
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
0,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::hashes, "hex string"));
|
||||
checkResult(false, jv, 0, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2965,13 +2859,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::hashes] = "10";
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
0,
|
||||
"malformedRequest",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::hashes, "hex string"));
|
||||
checkResult(false, jv, 0, "malformedRequest");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2981,13 +2869,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::hashes] = false;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false,
|
||||
jv,
|
||||
0,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
rpc::expectedFieldMessage(jss::hashes, "true"));
|
||||
checkResult(false, jv, 0, "invalidParams");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -2997,7 +2879,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::hashes] = -1;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(false, jv, 0, "internal", RpcInternal, "Internal error.");
|
||||
checkResult(false, jv, 0, "internal");
|
||||
BEAST_EXPECT(!jv[jss::result].isMember(jss::index));
|
||||
}
|
||||
|
||||
@@ -3008,8 +2890,7 @@ class LedgerEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_index] = jss::validated;
|
||||
params[jss::hashes] = badKey;
|
||||
auto const jv = env.rpc("json", "ledger_entry", to_string(params));
|
||||
checkResult(
|
||||
false, jv, 0, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkResult(false, jv, 0, "entryNotFound");
|
||||
BEAST_EXPECT(jv[jss::result][jss::index] == badKey);
|
||||
}
|
||||
|
||||
@@ -3147,18 +3028,12 @@ class LedgerEntry_XChain_test : public beast::unit_test::Suite,
|
||||
public test::jtx::XChainBridgeObjects
|
||||
{
|
||||
void
|
||||
checkErrorValue(
|
||||
json::Value const& jv,
|
||||
std::string const& err,
|
||||
ErrorCodeI code,
|
||||
std::string const& msg)
|
||||
checkErrorValue(json::Value const& jv, std::string const& err, std::string const& msg)
|
||||
{
|
||||
if (BEAST_EXPECT(jv.isMember(jss::status)))
|
||||
BEAST_EXPECT(jv[jss::status] == "error");
|
||||
if (BEAST_EXPECT(jv.isMember(jss::error)))
|
||||
BEAST_EXPECT(jv[jss::error] == err);
|
||||
if (BEAST_EXPECT(jv.isMember(jss::error_code)))
|
||||
BEAST_EXPECT(jv[jss::error_code] == code);
|
||||
if (msg.empty())
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
@@ -3235,7 +3110,7 @@ class LedgerEntry_XChain_test : public beast::unit_test::Suite,
|
||||
json::Value const jrr =
|
||||
mcEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
{
|
||||
// create two claim ids and verify that the bridge counter was
|
||||
@@ -3422,7 +3297,7 @@ class LedgerEntry_XChain_test : public beast::unit_test::Suite,
|
||||
[jss::xchain_owned_create_account_claim_id] = 1;
|
||||
json::Value const jrr =
|
||||
scEnv.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", RpcEntryNotFound, "Entry not found.");
|
||||
checkErrorValue(jrr, "entryNotFound", "Entry not found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -54,10 +53,8 @@ class LedgerHeader_test : public beast::unit_test::Suite
|
||||
json::Value params{json::ValueType::Object};
|
||||
params[jss::api_version] = 2;
|
||||
auto const result = env.client().invoke("ledger_header", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "unknownCmd");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcUnknownCommand);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Unknown method.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -37,18 +37,12 @@ namespace xrpl::test {
|
||||
class LedgerRPC_test : public beast::unit_test::Suite
|
||||
{
|
||||
void
|
||||
checkErrorValue(
|
||||
json::Value const& jv,
|
||||
std::string const& err,
|
||||
ErrorCodeI code,
|
||||
std::string const& msg)
|
||||
checkErrorValue(json::Value const& jv, std::string const& err, std::string const& msg)
|
||||
{
|
||||
if (BEAST_EXPECT(jv.isMember(jss::status)))
|
||||
BEAST_EXPECT(jv[jss::status] == "error");
|
||||
if (BEAST_EXPECT(jv.isMember(jss::error)))
|
||||
BEAST_EXPECT(jv[jss::error] == err);
|
||||
if (BEAST_EXPECT(jv.isMember(jss::error_code)))
|
||||
BEAST_EXPECT(jv[jss::error_code] == code);
|
||||
if (msg.empty())
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
@@ -145,10 +139,7 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_index] = "potato";
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'ledger_index', not string or number.");
|
||||
jrr, "invalidParams", "Invalid field 'ledger_index', not string or number.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -157,10 +148,7 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_index] = -1;
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr,
|
||||
"invalidParams",
|
||||
RpcInvalidParams,
|
||||
"Invalid field 'ledger_index', not string or number.");
|
||||
jrr, "invalidParams", "Invalid field 'ledger_index', not string or number.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -168,13 +156,13 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = 10u;
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound");
|
||||
checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
// unrecognized string arg -- error
|
||||
auto const jrr = env.rpc("ledger", "arbitrary_text")[jss::result];
|
||||
checkErrorValue(jrr, "lgrNotFound", RpcLgrNotFound, "ledgerNotFound");
|
||||
checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -183,21 +171,20 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_index] = "validated";
|
||||
jvParams[jss::queue] = true;
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "invalidParams", RpcInvalidParams, "Invalid parameters.");
|
||||
checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
|
||||
}
|
||||
|
||||
{
|
||||
// Request a ledger with a very large (double) sequence.
|
||||
auto const ret = env.rpc("json", "ledger", "{ \"ledger_index\" : 2e15 }");
|
||||
BEAST_EXPECT(ret[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(ret[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(rpc::containsError(ret));
|
||||
BEAST_EXPECT(ret[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
{
|
||||
// Request a ledger with very large (integer) sequence.
|
||||
auto const ret = env.rpc("json", "ledger", "{ \"ledger_index\" : 1000000000000000 }");
|
||||
checkErrorValue(ret, "invalidParams", RpcInvalidParams, "Invalid parameters.");
|
||||
checkErrorValue(ret, "invalidParams", "Invalid parameters.");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -206,7 +193,7 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
json::Value jvParams;
|
||||
jvParams[field] = "blah";
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "invalidParams", RpcInvalidParams, "Invalid parameters.");
|
||||
checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
|
||||
};
|
||||
testBooleanField(jss::full);
|
||||
testBooleanField(jss::accounts);
|
||||
@@ -268,8 +255,7 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_index] = 1u;
|
||||
jvParams[jss::full] = true;
|
||||
auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(
|
||||
jrr, "noPermission", RpcNoPermission, "You don't have permission for this command.");
|
||||
checkErrorValue(jrr, "noPermission", "You don't have permission for this command.");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -436,7 +422,6 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger] = "invalid";
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'ledger', not string or number.");
|
||||
|
||||
@@ -451,7 +436,6 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger] = 20;
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
@@ -471,14 +455,12 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_hash] = "DEADBEEF" + hash3;
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
|
||||
|
||||
// request with non-string ledger_hash
|
||||
jvParams[jss::ledger_hash] = 2;
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
|
||||
|
||||
// malformed (non hex) hash
|
||||
@@ -487,7 +469,6 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
"7F2775F2F7485BB37307984C3C0F2340";
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
|
||||
|
||||
// properly formed, but just doesn't exist
|
||||
@@ -496,7 +477,6 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
"5A75FD71FC3FD6F36FEB803C5F1D812D";
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
@@ -526,7 +506,6 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_index] = "invalid";
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::error_message] == "Invalid field 'ledger_index', not string or number.");
|
||||
|
||||
@@ -545,7 +524,6 @@ class LedgerRPC_test : public beast::unit_test::Suite
|
||||
jvParams[jss::ledger_index] = 7;
|
||||
jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,24 +43,21 @@ public:
|
||||
// arbitrary text is converted to 0.
|
||||
auto const result = env.rpc("ledger_request", "arbitrary_text");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error] == "invalidParams" &&
|
||||
result[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
rpc::containsError(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] == "Ledger index too small");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "-1");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error] == "invalidParams" &&
|
||||
result[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
rpc::containsError(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] == "Ledger index too small");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "0");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error] == "invalidParams" &&
|
||||
result[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
rpc::containsError(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] == "Ledger index too small");
|
||||
}
|
||||
|
||||
@@ -115,8 +112,7 @@ public:
|
||||
auto const result = env.rpc("ledger_request", ledgerHash);
|
||||
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error] == "invalidParams" &&
|
||||
result[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
rpc::containsError(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Invalid field 'ledger_hash', not hex string.");
|
||||
}
|
||||
@@ -134,16 +130,14 @@ public:
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "4");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error] == "invalidParams" &&
|
||||
result[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
rpc::containsError(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] == "Ledger index too large");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "5");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::error] == "invalidParams" &&
|
||||
result[jss::result][jss::error_code] == RpcInvalidParams &&
|
||||
rpc::containsError(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] == "Ledger index too large");
|
||||
}
|
||||
}
|
||||
@@ -242,9 +236,8 @@ public:
|
||||
"8FE8592EF22FBC2E8C774C7A1ED76AA3FCE64BED17D748CBA9AFDF7072FE36C7");
|
||||
|
||||
result = env.rpc("ledger_request", "6")[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error_message] == "Ledger index too large");
|
||||
}
|
||||
|
||||
@@ -266,9 +259,8 @@ public:
|
||||
jvParams[jss::ledger_index] = "1";
|
||||
auto const result =
|
||||
env.rpc("json", "ledger_request", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] ==
|
||||
"Exactly one of 'ledger_hash' or 'ledger_index' can be "
|
||||
@@ -280,9 +272,8 @@ public:
|
||||
jvParams[jss::ledger_index] = "index";
|
||||
auto const result =
|
||||
env.rpc("json", "ledger_request", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'ledger_index', not number.");
|
||||
}
|
||||
|
||||
@@ -294,13 +285,11 @@ public:
|
||||
if (apiVersion == 1)
|
||||
{
|
||||
BEAST_EXPECT(result[jss::error] == "noCurrent");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcNoCurrent);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Current ledger is unavailable.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(result[jss::error] == "notSynced");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcNotSynced);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Not synced to the network.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <memory>
|
||||
@@ -28,8 +27,6 @@ public:
|
||||
{
|
||||
// manifest with no public key
|
||||
auto const info = env.rpc("json", "manifest", "{ }");
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Missing field 'public_key'.");
|
||||
}
|
||||
{
|
||||
@@ -39,8 +36,6 @@ public:
|
||||
"manifest",
|
||||
"{ \"public_key\": "
|
||||
"\"abcdef12345\"}");
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -50,7 +49,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
{ // missing account field
|
||||
auto const result = env.rpc("json", "noripple_check", "{}")[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Missing field 'account'.");
|
||||
}
|
||||
|
||||
@@ -59,7 +57,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::account] = alice.human();
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Missing field 'role'.");
|
||||
}
|
||||
|
||||
@@ -71,7 +68,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::role] = "user";
|
||||
auto jrr = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'account'.");
|
||||
};
|
||||
|
||||
@@ -89,7 +85,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::role] = "not_a_role";
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'role'.");
|
||||
}
|
||||
|
||||
@@ -100,7 +95,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::limit] = -1;
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] == "Invalid field 'limit', not unsigned integer.");
|
||||
}
|
||||
@@ -112,7 +106,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_hash] = 1;
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] == "Invalid field 'ledger_hash', not hex string.");
|
||||
}
|
||||
@@ -124,7 +117,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::ledger] = "current";
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcActNotFound);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Account not found.");
|
||||
}
|
||||
|
||||
@@ -137,7 +129,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::transactions] = true;
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
|
||||
// The changelog promises malformed-account responses carry
|
||||
// neither `transactions` nor any ledger metadata.
|
||||
@@ -156,7 +147,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::ledger_hash] = "ABCDEF";
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] ==
|
||||
"Exactly one of 'ledger', 'ledger_hash', or 'ledger_index' can "
|
||||
@@ -171,7 +161,6 @@ class NoRippleCheck_test : public beast::unit_test::Suite
|
||||
params[jss::ledger] = json::ValueType::Object;
|
||||
auto const result = env.rpc("json", "noripple_check", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] == "Invalid field 'ledger', not string or number.");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -257,9 +256,6 @@ public:
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(lines[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
lines[jss::result][jss::error_message] == "Invalid field 'transactions'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
@@ -35,7 +34,6 @@ class OwnerInfo_test : public beast::unit_test::Suite
|
||||
{ // missing account field
|
||||
auto const result = env.rpc("json", "owner_info", "{}")[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Missing field 'account'.");
|
||||
}
|
||||
|
||||
@@ -46,10 +44,8 @@ class OwnerInfo_test : public beast::unit_test::Suite
|
||||
if (BEAST_EXPECT(result.isMember(jss::accepted) && result.isMember(jss::current)))
|
||||
{
|
||||
BEAST_EXPECT(result[jss::accepted][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(result[jss::accepted][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(result[jss::accepted][jss::error_message] == "Account malformed.");
|
||||
BEAST_EXPECT(result[jss::current][jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(result[jss::current][jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(result[jss::current][jss::error_message] == "Account malformed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2157,6 +2157,53 @@ static RPCCallTestData const kRpcCallTestArray[] = {
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel_authorize: token amount.",
|
||||
__LINE__,
|
||||
{
|
||||
"channel_authorize",
|
||||
"secret_can_be_anything",
|
||||
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
|
||||
R"({"currency":"USD","issuer":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh","value":"10.5"})",
|
||||
},
|
||||
RPCCallTestData::Exception::NoException,
|
||||
R"({
|
||||
"method" : "channel_authorize",
|
||||
"params" : [
|
||||
{
|
||||
"api_version" : %API_VER%,
|
||||
"amount" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
||||
"value" : "10.5"
|
||||
},
|
||||
"channel_id" : "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
|
||||
"secret" : "secret_can_be_anything"
|
||||
}
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel_authorize: amount JSON not an object.",
|
||||
__LINE__,
|
||||
{
|
||||
"channel_authorize",
|
||||
"secret_can_be_anything",
|
||||
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
|
||||
R"(["10","USD"])",
|
||||
},
|
||||
RPCCallTestData::Exception::NoException,
|
||||
R"({
|
||||
"method" : "channel_authorize",
|
||||
"params" : [
|
||||
{
|
||||
"error" : "channelAmtMalformed",
|
||||
"error_code" : 43,
|
||||
"error_message" : "Payment channel amount is malformed."
|
||||
}
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel_authorize: too few arguments.",
|
||||
__LINE__,
|
||||
@@ -2378,6 +2425,33 @@ static RPCCallTestData const kRpcCallTestArray[] = {
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel_verify: token amount.",
|
||||
__LINE__,
|
||||
{
|
||||
"channel_verify",
|
||||
"aB4BXXLuPu8DpVuyq1DBiu3SrPdtK9AYZisKhu8mvkoiUD8J9Gov",
|
||||
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
|
||||
R"({"mpt_issuance_id":"00000001A407AF5856CCF3C42619DAA925813FC955C72983","value":"10"})",
|
||||
"DEADBEEF",
|
||||
},
|
||||
RPCCallTestData::Exception::NoException,
|
||||
R"({
|
||||
"method" : "channel_verify",
|
||||
"params" : [
|
||||
{
|
||||
"api_version" : %API_VER%,
|
||||
"amount" : {
|
||||
"mpt_issuance_id" : "00000001A407AF5856CCF3C42619DAA925813FC955C72983",
|
||||
"value" : "10"
|
||||
},
|
||||
"channel_id" : "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
|
||||
"public_key" : "aB4BXXLuPu8DpVuyq1DBiu3SrPdtK9AYZisKhu8mvkoiUD8J9Gov",
|
||||
"signature" : "DEADBEEF"
|
||||
}
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"channel_verify: too few arguments.",
|
||||
__LINE__,
|
||||
|
||||
@@ -183,8 +183,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
// No params
|
||||
json::Value const params = json::ValueType::Object;
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"Neither `tx_blob` nor `tx_json` included.");
|
||||
@@ -196,8 +194,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_blob] = "1200";
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"Can only include one of `tx_blob` and `tx_json`.");
|
||||
@@ -208,8 +204,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_blob] = "1200";
|
||||
params[jss::binary] = "100";
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'binary'.");
|
||||
}
|
||||
{
|
||||
@@ -218,8 +212,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_blob] = "12";
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx_blob'.");
|
||||
}
|
||||
{
|
||||
@@ -228,8 +220,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = json::ValueType::Object;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Missing field 'tx.TransactionType'.");
|
||||
}
|
||||
@@ -241,8 +231,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Missing field 'tx.Account'.");
|
||||
}
|
||||
{
|
||||
@@ -251,8 +239,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_blob] = "";
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx_blob'.");
|
||||
}
|
||||
{
|
||||
@@ -261,8 +247,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_blob] = 1.1;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx_blob'.");
|
||||
}
|
||||
{
|
||||
@@ -271,8 +255,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = "";
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Invalid field 'tx_json', not object.");
|
||||
}
|
||||
@@ -285,8 +267,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
txJson[jss::Account] = env.master.human();
|
||||
params[jss::tx_json] = txJson;
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'seed'.");
|
||||
}
|
||||
{
|
||||
@@ -298,8 +278,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
txJson[jss::Account] = env.master.human();
|
||||
params[jss::tx_json] = txJson;
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'secret'.");
|
||||
}
|
||||
{
|
||||
@@ -311,8 +289,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
txJson[jss::Account] = env.master.human();
|
||||
params[jss::tx_json] = txJson;
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'seed_hex'.");
|
||||
}
|
||||
{
|
||||
@@ -324,8 +300,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
txJson[jss::Account] = env.master.human();
|
||||
params[jss::tx_json] = txJson;
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'passphrase'.");
|
||||
}
|
||||
{
|
||||
@@ -337,9 +311,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(!resp[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_exception] ==
|
||||
"Field 'Destination' is required but missing.");
|
||||
@@ -356,7 +327,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
BEAST_EXPECTS(
|
||||
resp[jss::result][jss::error] == "srcActMalformed",
|
||||
resp[jss::result][jss::error].toStyledString());
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcSrcActMalformed);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx.Account'.");
|
||||
}
|
||||
{
|
||||
@@ -368,8 +338,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "srcActNotFound");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcSrcActNotFound);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Source account not found.");
|
||||
}
|
||||
{
|
||||
@@ -382,8 +350,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx.Signers'.");
|
||||
}
|
||||
{
|
||||
@@ -397,8 +363,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Invalid field 'tx.Signers[0]'.");
|
||||
}
|
||||
{
|
||||
@@ -411,8 +375,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"Invalid field 'SponsorSignature', not object.");
|
||||
@@ -429,8 +391,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECTS(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"Invalid field 'tx.SponsorSignature.Signers'.",
|
||||
@@ -446,8 +406,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Field 'tx_json.foo' is unknown.");
|
||||
}
|
||||
@@ -457,8 +415,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
txJson[jss::TransactionType] = jss::AccountSet;
|
||||
txJson[jss::Account] = alice.human();
|
||||
auto const resp = env.rpc("simulate", to_string(txJson), "1");
|
||||
BEAST_EXPECT(resp[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(resp[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
{
|
||||
@@ -471,8 +427,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "transactionSigned");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcTxSigned);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Transaction should not be signed.");
|
||||
}
|
||||
@@ -495,8 +449,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "transactionSigned");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcTxSigned);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] == "Transaction should not be signed.");
|
||||
}
|
||||
@@ -510,8 +462,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = txJson;
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::result][jss::error_message] ==
|
||||
"tfInnerBatchTxn flag is not allowed on top-level transactions.");
|
||||
@@ -580,7 +530,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
{
|
||||
auto const resp = simulate(env, tx);
|
||||
BEAST_EXPECT(resp[jss::error] == "noPermission");
|
||||
BEAST_EXPECT(resp[jss::error_code] == RpcNoPermission);
|
||||
BEAST_EXPECT(
|
||||
resp[jss::error_message] ==
|
||||
"WASM transactions are only supported in admin mode.");
|
||||
@@ -610,10 +559,7 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// Pin the reference fee so the asserted error message doesn't depend on
|
||||
// UNIT_TEST_REFERENCE_FEE.
|
||||
Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
|
||||
cfg->fees.referenceFee = 10;
|
||||
cfg->section(Sections::kTransactionQueue).set(Keys::kMinimumTxnInLedgerStandalone, "3");
|
||||
return cfg;
|
||||
}));
|
||||
@@ -633,10 +579,11 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
auto const result = resp[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "highFee");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcHighFee);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] == "Fee of 8889 exceeds the requested tx limit of 100");
|
||||
if (BEAST_EXPECT(result.isMember(jss::error)))
|
||||
{
|
||||
BEAST_EXPECT(result[jss::error] == "highFee");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcHighFee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,7 +613,6 @@ class Simulate_test : public beast::unit_test::Suite
|
||||
params[jss::tx_json] = jt.jv;
|
||||
auto const resp = env.rpc("json", "simulate", to_string(params));
|
||||
BEAST_EXPECT(resp[jss::result][jss::error] == "notImpl");
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_code] == RpcNotImpl);
|
||||
BEAST_EXPECT(resp[jss::result][jss::error_message] == "Not implemented.");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Seed.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
@@ -40,7 +39,6 @@ public:
|
||||
params[jss::fail_hard] = param;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'fail_hard', not boolean.");
|
||||
}
|
||||
|
||||
@@ -52,7 +50,6 @@ public:
|
||||
params[jss::fail_hard] = param;
|
||||
auto const jrr = env.rpc("json", "submit", to_string(params))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'fail_hard', not boolean.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/json/to_string.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -622,7 +621,6 @@ public:
|
||||
{
|
||||
auto const jr = env.rpc("json", method, "{}")[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -635,7 +633,6 @@ public:
|
||||
if (subscribe)
|
||||
{
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Failed to parse url.");
|
||||
}
|
||||
// else TODO: why isn't this an error for unsubscribe ?
|
||||
@@ -649,7 +646,6 @@ public:
|
||||
if (subscribe)
|
||||
{
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Only http and https is supported.");
|
||||
}
|
||||
}
|
||||
@@ -660,7 +656,6 @@ public:
|
||||
jv[jss::url] = "no-url";
|
||||
auto const jr = envNonadmin.rpc("json", method, to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "noPermission");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcNoPermission);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "You don't have permission for this command.");
|
||||
}
|
||||
|
||||
@@ -682,7 +677,6 @@ public:
|
||||
jv[f] = nonArray;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -691,7 +685,6 @@ public:
|
||||
jv[f] = json::ValueType::Array;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "actMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
}
|
||||
@@ -702,7 +695,6 @@ public:
|
||||
jv[jss::mpt_issuances] = nonArray;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -712,7 +704,6 @@ public:
|
||||
jv[jss::mpt_issuances][0u] = 1;
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -722,7 +713,6 @@ public:
|
||||
jv[jss::mpt_issuances][0u] = "not-an-mpt-issuance-id";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -732,7 +722,6 @@ public:
|
||||
jv[jss::mpt_issuances][0u] = "0123456789ABCDEF";
|
||||
auto jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -742,7 +731,6 @@ public:
|
||||
jv[jss::books] = nonArray;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -752,7 +740,6 @@ public:
|
||||
jv[jss::books][0u] = 1;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -765,7 +752,6 @@ public:
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
|
||||
BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcSrcCurMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source currency is malformed.");
|
||||
}
|
||||
|
||||
@@ -778,7 +764,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays][jss::currency] = "ZZZZ";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcCurMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcSrcCurMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source currency is malformed.");
|
||||
}
|
||||
|
||||
@@ -792,7 +777,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcSrcIsrMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source issuer is malformed.");
|
||||
}
|
||||
|
||||
@@ -806,7 +790,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker_pays][jss::issuer] = Account{"gateway"}.human() + "DEAD";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "srcIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcSrcIsrMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Source issuer is malformed.");
|
||||
}
|
||||
|
||||
@@ -820,7 +803,6 @@ public:
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
// NOTE: this error is slightly incongruous with the equivalent source currency error
|
||||
BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcDstAmtMalformed);
|
||||
BEAST_EXPECT(
|
||||
jr[jss::error_message] == "Destination amount/currency/issuer is malformed.");
|
||||
}
|
||||
@@ -836,7 +818,6 @@ public:
|
||||
// NOTE: this error is slightly incongruous with the
|
||||
// equivalent source currency error
|
||||
BEAST_EXPECT(jr[jss::error] == "dstAmtMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcDstAmtMalformed);
|
||||
BEAST_EXPECT(
|
||||
jr[jss::error_message] == "Destination amount/currency/issuer is malformed.");
|
||||
}
|
||||
@@ -851,7 +832,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcDstIsrMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Destination issuer is malformed.");
|
||||
}
|
||||
|
||||
@@ -865,7 +845,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker_gets][jss::issuer] = Account{"gateway"}.human() + "DEAD";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "dstIsrMalformed");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcDstIsrMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Destination issuer is malformed.");
|
||||
}
|
||||
|
||||
@@ -879,7 +858,6 @@ public:
|
||||
Account{"gateway"}["USD"](1).value().getJson(JsonOptions::Values::IncludeDate);
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "badMarket");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcBadMarket);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "No such market.");
|
||||
}
|
||||
|
||||
@@ -889,7 +867,6 @@ public:
|
||||
jv[jss::streams] = nonArray;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
@@ -899,7 +876,6 @@ public:
|
||||
jv[jss::streams][0u] = 1;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "malformedStream");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcStreamMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
|
||||
}
|
||||
|
||||
@@ -909,7 +885,6 @@ public:
|
||||
jv[jss::streams][0u] = "not_a_stream";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "malformedStream");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcStreamMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Stream malformed.");
|
||||
}
|
||||
|
||||
@@ -926,7 +901,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker] = 1;
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECTS(jr[jss::error] == "actMalformed", jr.toStyledString());
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
|
||||
@@ -941,7 +915,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker] = "not_an_account";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECTS(jr[jss::error] == "actMalformed", jr.toStyledString());
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
|
||||
@@ -956,7 +929,6 @@ public:
|
||||
jv[jss::books][0u][jss::taker] = Account{"alice"}.human() + "DEAD";
|
||||
auto const jr = wsc->invoke(method, jv)[jss::result];
|
||||
BEAST_EXPECTS(jr[jss::error] == "actMalformed", jr.toStyledString());
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcActMalformed);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Account malformed.");
|
||||
}
|
||||
}
|
||||
@@ -1812,7 +1784,6 @@ public:
|
||||
auto const jr =
|
||||
wsc->invoke("subscribe", accountsRequest(makeAccountStrings(6)))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
}
|
||||
|
||||
@@ -1895,7 +1866,6 @@ public:
|
||||
{
|
||||
auto const jr = wsc->invoke("subscribe", req)[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
}
|
||||
|
||||
@@ -1946,7 +1916,6 @@ public:
|
||||
auto const jr =
|
||||
wsc->invoke("subscribe", accountHistoryRequest(alice.human()))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(jr[jss::error_message] != "Too many subscriptions for this connection.");
|
||||
}
|
||||
@@ -1982,7 +1951,6 @@ public:
|
||||
auto const jr =
|
||||
wsc->invoke("subscribe", accountHistoryRequest(bob.human()))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
}
|
||||
}
|
||||
@@ -2006,7 +1974,6 @@ public:
|
||||
auto const jr = wsc->invoke(
|
||||
"subscribe", mptIssuancesRequest(makeMPTIssuanceStrings(6)))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
}
|
||||
|
||||
@@ -2050,7 +2017,6 @@ public:
|
||||
auto const jr = wsc->invoke(
|
||||
"subscribe", mptIssuancesRequest(makeMPTIssuanceStrings(1, 100)))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
}
|
||||
}
|
||||
@@ -2075,7 +2041,6 @@ public:
|
||||
auto const jr = wsc->invoke(
|
||||
"subscribe", mptIssuancesRequest(makeMPTIssuanceStrings(3)))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
|
||||
// Two more issuances do fit the remaining allowance.
|
||||
@@ -2097,7 +2062,6 @@ public:
|
||||
auto const jr =
|
||||
wsc->invoke("subscribe", accountsRequest(makeAccountStrings(1)))[jss::result];
|
||||
BEAST_EXPECT(jr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jr[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(jr[jss::error_message] == "Too many subscriptions for this connection.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,20 +38,16 @@ class TransactionEntry_test : public beast::unit_test::Suite
|
||||
{
|
||||
// no params
|
||||
auto const result = env.client().invoke("transaction_entry", {})[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "fieldNotFoundTransaction");
|
||||
BEAST_EXPECT(!result.isMember(jss::error_code));
|
||||
BEAST_EXPECT(result[jss::error_message].isNull());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
json::Value params{json::ValueType::Object};
|
||||
params[jss::ledger] = 20;
|
||||
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcLgrNotFound);
|
||||
BEAST_EXPECT(result[jss::error_message] == "ledgerNotFound");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -59,10 +55,8 @@ class TransactionEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger] = "current";
|
||||
params[jss::tx_hash] = "DEADBEEF";
|
||||
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "notYetImplemented");
|
||||
BEAST_EXPECT(!result.isMember(jss::error_code));
|
||||
BEAST_EXPECT(result[jss::error_message].isNull());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -71,10 +65,8 @@ class TransactionEntry_test : public beast::unit_test::Suite
|
||||
params[jss::tx_hash] =
|
||||
"E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05";
|
||||
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "notYetImplemented");
|
||||
BEAST_EXPECT(!result.isMember(jss::error_code));
|
||||
BEAST_EXPECT(result[jss::error_message].isNull());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result.isMember(jss::ledger_current_index));
|
||||
BEAST_EXPECT(!result.isMember(jss::ledger_hash));
|
||||
BEAST_EXPECT(result[jss::validated] == false);
|
||||
@@ -86,10 +78,8 @@ class TransactionEntry_test : public beast::unit_test::Suite
|
||||
params[jss::tx_hash] = "DEADBEEF";
|
||||
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
|
||||
BEAST_EXPECT(!result[jss::ledger_hash].asString().empty());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "malformedRequest");
|
||||
BEAST_EXPECT(!result.isMember(jss::error_code));
|
||||
BEAST_EXPECT(result[jss::error_message].isNull());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
for (auto const type : {json::ValueType::Object, json::ValueType::Array})
|
||||
@@ -98,10 +88,8 @@ class TransactionEntry_test : public beast::unit_test::Suite
|
||||
params[jss::ledger] = "closed";
|
||||
params[jss::tx_hash] = json::Value{type};
|
||||
auto const result = env.client().invoke("transaction_entry", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "malformedRequest");
|
||||
BEAST_EXPECT(!result.isMember(jss::error_code));
|
||||
BEAST_EXPECT(result[jss::error_message].isNull());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
std::string const txHash{
|
||||
@@ -112,74 +100,58 @@ class TransactionEntry_test : public beast::unit_test::Suite
|
||||
// No arguments
|
||||
json::Value const result{env.rpc("transaction_entry")};
|
||||
BEAST_EXPECT(result[jss::ledger_hash].asString().empty());
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "badSyntax");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcBadSyntax);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Syntax error.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// One argument
|
||||
json::Value const result{env.rpc("transaction_entry", txHash)};
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "badSyntax");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcBadSyntax);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Syntax error.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// First argument with too few characters
|
||||
json::Value const result{env.rpc("transaction_entry", txHash.substr(1), "closed")};
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// First argument with too many characters
|
||||
json::Value const result{env.rpc("transaction_entry", txHash + "A", "closed")};
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// Second argument not valid
|
||||
json::Value const result{env.rpc("transaction_entry", txHash, "closer")};
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// Ledger index of 0 is not valid
|
||||
json::Value const result{env.rpc("transaction_entry", txHash, "0")};
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// Three arguments
|
||||
json::Value const result{env.rpc("transaction_entry", txHash, "closed", "extra")};
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "badSyntax");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcBadSyntax);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Syntax error.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
// Valid structure, but transaction not found.
|
||||
json::Value const result{env.rpc("transaction_entry", txHash, "closed")};
|
||||
BEAST_EXPECT(!result[jss::result][jss::ledger_hash].asString().empty());
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == "transactionNotFound");
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::error_code));
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message].isNull());
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
@@ -31,10 +30,8 @@ class TransactionHistory_test : public beast::unit_test::Suite
|
||||
{
|
||||
// no params
|
||||
auto const result = env.client().invoke("tx_history", {})[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid parameters.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -42,11 +39,8 @@ class TransactionHistory_test : public beast::unit_test::Suite
|
||||
json::Value params{json::ValueType::Object};
|
||||
params[jss::start] = 10001; // limited to <= 10000 for non admin
|
||||
auto const result = env.client().invoke("tx_history", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "noPermission");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcNoPermission);
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message] == "You don't have permission for this command.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +54,8 @@ class TransactionHistory_test : public beast::unit_test::Suite
|
||||
json::Value params{json::ValueType::Object};
|
||||
params[jss::api_version] = 2;
|
||||
auto const result = env.client().invoke("tx_history", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error] == "unknownCmd");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcUnknownCommand);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Unknown method.");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -110,10 +110,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(startLegSeq),
|
||||
to_string(endLegSeq + deltaEndSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
|
||||
if (deltaEndSeq != 0)
|
||||
{
|
||||
@@ -154,10 +153,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(startLegSeq),
|
||||
to_string(endLegSeq + deltaEndSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
|
||||
}
|
||||
|
||||
@@ -170,10 +168,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(startLegSeq),
|
||||
to_string(endLegSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
|
||||
}
|
||||
@@ -187,10 +184,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(startLegSeq),
|
||||
to_string(deletedLedger - 1));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
|
||||
}
|
||||
@@ -217,10 +213,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(deletedLedger - 1),
|
||||
to_string(startLegSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -234,10 +229,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(-1),
|
||||
to_string(deletedLedger - 1));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -247,10 +241,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result = env.rpc(
|
||||
command, to_string(tx->getTransactionID()), binary, to_string(-20), to_string(-10));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -260,10 +253,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result =
|
||||
env.rpc(command, to_string(tx->getTransactionID()), binary, to_string(20));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -291,10 +283,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
to_string(startLegSeq),
|
||||
to_string(startLegSeq + 1001));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == excessive);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcExcessiveLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range exceeds 1000.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == excessive);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -360,10 +351,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result = env.rpc(
|
||||
command, ctid, binary, to_string(startLegSeq), to_string(endLegSeq + deltaEndSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
|
||||
if (deltaEndSeq != 0)
|
||||
{
|
||||
@@ -404,10 +394,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result = env.rpc(
|
||||
command, ctid, binary, to_string(startLegSeq), to_string(endLegSeq + deltaEndSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
|
||||
}
|
||||
|
||||
@@ -417,10 +406,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result =
|
||||
env.rpc(command, ctid, to_string(startLegSeq), to_string(endLegSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
|
||||
}
|
||||
@@ -431,10 +419,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result =
|
||||
env.rpc(command, ctid, to_string(startLegSeq), to_string(deletedLedger - 1));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == notFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcTxnNotFound);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Transaction not found.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == notFound);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
|
||||
}
|
||||
@@ -460,10 +447,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result = env.rpc(
|
||||
command, ctid, binary, to_string(deletedLedger - 1), to_string(startLegSeq));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -473,10 +459,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result =
|
||||
env.rpc(command, ctid, binary, to_string(-1), to_string(deletedLedger - 1));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -485,10 +470,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
{
|
||||
auto const result = env.rpc(command, ctid, binary, to_string(-20), to_string(-10));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -497,10 +481,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
{
|
||||
auto const result = env.rpc(command, ctid, binary, to_string(20));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == invalid);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcInvalidLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range is invalid.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == invalid);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -524,10 +507,9 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto const result = env.rpc(
|
||||
command, ctid, binary, to_string(startLegSeq), to_string(startLegSeq + 1001));
|
||||
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
|
||||
BEAST_EXPECT(result[jss::result][jss::error] == excessive);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_code] == RpcExcessiveLgrRange);
|
||||
BEAST_EXPECT(result[jss::result][jss::error_message] == "Ledger range exceeds 1000.");
|
||||
BEAST_EXPECT(
|
||||
result[jss::result][jss::status] == jss::error &&
|
||||
result[jss::result][jss::error] == excessive);
|
||||
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
|
||||
}
|
||||
@@ -963,7 +945,6 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto result = signFor(env, tx);
|
||||
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Missing field 'tx_json.NetworkID'.");
|
||||
}
|
||||
|
||||
@@ -977,7 +958,6 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto result = signFor(env, tx);
|
||||
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'tx_json.NetworkID'.");
|
||||
}
|
||||
|
||||
@@ -991,7 +971,6 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto result = signFor(env, tx);
|
||||
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'tx_json.NetworkID'.");
|
||||
}
|
||||
|
||||
@@ -1006,7 +985,6 @@ class Transaction_test : public beast::unit_test::Suite
|
||||
auto result = signFor(env, tx);
|
||||
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == "Invalid field 'tx_json.NetworkID'.");
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <memory>
|
||||
@@ -28,8 +27,6 @@ public:
|
||||
{
|
||||
Env env(*this);
|
||||
auto const info = env.rpc("validator_info");
|
||||
BEAST_EXPECT(info[jss::result][jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] == "not a validator");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,57 +306,33 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En
|
||||
// 1 - FAILS with wrong pass
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
jrr = makeAdminRequest(env, proto, *user, *password + "_")[jss::result];
|
||||
if (protoWs)
|
||||
{
|
||||
BEAST_EXPECT(jrr[jss::error] == "forbidden");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcForbidden);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Bad credentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(jrr.isNull());
|
||||
}
|
||||
BEAST_EXPECT(jrr["error"] == protoWs ? "forbidden" : "noPermission");
|
||||
BEAST_EXPECT(
|
||||
jrr["error_message"] == protoWs ? "Bad credentials."
|
||||
: "You don't have permission for this command.");
|
||||
|
||||
// 2 - FAILS with password in an object
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
jrr = makeAdminRequest(env, proto, *user, *password, true)[jss::result];
|
||||
if (protoWs)
|
||||
{
|
||||
BEAST_EXPECT(jrr[jss::error] == "forbidden");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcForbidden);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Bad credentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(jrr.isNull());
|
||||
}
|
||||
BEAST_EXPECT(jrr["error"] == protoWs ? "forbidden" : "noPermission");
|
||||
BEAST_EXPECT(
|
||||
jrr["error_message"] == protoWs ? "Bad credentials."
|
||||
: "You don't have permission for this command.");
|
||||
|
||||
// 3 - FAILS with wrong user
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
jrr = makeAdminRequest(env, proto, *user + "_", *password)[jss::result];
|
||||
if (protoWs)
|
||||
{
|
||||
BEAST_EXPECT(jrr[jss::error] == "forbidden");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcForbidden);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Bad credentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(jrr.isNull());
|
||||
}
|
||||
BEAST_EXPECT(jrr["error"] == protoWs ? "forbidden" : "noPermission");
|
||||
BEAST_EXPECT(
|
||||
jrr["error_message"] == protoWs ? "Bad credentials."
|
||||
: "You don't have permission for this command.");
|
||||
|
||||
// 4 - FAILS no credentials
|
||||
jrr = makeAdminRequest(env, proto, "", "")[jss::result];
|
||||
if (protoWs)
|
||||
{
|
||||
BEAST_EXPECT(jrr[jss::error] == "forbidden");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcForbidden);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Bad credentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(jrr.isNull());
|
||||
}
|
||||
BEAST_EXPECT(jrr["error"] == protoWs ? "forbidden" : "noPermission");
|
||||
BEAST_EXPECT(
|
||||
jrr["error_message"] == protoWs ? "Bad credentials."
|
||||
: "You don't have permission for this command.");
|
||||
|
||||
// 5 - SUCCEEDS with proper credentials
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
|
||||
@@ -377,16 +353,10 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En
|
||||
{
|
||||
// 1 - FAILS - admin disabled
|
||||
jrr = makeAdminRequest(env, proto, "", "")[jss::result];
|
||||
if (protoWs)
|
||||
{
|
||||
BEAST_EXPECT(jrr[jss::error] == "forbidden");
|
||||
BEAST_EXPECT(jrr[jss::error_code] == RpcForbidden);
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Bad credentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(jrr.isNull());
|
||||
}
|
||||
BEAST_EXPECT(jrr["error"] == protoWs ? "forbidden" : "noPermission");
|
||||
BEAST_EXPECT(
|
||||
jrr["error_message"] == protoWs ? "Bad credentials."
|
||||
: "You don't have permission for this command.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,9 +730,7 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En
|
||||
|
||||
{ // send invalid json
|
||||
auto resp = sendAndParse("NOT JSON");
|
||||
BEAST_EXPECT(resp[jss::error] == "jsonInvalid");
|
||||
BEAST_EXPECT(!resp.isMember(jss::error_code));
|
||||
BEAST_EXPECT(resp[jss::error_message].isNull());
|
||||
BEAST_EXPECT(resp.isMember(jss::error) && resp[jss::error] == "jsonInvalid");
|
||||
BEAST_EXPECT(!resp.isMember(jss::status));
|
||||
}
|
||||
|
||||
@@ -771,10 +739,8 @@ class ServerStatus_test : public beast::unit_test::Suite, public beast::test::En
|
||||
jv[jss::command] = "foo";
|
||||
jv[jss::method] = "bar";
|
||||
auto resp = sendAndParse(to_string(jv));
|
||||
BEAST_EXPECT(resp[jss::status] == "error");
|
||||
BEAST_EXPECT(resp[jss::error] == "missingCommand");
|
||||
BEAST_EXPECT(!resp.isMember(jss::error_code));
|
||||
BEAST_EXPECT(resp[jss::error_message].isNull());
|
||||
BEAST_EXPECT(resp.isMember(jss::error) && resp[jss::error] == "missingCommand");
|
||||
BEAST_EXPECT(resp.isMember(jss::status) && resp[jss::status] == "error");
|
||||
}
|
||||
|
||||
{ // send a ping (not an error)
|
||||
|
||||
@@ -35,6 +35,8 @@ TEST(PayChannelTests, BuilderSettersRoundTrip)
|
||||
auto const previousTxnIDValue = canonical_UINT256();
|
||||
auto const previousTxnLgrSeqValue = canonical_UINT32();
|
||||
auto const destinationNodeValue = canonical_UINT64();
|
||||
auto const transferRateValue = canonical_UINT32();
|
||||
auto const issuerNodeValue = canonical_UINT64();
|
||||
|
||||
PayChannelBuilder builder{
|
||||
accountValue,
|
||||
@@ -54,6 +56,8 @@ TEST(PayChannelTests, BuilderSettersRoundTrip)
|
||||
builder.setSourceTag(sourceTagValue);
|
||||
builder.setDestinationTag(destinationTagValue);
|
||||
builder.setDestinationNode(destinationNodeValue);
|
||||
builder.setTransferRate(transferRateValue);
|
||||
builder.setIssuerNode(issuerNodeValue);
|
||||
|
||||
builder.setLedgerIndex(index);
|
||||
builder.setFlags(0x1u);
|
||||
@@ -166,6 +170,22 @@ TEST(PayChannelTests, BuilderSettersRoundTrip)
|
||||
EXPECT_TRUE(entry.hasDestinationNode());
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = transferRateValue;
|
||||
auto const actualOpt = entry.getTransferRate();
|
||||
ASSERT_TRUE(actualOpt.has_value());
|
||||
expectEqualField(expected, *actualOpt, "sfTransferRate");
|
||||
EXPECT_TRUE(entry.hasTransferRate());
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = issuerNodeValue;
|
||||
auto const actualOpt = entry.getIssuerNode();
|
||||
ASSERT_TRUE(actualOpt.has_value());
|
||||
expectEqualField(expected, *actualOpt, "sfIssuerNode");
|
||||
EXPECT_TRUE(entry.hasIssuerNode());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(entry.hasLedgerIndex());
|
||||
auto const ledgerIndex = entry.getLedgerIndex();
|
||||
ASSERT_TRUE(ledgerIndex.has_value());
|
||||
@@ -194,6 +214,8 @@ TEST(PayChannelTests, BuilderFromSleRoundTrip)
|
||||
auto const previousTxnIDValue = canonical_UINT256();
|
||||
auto const previousTxnLgrSeqValue = canonical_UINT32();
|
||||
auto const destinationNodeValue = canonical_UINT64();
|
||||
auto const transferRateValue = canonical_UINT32();
|
||||
auto const issuerNodeValue = canonical_UINT64();
|
||||
|
||||
auto sle = std::make_shared<SLE>(PayChannel::entryType, index);
|
||||
|
||||
@@ -212,6 +234,8 @@ TEST(PayChannelTests, BuilderFromSleRoundTrip)
|
||||
sle->at(sfPreviousTxnID) = previousTxnIDValue;
|
||||
sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue;
|
||||
sle->at(sfDestinationNode) = destinationNodeValue;
|
||||
sle->at(sfTransferRate) = transferRateValue;
|
||||
sle->at(sfIssuerNode) = issuerNodeValue;
|
||||
|
||||
PayChannelBuilder builderFromSle{sle};
|
||||
EXPECT_TRUE(builderFromSle.validate());
|
||||
@@ -390,6 +414,32 @@ TEST(PayChannelTests, BuilderFromSleRoundTrip)
|
||||
expectEqualField(expected, *fromBuilderOpt, "sfDestinationNode");
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = transferRateValue;
|
||||
|
||||
auto const fromSleOpt = entryFromSle.getTransferRate();
|
||||
auto const fromBuilderOpt = entryFromBuilder.getTransferRate();
|
||||
|
||||
ASSERT_TRUE(fromSleOpt.has_value());
|
||||
ASSERT_TRUE(fromBuilderOpt.has_value());
|
||||
|
||||
expectEqualField(expected, *fromSleOpt, "sfTransferRate");
|
||||
expectEqualField(expected, *fromBuilderOpt, "sfTransferRate");
|
||||
}
|
||||
|
||||
{
|
||||
auto const& expected = issuerNodeValue;
|
||||
|
||||
auto const fromSleOpt = entryFromSle.getIssuerNode();
|
||||
auto const fromBuilderOpt = entryFromBuilder.getIssuerNode();
|
||||
|
||||
ASSERT_TRUE(fromSleOpt.has_value());
|
||||
ASSERT_TRUE(fromBuilderOpt.has_value());
|
||||
|
||||
expectEqualField(expected, *fromSleOpt, "sfIssuerNode");
|
||||
expectEqualField(expected, *fromBuilderOpt, "sfIssuerNode");
|
||||
}
|
||||
|
||||
EXPECT_EQ(entryFromSle.getKey(), index);
|
||||
EXPECT_EQ(entryFromBuilder.getKey(), index);
|
||||
}
|
||||
@@ -472,5 +522,9 @@ TEST(PayChannelTests, OptionalFieldsReturnNullopt)
|
||||
EXPECT_FALSE(entry.getDestinationTag().has_value());
|
||||
EXPECT_FALSE(entry.hasDestinationNode());
|
||||
EXPECT_FALSE(entry.getDestinationNode().has_value());
|
||||
EXPECT_FALSE(entry.hasTransferRate());
|
||||
EXPECT_FALSE(entry.getTransferRate().has_value());
|
||||
EXPECT_FALSE(entry.hasIssuerNode());
|
||||
EXPECT_FALSE(entry.getIssuerNode().has_value());
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,26 @@ struct Command
|
||||
unsigned maxParams = kUnlimitedParams;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the amount argument of channel_authorize or channel_verify to its
|
||||
* request field: a string of drops is kept as is, and a string holding a JSON
|
||||
* object is parsed so the server reads it as a token Amount.
|
||||
*
|
||||
* @return std::nullopt if the argument is neither.
|
||||
*/
|
||||
std::optional<json::Value>
|
||||
parseChannelAmount(json::Value const& arg)
|
||||
{
|
||||
if (!arg.isString())
|
||||
return std::nullopt;
|
||||
if (toUInt64(arg.asString()))
|
||||
return arg;
|
||||
json::Value amount;
|
||||
if (json::Reader().parse(arg.asString(), amount) && amount.isObject())
|
||||
return amount;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
@@ -831,7 +851,7 @@ private:
|
||||
return parseAccountRaw2(jvParams, jss::destination_account);
|
||||
}
|
||||
|
||||
// channel_authorize: <private_key> [<key_type>] <channel_id> <drops>
|
||||
// channel_authorize: <private_key> [<key_type>] <channel_id> <drops>|<amount_json>
|
||||
json::Value
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
parseChannelAuthorize(json::Value const& jvParams)
|
||||
@@ -865,9 +885,10 @@ private:
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!jvParams[index].isString() || !toUInt64(jvParams[index].asString()))
|
||||
auto const amount = parseChannelAmount(jvParams[index]);
|
||||
if (!amount)
|
||||
return rpcError(RpcChannelAmtMalformed);
|
||||
jvRequest[jss::amount] = jvParams[index];
|
||||
jvRequest[jss::amount] = *amount;
|
||||
|
||||
// If additional parameters are appended, be sure to increment index
|
||||
// here
|
||||
@@ -875,7 +896,7 @@ private:
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
// channel_verify <public_key> <channel_id> <drops> <signature>
|
||||
// channel_verify <public_key> <channel_id> <drops>|<amount_json> <signature>
|
||||
json::Value
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
parseChannelVerify(json::Value const& jvParams)
|
||||
@@ -896,9 +917,10 @@ private:
|
||||
}
|
||||
jvRequest[jss::channel_id] = jvParams[1u].asString();
|
||||
|
||||
if (!jvParams[2u].isString() || !toUInt64(jvParams[2u].asString()))
|
||||
auto const amount = parseChannelAmount(jvParams[2u]);
|
||||
if (!amount)
|
||||
return rpcError(RpcChannelAmtMalformed);
|
||||
jvRequest[jss::amount] = jvParams[2u];
|
||||
jvRequest[jss::amount] = *amount;
|
||||
|
||||
jvRequest[jss::signature] = jvParams[3u].asString();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
@@ -20,12 +21,16 @@
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/Seed.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol/tokens.h>
|
||||
|
||||
@@ -529,4 +534,25 @@ parseSubUnsubJson(
|
||||
return RpcSuccess;
|
||||
}
|
||||
|
||||
bool
|
||||
serializeChannelAuthorization(Serializer& msg, uint256 const& channelId, json::Value const& amount)
|
||||
{
|
||||
if (amount.isString())
|
||||
{
|
||||
std::optional<std::uint64_t> const drops = toUInt64(amount.asString());
|
||||
if (!drops)
|
||||
return false;
|
||||
serializePayChanAuthorization(msg, channelId, XRPAmount(*drops));
|
||||
return true;
|
||||
}
|
||||
|
||||
// amountFromJsonNoThrow rejects an object naming XRP; only the sign is checked here
|
||||
STAmount tokenAmount;
|
||||
if (!amount.isObject() || !amountFromJsonNoThrow(tokenAmount, amount) || tokenAmount.negative())
|
||||
return false;
|
||||
|
||||
serializePayChanAuthorization(msg, channelId, tokenAmount);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xrpl::rpc
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <xrpl/protocol/STLedgerEntry.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <xrpl/protocol/Seed.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
@@ -213,6 +214,21 @@ keypairForSignature(
|
||||
json::Value& error,
|
||||
unsigned int apiVersion = kApiVersionIfUnspecified);
|
||||
|
||||
/**
|
||||
* Serialize the payment channel claim authorization message for the `amount`
|
||||
* of a channel_authorize or channel_verify request.
|
||||
*
|
||||
* @param msg Serializer the message is appended to.
|
||||
* @param channelId The channel the claim is for.
|
||||
* @param amount A string of drops for an XRP channel, or the transaction
|
||||
* Amount JSON object (currency/issuer/value or mpt_issuance_id/value) for a
|
||||
* token channel.
|
||||
* @return false, with nothing appended, if `amount` is neither a string of
|
||||
* drops nor a valid non-negative token Amount object.
|
||||
*/
|
||||
bool
|
||||
serializeChannelAuthorization(Serializer& msg, uint256 const& channelId, json::Value const& amount);
|
||||
|
||||
/**
|
||||
* Parse subscribe/unsubscribe parameters
|
||||
*/
|
||||
|
||||
@@ -6,16 +6,13 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/PayChan.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/protocol/tokens.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
@@ -24,7 +21,7 @@ namespace xrpl {
|
||||
// {
|
||||
// public_key: <public_key>
|
||||
// channel_id: 256-bit channel id
|
||||
// drops: 64-bit uint (as string)
|
||||
// amount: drops as a string (XRP channel) or an Amount object (token channel)
|
||||
// signature: signature to verify
|
||||
// }
|
||||
json::Value
|
||||
@@ -62,14 +59,10 @@ doChannelVerify(rpc::JsonContext& context)
|
||||
if (!channelId.parseHex(params[jss::channel_id].asString()))
|
||||
return rpcError(RpcChannelMalformed);
|
||||
|
||||
std::optional<std::uint64_t> const optDrops =
|
||||
params[jss::amount].isString() ? toUInt64(params[jss::amount].asString()) : std::nullopt;
|
||||
|
||||
if (!optDrops)
|
||||
Serializer msg;
|
||||
if (!rpc::serializeChannelAuthorization(msg, channelId, params[jss::amount]))
|
||||
return rpcError(RpcChannelAmtMalformed);
|
||||
|
||||
std::uint64_t const drops = *optDrops;
|
||||
|
||||
if (!params[jss::signature].isString())
|
||||
return rpcError(RpcInvalidParams);
|
||||
|
||||
@@ -77,9 +70,6 @@ doChannelVerify(rpc::JsonContext& context)
|
||||
if (!sig || sig->empty())
|
||||
return rpcError(RpcInvalidParams);
|
||||
|
||||
Serializer msg;
|
||||
serializePayChanAuthorization(msg, channelId, XRPAmount(drops));
|
||||
|
||||
json::Value result;
|
||||
result[jss::signature_verified] = verify(*pk, msg.slice(), makeSlice(*sig));
|
||||
return result;
|
||||
|
||||
@@ -48,8 +48,13 @@ appendChannelJson(SLE const& channel, json::Value& channels)
|
||||
jDst[jss::channel_id] = to_string(channel.key());
|
||||
jDst[jss::account] = to_string(channel[sfAccount]);
|
||||
jDst[jss::destination_account] = to_string(channel[sfDestination]);
|
||||
jDst[jss::amount] = channel[sfAmount].getText();
|
||||
jDst[jss::balance] = channel[sfBalance].getText();
|
||||
// A token amount is an Amount JSON object; an XRP amount stays a string of drops.
|
||||
auto const amountJson = [](STAmount const& amount) {
|
||||
return amount.native() ? json::Value{amount.getText()}
|
||||
: amount.getJson(JsonOptions::Values::None);
|
||||
};
|
||||
jDst[jss::amount] = amountJson(channel[sfAmount]);
|
||||
jDst[jss::balance] = amountJson(channel[sfBalance]);
|
||||
if (publicKeyType(channel[sfPublicKey]))
|
||||
{
|
||||
PublicKey const pk(channel[sfPublicKey]);
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user