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/SetRegularKey.cpp
src/ripple/app/tx/impl/SetHook.cpp src/ripple/app/tx/impl/SetHook.cpp
src/ripple/app/tx/impl/ClaimReward.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/Invoke.cpp
src/ripple/app/tx/impl/SetSignerList.cpp src/ripple/app/tx/impl/SetSignerList.cpp
src/ripple/app/tx/impl/SetTrust.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; 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 // Server

View File

@@ -755,8 +755,14 @@ TxQ::apply(
auto const account = (*tx)[sfAccount]; auto const account = (*tx)[sfAccount];
Keylet const accountKey{keylet::account(account)}; Keylet const accountKey{keylet::account(account)};
auto const sleAccount = view.read(accountKey); auto const sleAccount = view.read(accountKey);
if (!sleAccount) if (!sleAccount)
{
if (tx->getTxnType() == ttIMPORT)
return {telCAN_NOT_QUEUE_IMPORT, false};
return {terNO_ACCOUNT, false}; return {terNO_ACCOUNT, false};
}
// If the transaction needs a Ticket is that Ticket in the ledger? // If the transaction needs a Ticket is that Ticket in the ledger?
SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]); SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
@@ -1809,19 +1815,27 @@ TxQ::tryDirectApply(
auto const account = (*tx)[sfAccount]; auto const account = (*tx)[sfAccount];
auto const sleAccount = view.read(keylet::account(account)); 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. // Don't attempt to direct apply if the account is not in the ledger.
if (!sleAccount) if (!sleAccount && !isFirstImport)
return {}; return {};
SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]); std::optional<SeqProxy> txSeqProx;
SeqProxy const txSeqProx = tx->getSeqProxy();
// Can only directly apply if the transaction sequence matches the account if (!isFirstImport)
// sequence or if the transaction uses a ticket. {
if (txSeqProx.isSeq() && txSeqProx != acctSeqProx) SeqProxy const acctSeqProx = SeqProxy::sequence((*sleAccount)[sfSequence]);
return {}; 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_); std::lock_guard lock(mutex_);
return getRequiredFeeLevel( return getRequiredFeeLevel(
view, flags, feeMetrics_.getSnapshot(), lock); view, flags, feeMetrics_.getSnapshot(), lock);
@@ -1856,11 +1870,12 @@ TxQ::tryDirectApply(
if (accountIter != byAccount_.end()) if (accountIter != byAccount_.end())
{ {
TxQAccount& txQAcct = accountIter->second; TxQAccount& txQAcct = accountIter->second;
if (auto const existingIter = if (txSeqProx)
txQAcct.transactions.find(txSeqProx);
existingIter != txQAcct.transactions.end())
{ {
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/STArray.h>
#include <ripple/protocol/SystemParameters.h> #include <ripple/protocol/SystemParameters.h>
#include <ripple/protocol/nftPageMask.h> #include <ripple/protocol/nftPageMask.h>
#include <ripple/app/tx/impl/Import.h>
namespace ripple { namespace ripple {
@@ -134,16 +135,47 @@ XRPNotCreated::visitEntry(
break; break;
} }
} }
if (!before && after->getType() == ltACCOUNT_ROOT)
accountsCreated_++;
} }
bool bool
XRPNotCreated::finalize( XRPNotCreated::finalize(
STTx const&, STTx const& tx,
TER const, TER const,
XRPAmount const fee, XRPAmount const fee,
ReadView const&, ReadView const& view,
beast::Journal const& j) 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 // The net change should never be positive, as this would mean that the
// transaction created XRP out of thin air. That's not possible. // transaction created XRP out of thin air. That's not possible.
if (drops_ > 0) if (drops_ > 0)
@@ -493,7 +525,8 @@ ValidNewAccountRoot::finalize(
} }
// From this point on we know exactly one account was created. // 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{ std::uint32_t const startingSeq{
view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1}; view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1};

View File

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

View File

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

View File

@@ -41,6 +41,7 @@
#include <ripple/app/tx/impl/SetSignerList.h> #include <ripple/app/tx/impl/SetSignerList.h>
#include <ripple/app/tx/impl/SetTrust.h> #include <ripple/app/tx/impl/SetTrust.h>
#include <ripple/app/tx/impl/SetHook.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/Invoke.h>
#include <ripple/app/tx/impl/URIToken.h> #include <ripple/app/tx/impl/URIToken.h>
@@ -155,6 +156,8 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<NFTokenAcceptOffer>(ctx); return invoke_preflight_helper<NFTokenAcceptOffer>(ctx);
case ttCLAIM_REWARD: case ttCLAIM_REWARD:
return invoke_preflight_helper<ClaimReward>(ctx); return invoke_preflight_helper<ClaimReward>(ctx);
case ttIMPORT:
return invoke_preflight_helper<Import>(ctx);
case ttINVOKE: case ttINVOKE:
return invoke_preflight_helper<Invoke>(ctx); return invoke_preflight_helper<Invoke>(ctx);
case ttURITOKEN_MINT: case ttURITOKEN_MINT:
@@ -194,7 +197,9 @@ invoke_preclaim(PreclaimContext const& ctx)
if (result != tesSUCCESS) if (result != tesSUCCESS)
return result; 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) if (result != tesSUCCESS)
return result; return result;
@@ -270,6 +275,8 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<NFTokenAcceptOffer>(ctx); return invoke_preclaim<NFTokenAcceptOffer>(ctx);
case ttCLAIM_REWARD: case ttCLAIM_REWARD:
return invoke_preclaim<ClaimReward>(ctx); return invoke_preclaim<ClaimReward>(ctx);
case ttIMPORT:
return invoke_preclaim<Import>(ctx);
case ttINVOKE: case ttINVOKE:
return invoke_preclaim<Invoke>(ctx); return invoke_preclaim<Invoke>(ctx);
case ttURITOKEN_MINT: case ttURITOKEN_MINT:
@@ -346,6 +353,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return NFTokenAcceptOffer::calculateBaseFee(view, tx); return NFTokenAcceptOffer::calculateBaseFee(view, tx);
case ttCLAIM_REWARD: case ttCLAIM_REWARD:
return ClaimReward::calculateBaseFee(view, tx); return ClaimReward::calculateBaseFee(view, tx);
case ttIMPORT:
return Import::calculateBaseFee(view, tx);
case ttINVOKE: case ttINVOKE:
return Invoke::calculateBaseFee(view, tx); return Invoke::calculateBaseFee(view, tx);
case ttURITOKEN_MINT: case ttURITOKEN_MINT:
@@ -516,6 +525,10 @@ invoke_apply(ApplyContext& ctx)
ClaimReward p(ctx); ClaimReward p(ctx);
return p(); return p();
} }
case ttIMPORT: {
Import p(ctx);
return p();
}
case ttINVOKE: { case ttINVOKE: {
Invoke p(ctx); Invoke p(ctx);
return p(); 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> IPS_FIXED; // Fixed Peer IPs from rippled.cfg.
std::vector<std::string> SNTP_SERVERS; // SNTP servers 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 }; enum StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
StartUpType START_UP = NORMAL; StartUpType START_UP = NORMAL;

View File

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

View File

@@ -837,7 +837,7 @@ Config::loadFromString(std::string const& fileContents)
BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp); BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
// Do not load trusted validator configuration for standalone mode // Do not load trusted validator configuration for standalone mode
if (!RUN_STANDALONE) do
{ {
// If a file was explicitly specified, then throw if the // If a file was explicitly specified, then throw if the
// path is malformed or if the file does not exist or is // path is malformed or if the file does not exist or is
@@ -905,6 +905,21 @@ Config::loadFromString(std::string const& fileContents)
} }
auto iniFile = parseIniFile(data, true); 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); auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
@@ -929,6 +944,8 @@ Config::loadFromString(std::string const& fileContents)
if (valListKeys) if (valListKeys)
section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys); section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
if (!entries && !valKeyEntries && !valListKeys) if (!entries && !valKeyEntries && !valListKeys)
Throw<std::runtime_error>( Throw<std::runtime_error>(
"The file specified in [" SECTION_VALIDATORS_FILE "The file specified in [" SECTION_VALIDATORS_FILE
@@ -943,6 +960,7 @@ Config::loadFromString(std::string const& fileContents)
validatorsFile.string()); validatorsFile.string());
} }
// Consolidate [validator_keys] and [validators] // Consolidate [validator_keys] and [validators]
section(SECTION_VALIDATORS) section(SECTION_VALIDATORS)
.append(section(SECTION_VALIDATOR_KEYS).lines()); .append(section(SECTION_VALIDATOR_KEYS).lines());
@@ -954,7 +972,7 @@ Config::loadFromString(std::string const& fileContents)
"[" + std::string(SECTION_VALIDATOR_LIST_KEYS) + "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
"] config section is missing"); "] config section is missing");
} }
} } while (0);
{ {
auto const part = section("features"); 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 // 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 // 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. // 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. /** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated 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 fixUniversalNumber;
extern uint256 const fixNonFungibleTokensV1_2; extern uint256 const fixNonFungibleTokensV1_2;
extern uint256 const fixNFTokenRemint; extern uint256 const fixNFTokenRemint;
extern uint256 const featureImport;
} // namespace ripple } // namespace ripple

View File

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

View File

@@ -64,7 +64,9 @@ enum TELcodes : TERUnderlyingType {
telWRONG_NETWORK, telWRONG_NETWORK,
telREQUIRES_NETWORK_ID, telREQUIRES_NETWORK_ID,
telNETWORK_ID_MAKES_TX_NON_CANONICAL, 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, tefTOO_BIG,
tefNO_TICKET, tefNO_TICKET,
tefNFTOKEN_IS_NOT_TRANSFERABLE, tefNFTOKEN_IS_NOT_TRANSFERABLE,
tefPAST_IMPORT_SEQ,
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -332,7 +335,8 @@ enum TECcodes : TERUnderlyingType {
tecXCHAIN_ACCOUNT_CREATE_TOO_MANY = 183, // RESERVED - XCHAIN tecXCHAIN_ACCOUNT_CREATE_TOO_MANY = 183, // RESERVED - XCHAIN
tecXCHAIN_PAYMENT_FAILED = 184, // RESERVED - XCHAIN tecXCHAIN_PAYMENT_FAILED = 184, // RESERVED - XCHAIN
tecXCHAIN_SELF_COMMIT = 185, // 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_CREATE_SELL_OFFER = 48,
ttURITOKEN_CANCEL_SELL_OFFER = 49, 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 /** This transaction resets accumulator/counters and claims a reward for holding an average balance
* from a specified hook */ * from a specified hook */
ttCLAIM_REWARD = 98, 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(BalanceRewards, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(URIToken, 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 // The following amendments are obsolete, but must remain supported
// because they could potentially get enabled. // because they could potentially get enabled.

View File

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

View File

@@ -156,6 +156,8 @@ CONSTRUCT_TYPED_SFIELD(sfEmitGeneration, "EmitGeneration", UINT32,
CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32, 49); CONSTRUCT_TYPED_SFIELD(sfLockCount, "LockCount", UINT32, 49);
CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50); CONSTRUCT_TYPED_SFIELD(sfFirstNFTokenSequence, "FirstNFTokenSequence", UINT32, 50);
CONSTRUCT_TYPED_SFIELD(sfImportSequence, "ImportSequence", UINT32, 97);
CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98); CONSTRUCT_TYPED_SFIELD(sfRewardTime, "RewardTime", UINT32, 98);
CONSTRUCT_TYPED_SFIELD(sfRewardLgrFirst, "RewardLgrFirst", UINT32, 99); CONSTRUCT_TYPED_SFIELD(sfRewardLgrFirst, "RewardLgrFirst", UINT32, 99);
CONSTRUCT_TYPED_SFIELD(sfRewardLgrLast, "RewardLgrLast", UINT32, 100); 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(sfHookCallbackFee, "HookCallbackFee", AMOUNT, 20);
CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT, 21); CONSTRUCT_TYPED_SFIELD(sfLockedBalance, "LockedBalance", AMOUNT, 21);
// Reserve 20 & 21 for Hooks
// currency amount (fees) // currency amount (fees)
CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22); CONSTRUCT_TYPED_SFIELD(sfBaseFeeDrops, "BaseFeeDrops", AMOUNT, 22);
CONSTRUCT_TYPED_SFIELD(sfReserveBaseDrops, "ReserveBaseDrops", AMOUNT, 23); 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(tefNO_AUTH_REQUIRED, "Auth is not required."),
MAKE_ERROR(tefNOT_MULTI_SIGNING, "Account has no appropriate list of multi-signers."), 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_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(tefWRONG_PRIOR, "This previous transaction does not match."),
MAKE_ERROR(tefBAD_AUTH_MASTER, "Auth for unclaimed account needs correct master key."), MAKE_ERROR(tefBAD_AUTH_MASTER, "Auth for unclaimed account needs correct master key."),
MAKE_ERROR(tefINVARIANT_FAILED, "Fee claim violated invariants for the transaction."), 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(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(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(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(temMALFORMED, "Malformed transaction."),
MAKE_ERROR(temBAD_AMOUNT, "Can only send positive amounts."), MAKE_ERROR(temBAD_AMOUNT, "Can only send positive amounts."),

View File

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

View File

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