ttImport (#65)

Add support for Burn2Mint and key import from original XRPL network in new txn type: ttIMPORT. Needs further testing.
This commit is contained in:
RichardAH
2023-05-22 15:06:05 +02:00
committed by GitHub
parent 8b87cc291c
commit 131bd9f4b3
22 changed files with 1571 additions and 32 deletions

View File

@@ -443,6 +443,7 @@ target_sources (rippled PRIVATE
src/ripple/app/tx/impl/SetRegularKey.cpp
src/ripple/app/tx/impl/SetHook.cpp
src/ripple/app/tx/impl/ClaimReward.cpp
src/ripple/app/tx/impl/Import.cpp
src/ripple/app/tx/impl/Invoke.cpp
src/ripple/app/tx/impl/SetSignerList.cpp
src/ripple/app/tx/impl/SetTrust.cpp

View File

@@ -1324,6 +1324,13 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
return false;
}
}
if (config_->IMPORT_VL_KEYS.empty())
{
JLOG(m_journal.fatal()) << "IMPORT_VL_KEYS section must be specified in validators file.";
return false;
}
//----------------------------------------------------------------------
//
// Server

View File

@@ -755,8 +755,14 @@ TxQ::apply(
auto const account = (*tx)[sfAccount];
Keylet const accountKey{keylet::account(account)};
auto const sleAccount = view.read(accountKey);
if (!sleAccount)
{
if (tx->getTxnType() == ttIMPORT)
return {telCAN_NOT_QUEUE_IMPORT, false};
return {terNO_ACCOUNT, false};
}
// If the transaction needs a Ticket is that Ticket in the ledger?
SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
@@ -1809,19 +1815,27 @@ TxQ::tryDirectApply(
auto const account = (*tx)[sfAccount];
auto const sleAccount = view.read(keylet::account(account));
const bool isFirstImport = !sleAccount && view.rules().enabled(featureImport) && tx->getTxnType() == ttIMPORT;
// Don't attempt to direct apply if the account is not in the ledger.
if (!sleAccount)
if (!sleAccount && !isFirstImport)
return {};
SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
SeqProxy const txSeqProx = tx->getSeqProxy();
std::optional<SeqProxy> txSeqProx;
// Can only directly apply if the transaction sequence matches the account
// sequence or if the transaction uses a ticket.
if (txSeqProx.isSeq() && txSeqProx != acctSeqProx)
return {};
if (!isFirstImport)
{
SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
txSeqProx = tx->getSeqProxy();
FeeLevel64 const requiredFeeLevel = [this, &view, flags]() {
// Can only directly apply if the transaction sequence matches the account
// sequence or if the transaction uses a ticket.
if (txSeqProx->isSeq() && *txSeqProx != acctSeqProx)
return {};
}
FeeLevel64 const requiredFeeLevel = isFirstImport ? FeeLevel64 { 0 } :
[this, &view, flags]() {
std::lock_guard lock(mutex_);
return getRequiredFeeLevel(
view, flags, feeMetrics_.getSnapshot(), lock);
@@ -1856,11 +1870,12 @@ TxQ::tryDirectApply(
if (accountIter != byAccount_.end())
{
TxQAccount& txQAcct = accountIter->second;
if (auto const existingIter =
txQAcct.transactions.find(txSeqProx);
existingIter != txQAcct.transactions.end())
if (txSeqProx)
{
removeFromByFee(existingIter, tx);
auto const existingIter =
txQAcct.transactions.find(*txSeqProx);
if (existingIter != txQAcct.transactions.end())
removeFromByFee(existingIter, tx);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TX_IMPORT_H_INCLUDED
#define RIPPLE_TX_IMPORT_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/protocol/Indexes.h>
#include <memory>
namespace ripple {
class Import : public Transactor
{
public:
// newly imported accounts get 2 XRP
static constexpr XRPAmount INITIAL_IMPORT_XRP{2 * DROPS_PER_XRP};
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
static std::pair<
std::unique_ptr<STTx const>,
std::unique_ptr<STObject const>>
getInnerTxn(STTx const& outer, beast::Journal const& j,Json::Value const* xpop = 0);
explicit Import(ApplyContext& ctx) : Transactor(ctx)
{
}
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TxConsequences
makeTxConsequences(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
} // namespace ripple
#endif

View File

@@ -27,6 +27,7 @@
#include <ripple/protocol/STArray.h>
#include <ripple/protocol/SystemParameters.h>
#include <ripple/protocol/nftPageMask.h>
#include <ripple/app/tx/impl/Import.h>
namespace ripple {
@@ -134,16 +135,47 @@ XRPNotCreated::visitEntry(
break;
}
}
if (!before && after->getType() == ltACCOUNT_ROOT)
accountsCreated_++;
}
bool
XRPNotCreated::finalize(
STTx const&,
STTx const& tx,
TER const,
XRPAmount const fee,
ReadView const&,
ReadView const& view,
beast::Journal const& j)
{
if (view.rules().enabled(featureImport) && tx.getTxnType() == ttIMPORT)
{
// different rules for ttIMPORT
auto const [inner, meta] = Import::getInnerTxn(tx, j);
if (!inner || !meta)
return false;
auto const result = meta->getFieldU8(sfTransactionResult);
XRPAmount dropsAdded =
result == tesSUCCESS || (result >= tecCLAIM && result <= tecLAST_POSSIBLE_ENTRY)
? inner->getFieldAmount(sfFee).xrp() // burned in PoB
: beast::zero; // if the txn didnt burn a fee we add nothing
if (accountsCreated_ == 1)
dropsAdded += Import::INITIAL_IMPORT_XRP; // welcome amount for new imports
JLOG(j.trace())
<< "Invariant XRPNotCreated Import: "
<< "dropsAdded: " << dropsAdded
<< " fee.drops(): " << fee.drops()
<< " drops_: " << drops_
<< " dropsAdded - fee.drops(): " << dropsAdded - fee.drops();
return (drops_ == dropsAdded.drops() - fee.drops());
}
// The net change should never be positive, as this would mean that the
// transaction created XRP out of thin air. That's not possible.
if (drops_ > 0)
@@ -493,7 +525,8 @@ ValidNewAccountRoot::finalize(
}
// From this point on we know exactly one account was created.
if (tx.getTxnType() == ttPAYMENT && result == tesSUCCESS)
auto tt = tx.getTxnType();
if ((tt == ttPAYMENT || tt == ttIMPORT) && result == tesSUCCESS)
{
std::uint32_t const startingSeq{
view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1};

View File

@@ -118,6 +118,7 @@ public:
class XRPNotCreated
{
std::int64_t drops_ = 0;
std::uint32_t accountsCreated_ = 0;
public:
void

View File

@@ -401,7 +401,12 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
auto const id = ctx.tx.getAccountID(sfAccount);
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
{
if (ctx.tx.getTxnType() == ttIMPORT)
return tesSUCCESS;
return terNO_ACCOUNT;
}
auto const balance = (*sle)[sfBalance].xrp();
@@ -429,6 +434,7 @@ Transactor::payFee()
auto const feePaid = ctx_.tx[sfFee].xrp();
auto const sle = view().peek(keylet::account(account_));
// RH NOTE: we don't need to check for ttIMPORT here because this function is skipped if the sle doesn't exist
if (!sle)
return tefINTERNAL;
@@ -453,15 +459,27 @@ Transactor::checkSeqProxy(
auto const sle = view.read(keylet::account(id));
SeqProxy const t_seqProx = tx.getSeqProxy();
if (!sle)
{
if (view.rules().enabled(featureImport) &&
tx.getTxnType() == ttIMPORT &&
t_seqProx.isSeq() &&
tx[sfSequence] == 0)
{
JLOG(j.trace())
<< "applyTransaction: allowing first Import txn with seq=0 "
<< toBase58(id);
return tesSUCCESS;
}
JLOG(j.trace())
<< "applyTransaction: delay: source account does not exist "
<< toBase58(id);
return terNO_ACCOUNT;
}
SeqProxy const t_seqProx = tx.getSeqProxy();
SeqProxy const a_seq = SeqProxy::sequence((*sle)[sfSequence]);
// pass all emitted tx provided their seq is 0
@@ -537,7 +555,8 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
auto const sle = ctx.view.read(keylet::account(id));
if (!sle)
bool const isFirstImport = !sle && ctx.view.rules().enabled(featureImport) && ctx.tx.getTxnType() == ttIMPORT;
if (!sle && !isFirstImport)
{
JLOG(ctx.j.trace())
<< "applyTransaction: delay: source account does not exist "
@@ -545,10 +564,11 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
return terNO_ACCOUNT;
}
if (ctx.tx.isFieldPresent(sfAccountTxnID) &&
(sle->getFieldH256(sfAccountTxnID) !=
ctx.tx.getFieldH256(sfAccountTxnID)))
return tefWRONG_PRIOR;
if (ctx.tx.isFieldPresent(sfAccountTxnID))
{
if (isFirstImport || sle->getFieldH256(sfAccountTxnID) != ctx.tx.getFieldH256(sfAccountTxnID))
return tefWRONG_PRIOR;
}
if (ctx.tx.isFieldPresent(sfLastLedgerSequence) &&
(ctx.view.seq() > ctx.tx.getFieldU32(sfLastLedgerSequence)))
@@ -653,8 +673,9 @@ Transactor::apply()
auto const sle = view().peek(keylet::account(account_));
// sle must exist except for transactions
// that allow zero account.
assert(sle != nullptr || account_ == beast::zero);
// that allow zero account. (and ttIMPORT)
assert(sle != nullptr || account_ == beast::zero ||
view().rules().enabled(featureImport) && ctx_.tx.getTxnType() == ttIMPORT);
if (sle)
{
@@ -694,6 +715,11 @@ Transactor::checkSign(PreclaimContext const& ctx)
return telNON_LOCAL_EMITTED_TXN;
}
// pass ttIMPORTs, their signatures are checked at the preflight against the internal xpop txn
if (ctx.view.rules().enabled(featureImport) &&
ctx.tx.getTxnType() == ttIMPORT)
return tesSUCCESS;
// If the pk is empty, then we must be multi-signing.
if (ctx.tx.getSigningPubKey().empty())
return checkMultiSign(ctx);
@@ -717,6 +743,7 @@ Transactor::checkSingleSign(PreclaimContext const& ctx)
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const idAccount = ctx.tx.getAccountID(sfAccount);
auto const sleAccount = ctx.view.read(keylet::account(idAccount));
if (!sleAccount)
return terNO_ACCOUNT;

View File

@@ -41,6 +41,7 @@
#include <ripple/app/tx/impl/SetSignerList.h>
#include <ripple/app/tx/impl/SetTrust.h>
#include <ripple/app/tx/impl/SetHook.h>
#include <ripple/app/tx/impl/Import.h>
#include <ripple/app/tx/impl/Invoke.h>
#include <ripple/app/tx/impl/URIToken.h>
@@ -155,6 +156,8 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<NFTokenAcceptOffer>(ctx);
case ttCLAIM_REWARD:
return invoke_preflight_helper<ClaimReward>(ctx);
case ttIMPORT:
return invoke_preflight_helper<Import>(ctx);
case ttINVOKE:
return invoke_preflight_helper<Invoke>(ctx);
case ttURITOKEN_MINT:
@@ -194,7 +197,9 @@ invoke_preclaim(PreclaimContext const& ctx)
if (result != tesSUCCESS)
return result;
result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx));
result =
ctx.tx.getTxnType() == ttIMPORT ? tesSUCCESS :
T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx));
if (result != tesSUCCESS)
return result;
@@ -270,6 +275,8 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<NFTokenAcceptOffer>(ctx);
case ttCLAIM_REWARD:
return invoke_preclaim<ClaimReward>(ctx);
case ttIMPORT:
return invoke_preclaim<Import>(ctx);
case ttINVOKE:
return invoke_preclaim<Invoke>(ctx);
case ttURITOKEN_MINT:
@@ -346,6 +353,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return NFTokenAcceptOffer::calculateBaseFee(view, tx);
case ttCLAIM_REWARD:
return ClaimReward::calculateBaseFee(view, tx);
case ttIMPORT:
return Import::calculateBaseFee(view, tx);
case ttINVOKE:
return Invoke::calculateBaseFee(view, tx);
case ttURITOKEN_MINT:
@@ -516,6 +525,10 @@ invoke_apply(ApplyContext& ctx)
ClaimReward p(ctx);
return p();
}
case ttIMPORT: {
Import p(ctx);
return p();
}
case ttINVOKE: {
Invoke p(ctx);
return p();

View File

@@ -150,6 +150,8 @@ public:
std::vector<std::string> IPS_FIXED; // Fixed Peer IPs from rippled.cfg.
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
std::vector<std::string> IMPORT_VL_KEYS;
enum StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
StartUpType START_UP = NORMAL;

View File

@@ -101,6 +101,7 @@ struct ConfigSection
#define SECTION_SWEEP_INTERVAL "sweep_interval"
#define SECTION_XPOP_HISTORY "xpop_history"
#define SECTION_NETWORK_ID "network_id"
#define SECTION_IMPORT_VL_KEYS "import_vl_keys"
} // namespace ripple

View File

@@ -837,7 +837,7 @@ Config::loadFromString(std::string const& fileContents)
BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
// Do not load trusted validator configuration for standalone mode
if (!RUN_STANDALONE)
do
{
// If a file was explicitly specified, then throw if the
// path is malformed or if the file does not exist or is
@@ -906,6 +906,21 @@ Config::loadFromString(std::string const& fileContents)
auto iniFile = parseIniFile(data, true);
if (auto importKeys =
getIniFileSection(iniFile, SECTION_IMPORT_VL_KEYS))
IMPORT_VL_KEYS = *importKeys;
else
Throw<std::runtime_error>(
"The file specified in [" SECTION_VALIDATORS_FILE
"] "
"does not contain a [" SECTION_IMPORT_VL_KEYS
"] section: " +
validatorsFile.string());
if (RUN_STANDALONE)
break;
auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
if (entries)
@@ -929,6 +944,8 @@ Config::loadFromString(std::string const& fileContents)
if (valListKeys)
section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
if (!entries && !valKeyEntries && !valListKeys)
Throw<std::runtime_error>(
"The file specified in [" SECTION_VALIDATORS_FILE
@@ -943,6 +960,7 @@ Config::loadFromString(std::string const& fileContents)
validatorsFile.string());
}
// Consolidate [validator_keys] and [validators]
section(SECTION_VALIDATORS)
.append(section(SECTION_VALIDATOR_KEYS).lines());
@@ -954,7 +972,7 @@ Config::loadFromString(std::string const& fileContents)
"[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
"] config section is missing");
}
}
} while (0);
{
auto const part = section("features");

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 62;
static constexpr std::size_t numFeatures = 63;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -350,6 +350,7 @@ extern uint256 const featureXRPFees;
extern uint256 const fixUniversalNumber;
extern uint256 const fixNonFungibleTokensV1_2;
extern uint256 const fixNFTokenRemint;
extern uint256 const featureImport;
} // namespace ripple

View File

@@ -408,6 +408,7 @@ extern SF_UINT32 const sfRewardTime;
extern SF_UINT32 const sfRewardLgrFirst;
extern SF_UINT32 const sfRewardLgrLast;
extern SF_UINT32 const sfFirstNFTokenSequence;
extern SF_UINT32 const sfImportSequence;
// 64-bit integers (common)
extern SF_UINT64 const sfIndexNext;

View File

@@ -64,7 +64,9 @@ enum TELcodes : TERUnderlyingType {
telWRONG_NETWORK,
telREQUIRES_NETWORK_ID,
telNETWORK_ID_MAKES_TX_NON_CANONICAL,
telNON_LOCAL_EMITTED_TXN
telNON_LOCAL_EMITTED_TXN,
telIMPORT_VL_KEY_NOT_RECOGNISED,
telCAN_NOT_QUEUE_IMPORT,
};
//------------------------------------------------------------------------------
@@ -180,6 +182,7 @@ enum TEFcodes : TERUnderlyingType {
tefTOO_BIG,
tefNO_TICKET,
tefNFTOKEN_IS_NOT_TRANSFERABLE,
tefPAST_IMPORT_SEQ,
};
//------------------------------------------------------------------------------
@@ -332,7 +335,8 @@ enum TECcodes : TERUnderlyingType {
tecXCHAIN_ACCOUNT_CREATE_TOO_MANY = 183, // RESERVED - XCHAIN
tecXCHAIN_PAYMENT_FAILED = 184, // RESERVED - XCHAIN
tecXCHAIN_SELF_COMMIT = 185, // RESERVED - XCHAIN
tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 186 // RESERVED - XCHAIN
tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 186, // RESERVED - XCHAIN
tecLAST_POSSIBLE_ENTRY = 255,
};
//------------------------------------------------------------------------------

View File

@@ -146,6 +146,10 @@ enum TxType : std::uint16_t
ttURITOKEN_CREATE_SELL_OFFER = 48,
ttURITOKEN_CANCEL_SELL_OFFER = 49,
/** This transaciton accepts a proof of burn from an external network as a basis
* for minting according to featureImport */
ttIMPORT = 97,
/** This transaction resets accumulator/counters and claims a reward for holding an average balance
* from a specified hook */
ttCLAIM_REWARD = 98,

View File

@@ -456,6 +456,7 @@ REGISTER_FEATURE(Hooks, Supported::yes, VoteBehavior::De
REGISTER_FEATURE(BalanceRewards, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(URIToken, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.

View File

@@ -62,6 +62,7 @@ LedgerFormats::LedgerFormats()
{sfRewardTime, soeOPTIONAL},
{sfRewardAccumulator, soeOPTIONAL},
{sfFirstNFTokenSequence, soeOPTIONAL},
{sfImportSequence, soeOPTIONAL},
},
commonFields);

View File

@@ -156,6 +156,8 @@ CONSTRUCT_TYPED_SFIELD(sfEmitGeneration, "EmitGeneration", UINT32,
CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32, 49);
CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50);
CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97);
CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98);
CONSTRUCT_TYPED_SFIELD(sfRewardLgrFirst, "RewardLgrFirst", UINT32, 99);
CONSTRUCT_TYPED_SFIELD(sfRewardLgrLast, "RewardLgrLast", UINT32, 100);
@@ -249,8 +251,6 @@ CONSTRUCT_TYPED_SFIELD(sfNFTokenBrokerFee, "NFTokenBrokerFee", AMOUNT,
CONSTRUCT_TYPED_SFIELD(sfHookCallbackFee, "HookCallbackFee", AMOUNT, 20);
CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT, 21);
// Reserve 20 & 21 for Hooks
// currency amount (fees)
CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22);
CONSTRUCT_TYPED_SFIELD(sfReserveBaseDrops, "ReserveBaseDrops", AMOUNT, 23);

View File

@@ -106,6 +106,7 @@ transResults()
MAKE_ERROR(tefNO_AUTH_REQUIRED, "Auth is not required."),
MAKE_ERROR(tefNOT_MULTI_SIGNING, "Account has no appropriate list of multi-signers."),
MAKE_ERROR(tefPAST_SEQ, "This sequence number has already passed."),
MAKE_ERROR(tefPAST_IMPORT_SEQ, "This import sequence number has already been used."),
MAKE_ERROR(tefWRONG_PRIOR, "This previous transaction does not match."),
MAKE_ERROR(tefBAD_AUTH_MASTER, "Auth for unclaimed account needs correct master key."),
MAKE_ERROR(tefINVARIANT_FAILED, "Fee claim violated invariants for the transaction."),
@@ -130,6 +131,7 @@ transResults()
MAKE_ERROR(telREQUIRES_NETWORK_ID, "Transactions submitted to this node/network must include a correct NetworkID field."),
MAKE_ERROR(telNETWORK_ID_MAKES_TX_NON_CANONICAL, "Transactions submitted to this node/network must NOT include a NetworkID field."),
MAKE_ERROR(telNON_LOCAL_EMITTED_TXN, "Emitted transaction cannot be applied because it was not generated locally."),
MAKE_ERROR(telCAN_NOT_QUEUE_IMPORT, "Import transaction was not able to be directly applied and cannot be queued."),
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
MAKE_ERROR(temBAD_AMOUNT, "Can only send positive amounts."),

View File

@@ -35,7 +35,6 @@ TxFormats::TxFormats()
{sfLastLedgerSequence, soeOPTIONAL},
{sfAccountTxnID, soeOPTIONAL},
{sfFee, soeREQUIRED},
{sfOperationLimit, soeOPTIONAL},
{sfMemos, soeOPTIONAL},
{sfSigningPubKey, soeREQUIRED},
{sfTxnSignature, soeOPTIONAL},
@@ -44,6 +43,7 @@ TxFormats::TxFormats()
{sfFirstLedgerSequence, soeOPTIONAL},
{sfNetworkID, soeOPTIONAL},
{sfHookParameters, soeOPTIONAL},
{sfOperationLimit, soeOPTIONAL},
};
add(jss::AccountSet,
@@ -358,6 +358,13 @@ TxFormats::TxFormats()
},
commonFields);
add(jss::Import,
ttIMPORT,
{
{sfBlob, soeREQUIRED},
},
commonFields);
add(jss::Invoke,
ttINVOKE,
{

View File

@@ -86,6 +86,7 @@ JSS(HookGrant); // field
JSS(isSerialized); // out: RPC server_definitions
JSS(isSigningField); // out: RPC server_definitions
JSS(isVLEncoded); // out: RPC server_definitions
JSS(Import);
JSS(Invalid); //
JSS(Invoke); // transaction type
JSS(InvoiceID); // field
@@ -163,6 +164,7 @@ JSS(account_history_tx_first); // out: Account txn history subscribe
JSS(accounts); // in: LedgerEntry, Subscribe,
// handlers/Ledger, Unsubscribe
JSS(accounts_proposed); // in: Subscribe, Unsubscribe
JSS(acroot);
JSS(action);
JSS(acquiring); // out: LedgerRequest
JSS(address); // out: PeerImp
@@ -231,7 +233,10 @@ JSS(converge_time_s); // out: NetworkOPs
JSS(cookie); // out: NetworkOPs
JSS(count); // in: AccountTx*, ValidatorList
JSS(counters); // in/out: retrieve counters
JSS(coins);
JSS(children);
JSS(ctid); // in/out: Tx RPC
JSS(cres);
JSS(currency_a); // out: BookChanges
JSS(currency_b); // out: BookChanges
JSS(currentShard); // out: NodeToShardStatus
@@ -504,6 +509,7 @@ JSS(paths); // in: RipplePathFind
JSS(paths_canonical); // out: RipplePathFind
JSS(paths_computed); // out: PathRequest, RipplePathFind
JSS(payment_channel); // in: LedgerEntry
JSS(pclose);
JSS(peer); // in: AccountLines
JSS(peer_authorized); // out: AccountLines
JSS(peer_id); // out: RCLCxPeerPos
@@ -511,6 +517,7 @@ JSS(peers); // out: InboundLedger, handlers/Peers, Overlay
JSS(peer_disconnects); // Severed peer connection counter.
JSS(peer_disconnects_resources); // Severed peer connections because of
// excess resource consumption.
JSS(phash);
JSS(port); // in: Connect
JSS(previous); // out: Reservations
JSS(previous_ledger); // out: LedgerPropose
@@ -643,6 +650,7 @@ JSS(treenode_track_size); // out: GetCounts
JSS(trusted); // out: UnlList
JSS(trusted_validator_keys); // out: ValidatorList
JSS(tx); // out: STTx, AccountTx*
JSS(txroot);
JSS(tx_blob); // in/out: Submit,
// in: TransactionSign, AccountTx*
JSS(tx_hash); // in: TransactionEntry
@@ -697,6 +705,7 @@ JSS(validation_private_key); // out: ValidationCreate
JSS(validation_public_key); // out: ValidationCreate, ValidationSeed
JSS(validation_quorum); // out: NetworkOPs
JSS(validation_seed); // out: ValidationCreate, ValidationSeed
JSS(validation);
JSS(validations); // out: AmendmentTableImpl
JSS(validator_sites); // out: ValidatorSites
JSS(value); // out: STAmount