mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-04 10:48:15 +00:00
XChainBridge: Introduce sidechain support (XLS-38): (#4292)
A bridge connects two blockchains: a locking chain and an issuing chain (also called a mainchain and a sidechain). Both are independent ledgers, with their own validators and potentially their own custom transactions. Importantly, there is a way to move assets from the locking chain to the issuing chain and a way to return those assets from the issuing chain back to the locking chain: the bridge. This key operation is called a cross-chain transfer. A cross-chain transfer is not a single transaction. It happens on two chains, requires multiple transactions, and involves an additional server type called a "witness". A bridge does not exchange assets between two ledgers. Instead, it locks assets on one ledger (the "locking chain") and represents those assets with wrapped assets on another chain (the "issuing chain"). A good model to keep in mind is a box with an infinite supply of wrapped assets. Putting an asset from the locking chain into the box will release a wrapped asset onto the issuing chain. Putting a wrapped asset from the issuing chain back into the box will release one of the existing locking chain assets back onto the locking chain. There is no other way to get assets into or out of the box. Note that there is no way for the box to "run out of" wrapped assets - it has an infinite supply. Co-authored-by: Gregory Popovitch <greg7mdp@gmail.com>
This commit is contained in:
@@ -18,8 +18,10 @@ macro(group_sources curdir)
|
||||
endmacro()
|
||||
|
||||
macro (exclude_from_default target_)
|
||||
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
|
||||
if(target_)
|
||||
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||
set_target_properties (${target_} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
|
||||
endif()
|
||||
endmacro ()
|
||||
|
||||
macro (exclude_if_included target_)
|
||||
|
||||
@@ -103,7 +103,9 @@ target_sources (xrpl_core PRIVATE
|
||||
src/ripple/protocol/impl/STObject.cpp
|
||||
src/ripple/protocol/impl/STParsedJSON.cpp
|
||||
src/ripple/protocol/impl/STPathSet.cpp
|
||||
src/ripple/protocol/impl/STXChainBridge.cpp
|
||||
src/ripple/protocol/impl/STTx.cpp
|
||||
src/ripple/protocol/impl/XChainAttestations.cpp
|
||||
src/ripple/protocol/impl/STValidation.cpp
|
||||
src/ripple/protocol/impl/STVar.cpp
|
||||
src/ripple/protocol/impl/STVector256.cpp
|
||||
@@ -243,6 +245,7 @@ install (
|
||||
src/ripple/protocol/Indexes.h
|
||||
src/ripple/protocol/InnerObjectFormats.h
|
||||
src/ripple/protocol/Issue.h
|
||||
src/ripple/protocol/json_get_or_throw.h
|
||||
src/ripple/protocol/KeyType.h
|
||||
src/ripple/protocol/Keylet.h
|
||||
src/ripple/protocol/KnownFormats.h
|
||||
@@ -273,6 +276,8 @@ install (
|
||||
src/ripple/protocol/STParsedJSON.h
|
||||
src/ripple/protocol/STPathSet.h
|
||||
src/ripple/protocol/STTx.h
|
||||
src/ripple/protocol/XChainAttestations.h
|
||||
src/ripple/protocol/STXChainBridge.h
|
||||
src/ripple/protocol/STValidation.h
|
||||
src/ripple/protocol/STVector256.h
|
||||
src/ripple/protocol/SecretKey.h
|
||||
@@ -528,6 +533,7 @@ target_sources (rippled PRIVATE
|
||||
src/ripple/app/tx/impl/SetRegularKey.cpp
|
||||
src/ripple/app/tx/impl/SetSignerList.cpp
|
||||
src/ripple/app/tx/impl/SetTrust.cpp
|
||||
src/ripple/app/tx/impl/XChainBridge.cpp
|
||||
src/ripple/app/tx/impl/SignerEntries.cpp
|
||||
src/ripple/app/tx/impl/Taker.cpp
|
||||
src/ripple/app/tx/impl/Transactor.cpp
|
||||
@@ -809,6 +815,7 @@ if (tests)
|
||||
src/test/app/ReducedOffer_test.cpp
|
||||
src/test/app/Regression_test.cpp
|
||||
src/test/app/SHAMapStore_test.cpp
|
||||
src/test/app/XChain_test.cpp
|
||||
src/test/app/SetAuth_test.cpp
|
||||
src/test/app/SetRegularKey_test.cpp
|
||||
src/test/app/SetTrust_test.cpp
|
||||
@@ -927,6 +934,7 @@ if (tests)
|
||||
src/test/jtx/impl/acctdelete.cpp
|
||||
src/test/jtx/impl/account_txn_id.cpp
|
||||
src/test/jtx/impl/amount.cpp
|
||||
src/test/jtx/impl/attester.cpp
|
||||
src/test/jtx/impl/balance.cpp
|
||||
src/test/jtx/impl/check.cpp
|
||||
src/test/jtx/impl/delivermin.cpp
|
||||
@@ -948,6 +956,7 @@ if (tests)
|
||||
src/test/jtx/impl/regkey.cpp
|
||||
src/test/jtx/impl/sendmax.cpp
|
||||
src/test/jtx/impl/seq.cpp
|
||||
src/test/jtx/impl/xchain_bridge.cpp
|
||||
src/test/jtx/impl/sig.cpp
|
||||
src/test/jtx/impl/tag.cpp
|
||||
src/test/jtx/impl/ticket.cpp
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
package/container targets - (optional)
|
||||
#]===================================================================]
|
||||
|
||||
# Early return if the `containers` directory is missing,
|
||||
# e.g. when we are building a Conan package.
|
||||
if(NOT EXISTS containers)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (is_root_project)
|
||||
if (NOT DOCKER)
|
||||
find_program (DOCKER docker)
|
||||
|
||||
@@ -19,7 +19,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
# make GIT_COMMIT_HASH define available to all sources
|
||||
find_package(Git)
|
||||
if(Git_FOUND)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=40
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git describe --always --abbrev=40
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
|
||||
if(gch)
|
||||
set(GIT_COMMIT_HASH "${gch}")
|
||||
|
||||
@@ -1279,6 +1279,12 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const& forcedRange = config().FORCED_LEDGER_RANGE_PRESENT)
|
||||
{
|
||||
m_ledgerMaster->setLedgerRangePresent(
|
||||
forcedRange->first, forcedRange->second);
|
||||
}
|
||||
|
||||
if (!config().reporting())
|
||||
m_orderBookDB.setup(getLedgerMaster().getCurrentLedger());
|
||||
|
||||
|
||||
@@ -378,8 +378,13 @@ run(int argc, char** argv)
|
||||
"Override the minimum validation quorum.")(
|
||||
"reportingReadOnly", "Run in read-only reporting mode")(
|
||||
"silent", "No output to the console after startup.")(
|
||||
"standalone,a", "Run with no peers.")("verbose,v", "Verbose logging.")(
|
||||
"version", "Display the build version.");
|
||||
"standalone,a", "Run with no peers.")("verbose,v", "Verbose logging.")
|
||||
|
||||
("force_ledger_present_range",
|
||||
po::value<std::string>(),
|
||||
"Specify the range of present ledgers for testing purposes. Min and "
|
||||
"max values are comma separated.")(
|
||||
"version", "Display the build version.");
|
||||
|
||||
po::options_description data("Ledger/Data Options");
|
||||
data.add_options()("import", importText.c_str())(
|
||||
@@ -602,6 +607,51 @@ run(int argc, char** argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.contains("force_ledger_present_range"))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto const r = [&vm]() -> std::vector<std::uint32_t> {
|
||||
std::vector<std::string> strVec;
|
||||
boost::split(
|
||||
strVec,
|
||||
vm["force_ledger_present_range"].as<std::string>(),
|
||||
boost::algorithm::is_any_of(","));
|
||||
std::vector<std::uint32_t> result;
|
||||
for (auto& s : strVec)
|
||||
{
|
||||
boost::trim(s);
|
||||
if (!s.empty())
|
||||
result.push_back(std::stoi(s));
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
if (r.size() == 2)
|
||||
{
|
||||
if (r[0] > r[1])
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Invalid force_ledger_present_range parameter");
|
||||
}
|
||||
config->FORCED_LEDGER_RANGE_PRESENT.emplace(r[0], r[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Invalid force_ledger_present_range parameter");
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << "invalid 'force_ledger_present_range' parameter. The "
|
||||
"parameter must be two numbers separated by a comma. "
|
||||
"The first number must be <= the second."
|
||||
<< std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (vm.count("start"))
|
||||
{
|
||||
config->START_UP = Config::FRESH;
|
||||
|
||||
@@ -607,12 +607,14 @@ private:
|
||||
void
|
||||
pubValidatedTransaction(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
AcceptedLedgerTx const& transaction);
|
||||
AcceptedLedgerTx const& transaction,
|
||||
bool last);
|
||||
|
||||
void
|
||||
pubAccountTransaction(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
AcceptedLedgerTx const& transaction);
|
||||
AcceptedLedgerTx const& transaction,
|
||||
bool last);
|
||||
|
||||
void
|
||||
pubProposedAccountTransaction(
|
||||
@@ -3031,7 +3033,8 @@ NetworkOPsImp::pubLedger(std::shared_ptr<ReadView const> const& lpAccepted)
|
||||
for (auto const& accTx : *alpAccepted)
|
||||
{
|
||||
JLOG(m_journal.trace()) << "pubAccepted: " << accTx->getJson();
|
||||
pubValidatedTransaction(lpAccepted, *accTx);
|
||||
pubValidatedTransaction(
|
||||
lpAccepted, *accTx, accTx == *(--alpAccepted->end()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3138,7 +3141,8 @@ NetworkOPsImp::transJson(
|
||||
void
|
||||
NetworkOPsImp::pubValidatedTransaction(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
const AcceptedLedgerTx& transaction)
|
||||
const AcceptedLedgerTx& transaction,
|
||||
bool last)
|
||||
{
|
||||
auto const& stTxn = transaction.getTxn();
|
||||
|
||||
@@ -3187,13 +3191,14 @@ NetworkOPsImp::pubValidatedTransaction(
|
||||
if (transaction.getResult() == tesSUCCESS)
|
||||
app_.getOrderBookDB().processTxn(ledger, transaction, jvObj);
|
||||
|
||||
pubAccountTransaction(ledger, transaction);
|
||||
pubAccountTransaction(ledger, transaction, last);
|
||||
}
|
||||
|
||||
void
|
||||
NetworkOPsImp::pubAccountTransaction(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
AcceptedLedgerTx const& transaction)
|
||||
AcceptedLedgerTx const& transaction,
|
||||
bool last)
|
||||
{
|
||||
hash_set<InfoSub::pointer> notify;
|
||||
int iProposed = 0;
|
||||
@@ -3301,6 +3306,9 @@ NetworkOPsImp::pubAccountTransaction(
|
||||
for (InfoSub::ref isrListener : notify)
|
||||
isrListener->send(jvObj, true);
|
||||
|
||||
if (last)
|
||||
jvObj[jss::account_history_boundary] = true;
|
||||
|
||||
assert(!jvObj.isMember(jss::account_history_tx_stream));
|
||||
for (auto& info : accountHistoryNotify)
|
||||
{
|
||||
@@ -3699,8 +3707,11 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo)
|
||||
|
||||
auto const& txns = dbResult->first;
|
||||
marker = dbResult->second;
|
||||
for (auto const& [tx, meta] : txns)
|
||||
size_t num_txns = txns.size();
|
||||
for (size_t i = 0; i < num_txns; ++i)
|
||||
{
|
||||
auto const& [tx, meta] = txns[i];
|
||||
|
||||
if (!tx || !meta)
|
||||
{
|
||||
JLOG(m_journal.debug())
|
||||
@@ -3735,6 +3746,11 @@ NetworkOPsImp::addAccountHistoryJob(SubAccountHistoryInfoWeak subInfo)
|
||||
*stTxn, meta->getResultTER(), true, curTxLedger);
|
||||
jvTx[jss::meta] = meta->getJson(JsonOptions::none);
|
||||
jvTx[jss::account_history_tx_index] = txHistoryIndex--;
|
||||
|
||||
if (i + 1 == num_txns ||
|
||||
txns[i + 1].first->getLedger() != tx->getLedger())
|
||||
jvTx[jss::account_history_boundary] = true;
|
||||
|
||||
RPC::insertDeliveredAmount(
|
||||
jvTx[jss::meta], *curTxLedger, stTxn, *meta);
|
||||
if (isFirstTx(tx, meta))
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/STArray.h>
|
||||
#include <ripple/protocol/SystemParameters.h>
|
||||
#include <ripple/protocol/TxFormats.h>
|
||||
#include <ripple/protocol/nftPageMask.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -387,6 +388,9 @@ LedgerEntryTypesMatch::visitEntry(
|
||||
case ltNFTOKEN_PAGE:
|
||||
case ltNFTOKEN_OFFER:
|
||||
case ltAMM:
|
||||
case ltBRIDGE:
|
||||
case ltXCHAIN_OWNED_CLAIM_ID:
|
||||
case ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID:
|
||||
break;
|
||||
default:
|
||||
invalidTypeAdded_ = true;
|
||||
@@ -487,7 +491,9 @@ ValidNewAccountRoot::finalize(
|
||||
}
|
||||
|
||||
// From this point on we know exactly one account was created.
|
||||
if ((tx.getTxnType() == ttPAYMENT || tx.getTxnType() == ttAMM_CREATE) &&
|
||||
if ((tx.getTxnType() == ttPAYMENT || tx.getTxnType() == ttAMM_CREATE ||
|
||||
tx.getTxnType() == ttXCHAIN_ADD_CLAIM_ATTESTATION ||
|
||||
tx.getTxnType() == ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION) &&
|
||||
result == tesSUCCESS)
|
||||
{
|
||||
std::uint32_t const startingSeq{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
@@ -300,7 +301,7 @@ public:
|
||||
class ValidNewAccountRoot
|
||||
{
|
||||
std::uint32_t accountsCreated_ = 0;
|
||||
std::uint32_t accountSeq_ = 0; // Only meaningful if accountsCreated_ > 0
|
||||
std::uint32_t accountSeq_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
2292
src/ripple/app/tx/impl/XChainBridge.cpp
Normal file
2292
src/ripple/app/tx/impl/XChainBridge.cpp
Normal file
File diff suppressed because it is too large
Load Diff
255
src/ripple/app/tx/impl/XChainBridge.h
Normal file
255
src/ripple/app/tx/impl/XChainBridge.h
Normal file
@@ -0,0 +1,255 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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_XCHAINBRIDGE_H_INCLUDED
|
||||
#define RIPPLE_TX_XCHAINBRIDGE_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
constexpr size_t xbridgeMaxAccountCreateClaims = 128;
|
||||
|
||||
// Attach a new bridge to a door account. Once this is done, the cross-chain
|
||||
// transfer transactions may be used to transfer funds from this account.
|
||||
class XChainCreateBridge : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit XChainCreateBridge(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
class BridgeModify : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit BridgeModify(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Claim funds from a `XChainCommit` transaction. This is normally not needed,
|
||||
// but may be used to handle transaction failures or if the destination account
|
||||
// was not specified in the `XChainCommit` transaction. It may only be used
|
||||
// after a quorum of signatures have been sent from the witness servers.
|
||||
//
|
||||
// If the transaction succeeds in moving funds, the referenced `XChainClaimID`
|
||||
// ledger object will be destroyed. This prevents transaction replay. If the
|
||||
// transaction fails, the `XChainClaimID` will not be destroyed and the
|
||||
// transaction may be re-run with different parameters.
|
||||
class XChainClaim : public Transactor
|
||||
{
|
||||
public:
|
||||
// Blocker since we cannot accurately calculate the consequences
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit XChainClaim(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Put assets into trust on the locking-chain so they may be wrapped on the
|
||||
// issuing-chain, or return wrapped assets on the issuing-chain so they can be
|
||||
// unlocked on the locking-chain. The second step in a cross-chain transfer.
|
||||
class XChainCommit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
explicit XChainCommit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Create a new claim id owned by the account. This is the first step in a
|
||||
// cross-chain transfer. The claim id must be created on the destination chain
|
||||
// before the `XChainCommit` transaction (which must reference this number) can
|
||||
// be sent on the source chain. The account that will send the `XChainCommit` on
|
||||
// the source chain must be specified in this transaction (see note on the
|
||||
// `SourceAccount` field in the `XChainClaimID` ledger object for
|
||||
// justification). The actual sequence number must be retrieved from a validated
|
||||
// ledger.
|
||||
class XChainCreateClaimID : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit XChainCreateClaimID(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Provide attestations from a witness server attesting to events on
|
||||
// the other chain. The signatures must be from one of the keys on the door's
|
||||
// signer's list at the time the signature was provided. However, if the
|
||||
// signature list changes between the time the signature was submitted and the
|
||||
// quorum is reached, the new signature set is used and some of the currently
|
||||
// collected signatures may be removed. Also note the reward is only sent to
|
||||
// accounts that have keys on the current list.
|
||||
class XChainAddClaimAttestation : public Transactor
|
||||
{
|
||||
public:
|
||||
// Blocker since we cannot accurately calculate the consequences
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit XChainAddClaimAttestation(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
class XChainAddAccountCreateAttestation : public Transactor
|
||||
{
|
||||
public:
|
||||
// Blocker since we cannot accurately calculate the consequences
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit XChainAddAccountCreateAttestation(ApplyContext& ctx)
|
||||
: Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This is a special transaction used for creating accounts through a
|
||||
// cross-chain transfer. A normal cross-chain transfer requires a "chain claim
|
||||
// id" (which requires an existing account on the destination chain). One
|
||||
// purpose of the "chain claim id" is to prevent transaction replay. For this
|
||||
// transaction, we use a different mechanism: the accounts must be claimed on
|
||||
// the destination chain in the same order that the `XChainCreateAccountCommit`
|
||||
// transactions occurred on the source chain.
|
||||
//
|
||||
// This transaction can only be used for XRP to XRP bridges.
|
||||
//
|
||||
// IMPORTANT: This transaction should only be enabled if the witness
|
||||
// attestations will be reliably delivered to the destination chain. If the
|
||||
// signatures are not delivered (for example, the chain relies on user wallets
|
||||
// to collect signatures) then account creation would be blocked for all
|
||||
// transactions that happened after the one waiting on attestations. This could
|
||||
// be used maliciously. To disable this transaction on XRP to XRP bridges, the
|
||||
// bridge's `MinAccountCreateAmount` should not be present.
|
||||
//
|
||||
// Note: If this account already exists, the XRP is transferred to the existing
|
||||
// account. However, note that unlike the `XChainCommit` transaction, there is
|
||||
// no error handling mechanism. If the claim transaction fails, there is no
|
||||
// mechanism for refunds. The funds are permanently lost. This transaction
|
||||
// should still only be used for account creation.
|
||||
class XChainCreateAccountCommit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit XChainCreateAccountCommit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -47,6 +47,8 @@
|
||||
#include <ripple/app/tx/impl/SetRegularKey.h>
|
||||
#include <ripple/app/tx/impl/SetSignerList.h>
|
||||
#include <ripple/app/tx/impl/SetTrust.h>
|
||||
#include <ripple/app/tx/impl/XChainBridge.h>
|
||||
#include <ripple/protocol/TxFormats.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -168,6 +170,23 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
return invoke_preflight_helper<AMMBid>(ctx);
|
||||
case ttAMM_DELETE:
|
||||
return invoke_preflight_helper<AMMDelete>(ctx);
|
||||
case ttXCHAIN_CREATE_BRIDGE:
|
||||
return invoke_preflight_helper<XChainCreateBridge>(ctx);
|
||||
case ttXCHAIN_MODIFY_BRIDGE:
|
||||
return invoke_preflight_helper<BridgeModify>(ctx);
|
||||
case ttXCHAIN_CREATE_CLAIM_ID:
|
||||
return invoke_preflight_helper<XChainCreateClaimID>(ctx);
|
||||
case ttXCHAIN_COMMIT:
|
||||
return invoke_preflight_helper<XChainCommit>(ctx);
|
||||
case ttXCHAIN_CLAIM:
|
||||
return invoke_preflight_helper<XChainClaim>(ctx);
|
||||
case ttXCHAIN_ADD_CLAIM_ATTESTATION:
|
||||
return invoke_preflight_helper<XChainAddClaimAttestation>(ctx);
|
||||
case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION:
|
||||
return invoke_preflight_helper<XChainAddAccountCreateAttestation>(
|
||||
ctx);
|
||||
case ttXCHAIN_ACCOUNT_CREATE_COMMIT:
|
||||
return invoke_preflight_helper<XChainCreateAccountCommit>(ctx);
|
||||
default:
|
||||
assert(false);
|
||||
return {temUNKNOWN, TxConsequences{temUNKNOWN}};
|
||||
@@ -283,6 +302,22 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
return invoke_preclaim<AMMBid>(ctx);
|
||||
case ttAMM_DELETE:
|
||||
return invoke_preclaim<AMMDelete>(ctx);
|
||||
case ttXCHAIN_CREATE_BRIDGE:
|
||||
return invoke_preclaim<XChainCreateBridge>(ctx);
|
||||
case ttXCHAIN_MODIFY_BRIDGE:
|
||||
return invoke_preclaim<BridgeModify>(ctx);
|
||||
case ttXCHAIN_CREATE_CLAIM_ID:
|
||||
return invoke_preclaim<XChainCreateClaimID>(ctx);
|
||||
case ttXCHAIN_COMMIT:
|
||||
return invoke_preclaim<XChainCommit>(ctx);
|
||||
case ttXCHAIN_CLAIM:
|
||||
return invoke_preclaim<XChainClaim>(ctx);
|
||||
case ttXCHAIN_ACCOUNT_CREATE_COMMIT:
|
||||
return invoke_preclaim<XChainCreateAccountCommit>(ctx);
|
||||
case ttXCHAIN_ADD_CLAIM_ATTESTATION:
|
||||
return invoke_preclaim<XChainAddClaimAttestation>(ctx);
|
||||
case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION:
|
||||
return invoke_preclaim<XChainAddAccountCreateAttestation>(ctx);
|
||||
default:
|
||||
assert(false);
|
||||
return temUNKNOWN;
|
||||
@@ -360,6 +395,23 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
return AMMBid::calculateBaseFee(view, tx);
|
||||
case ttAMM_DELETE:
|
||||
return AMMDelete::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_CREATE_BRIDGE:
|
||||
return XChainCreateBridge::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_MODIFY_BRIDGE:
|
||||
return BridgeModify::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_CREATE_CLAIM_ID:
|
||||
return XChainCreateClaimID::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_COMMIT:
|
||||
return XChainCommit::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_CLAIM:
|
||||
return XChainClaim::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_ADD_CLAIM_ATTESTATION:
|
||||
return XChainAddClaimAttestation::calculateBaseFee(view, tx);
|
||||
case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION:
|
||||
return XChainAddAccountCreateAttestation::calculateBaseFee(
|
||||
view, tx);
|
||||
case ttXCHAIN_ACCOUNT_CREATE_COMMIT:
|
||||
return XChainCreateAccountCommit::calculateBaseFee(view, tx);
|
||||
default:
|
||||
assert(false);
|
||||
return XRPAmount{0};
|
||||
@@ -540,6 +592,38 @@ invoke_apply(ApplyContext& ctx)
|
||||
AMMDelete p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_CREATE_BRIDGE: {
|
||||
XChainCreateBridge p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_MODIFY_BRIDGE: {
|
||||
BridgeModify p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_CREATE_CLAIM_ID: {
|
||||
XChainCreateClaimID p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_COMMIT: {
|
||||
XChainCommit p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_CLAIM: {
|
||||
XChainClaim p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_ADD_CLAIM_ATTESTATION: {
|
||||
XChainAddClaimAttestation p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION: {
|
||||
XChainAddAccountCreateAttestation p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttXCHAIN_ACCOUNT_CREATE_COMMIT: {
|
||||
XChainCreateAccountCommit p(ctx);
|
||||
return p();
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
return {temUNKNOWN, false};
|
||||
|
||||
@@ -134,7 +134,6 @@ public:
|
||||
|
||||
class Stream;
|
||||
|
||||
private:
|
||||
/* Scoped ostream-based container for writing messages to a Journal. */
|
||||
class ScopedStream
|
||||
{
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
#include <ripple/beast/net/IPEndpoint.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/protocol/SystemParameters.h> // VFALCO Breaks levelization
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/filesystem.hpp> // VFALCO FIX: This include should not be here
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
@@ -37,6 +39,7 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
@@ -295,6 +298,14 @@ public:
|
||||
|
||||
// First, attempt to load the latest ledger directly from disk.
|
||||
bool FAST_LOAD = false;
|
||||
// When starting rippled with existing database it do not know it has those
|
||||
// ledgers locally until the server naturally tries to backfill. This makes
|
||||
// is difficult to test some functionality (in particular performance
|
||||
// testing sidechains). With this variable the user is able to force rippled
|
||||
// to consider the ledger range to be present. It should be used for testing
|
||||
// only.
|
||||
std::optional<std::pair<std::uint32_t, std::uint32_t>>
|
||||
FORCED_LEDGER_RANGE_PRESENT;
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
#include <ripple/json/json_forwards.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
|
||||
#define SOCI_USE_BOOST
|
||||
#include <soci/soci.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <ripple/basics/UnorderedContainers.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/json_get_or_throw.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
@@ -123,6 +125,21 @@ initAccountIdCache(std::size_t count);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace Json {
|
||||
template <>
|
||||
inline ripple::AccountID
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
|
||||
std::string const b58 = getOrThrow<std::string>(v, field);
|
||||
if (auto const r = parseBase58<AccountID>(b58))
|
||||
return *r;
|
||||
Throw<JsonTypeMismatchError>(field.getJsonName(), "AccountID");
|
||||
}
|
||||
} // namespace Json
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace std {
|
||||
|
||||
@@ -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 = 61;
|
||||
static constexpr std::size_t numFeatures = 62;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -348,6 +348,7 @@ extern uint256 const fixNonFungibleTokensV1_2;
|
||||
extern uint256 const fixNFTokenRemint;
|
||||
extern uint256 const fixReducedOffersV1;
|
||||
extern uint256 const featureClawback;
|
||||
extern uint256 const featureXChainBridge;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <ripple/protocol/LedgerFormats.h>
|
||||
#include <ripple/protocol/Protocol.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <cstdint>
|
||||
@@ -270,6 +271,15 @@ amm(Issue const& issue1, Issue const& issue2) noexcept;
|
||||
Keylet
|
||||
amm(uint256 const& amm) noexcept;
|
||||
|
||||
Keylet
|
||||
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType);
|
||||
|
||||
Keylet
|
||||
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||
|
||||
Keylet
|
||||
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq);
|
||||
|
||||
} // namespace keylet
|
||||
|
||||
// Everything below is deprecated and should be removed in favor of keylets:
|
||||
|
||||
@@ -91,6 +91,13 @@ enum LedgerEntryType : std::uint16_t
|
||||
*/
|
||||
ltOFFER = 0x006f,
|
||||
|
||||
|
||||
/** The ledger object which lists details about sidechains.
|
||||
|
||||
\sa keylet::bridge
|
||||
*/
|
||||
ltBRIDGE = 0x0069,
|
||||
|
||||
/** A ledger object that contains a list of ledger hashes.
|
||||
|
||||
This type is used to store the ledger hashes which the protocol uses
|
||||
@@ -109,6 +116,18 @@ enum LedgerEntryType : std::uint16_t
|
||||
*/
|
||||
ltAMENDMENTS = 0x0066,
|
||||
|
||||
/** A claim id for a cross chain transaction.
|
||||
|
||||
\sa keylet::xChainClaimID
|
||||
*/
|
||||
ltXCHAIN_OWNED_CLAIM_ID = 0x0071,
|
||||
|
||||
/** A claim id for a cross chain create account transaction.
|
||||
|
||||
\sa keylet::xChainCreateAccountClaimID
|
||||
*/
|
||||
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID = 0x0074,
|
||||
|
||||
/** The ledger object which lists the network's fee settings.
|
||||
|
||||
\note This is a singleton: only one such object exists in the ledger.
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#include <ripple/protocol/KeyType.h>
|
||||
#include <ripple/protocol/STExchange.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/protocol/json_get_or_throw.h>
|
||||
#include <ripple/protocol/tokens.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -268,4 +270,27 @@ calcAccountID(PublicKey const& pk);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Json {
|
||||
template <>
|
||||
inline ripple::PublicKey
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
std::string const b58 = getOrThrow<std::string>(v, field);
|
||||
if (auto pubKeyBlob = strUnHex(b58); publicKeyType(makeSlice(*pubKeyBlob)))
|
||||
{
|
||||
return PublicKey{makeSlice(*pubKeyBlob)};
|
||||
}
|
||||
for (auto const tokenType :
|
||||
{TokenType::NodePublic, TokenType::AccountPublic})
|
||||
{
|
||||
if (auto const pk = parseBase58<PublicKey>(tokenType, b58))
|
||||
return *pk;
|
||||
}
|
||||
Throw<JsonTypeMismatchError>(field.getJsonName(), "PublicKey");
|
||||
}
|
||||
} // namespace Json
|
||||
|
||||
#endif
|
||||
|
||||
@@ -49,6 +49,7 @@ template <int>
|
||||
class STBitString;
|
||||
template <class>
|
||||
class STInteger;
|
||||
class STXChainBridge;
|
||||
class STVector256;
|
||||
|
||||
enum SerializedTypeID {
|
||||
@@ -79,6 +80,7 @@ enum SerializedTypeID {
|
||||
STI_UINT384 = 22,
|
||||
STI_UINT512 = 23,
|
||||
STI_ISSUE = 24,
|
||||
STI_XCHAIN_BRIDGE = 25,
|
||||
|
||||
// high level types
|
||||
// cannot be serialized inside other types
|
||||
@@ -318,6 +320,7 @@ using SF_AMOUNT = TypedField<STAmount>;
|
||||
using SF_ISSUE = TypedField<STIssue>;
|
||||
using SF_VL = TypedField<STBlob>;
|
||||
using SF_VECTOR256 = TypedField<STVector256>;
|
||||
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -332,6 +335,7 @@ extern SField const sfMetadata;
|
||||
extern SF_UINT8 const sfCloseResolution;
|
||||
extern SF_UINT8 const sfMethod;
|
||||
extern SF_UINT8 const sfTransactionResult;
|
||||
extern SF_UINT8 const sfWasLockingChainSend;
|
||||
|
||||
// 8-bit integers (uncommon)
|
||||
extern SF_UINT8 const sfTickSize;
|
||||
@@ -424,6 +428,9 @@ extern SF_UINT64 const sfHookOn;
|
||||
extern SF_UINT64 const sfHookInstructionCount;
|
||||
extern SF_UINT64 const sfHookReturnCode;
|
||||
extern SF_UINT64 const sfReferenceCount;
|
||||
extern SF_UINT64 const sfXChainClaimID;
|
||||
extern SF_UINT64 const sfXChainAccountCreateCount;
|
||||
extern SF_UINT64 const sfXChainAccountClaimCount;
|
||||
|
||||
// 128-bit
|
||||
extern SF_UINT128 const sfEmailHash;
|
||||
@@ -499,6 +506,8 @@ extern SF_AMOUNT const sfLPTokenIn;
|
||||
extern SF_AMOUNT const sfBaseFeeDrops;
|
||||
extern SF_AMOUNT const sfReserveBaseDrops;
|
||||
extern SF_AMOUNT const sfReserveIncrementDrops;
|
||||
extern SF_AMOUNT const sfSignatureReward;
|
||||
extern SF_AMOUNT const sfMinAccountCreateAmount;
|
||||
|
||||
// variable length (common)
|
||||
extern SF_VL const sfPublicKey;
|
||||
@@ -541,6 +550,12 @@ extern SF_ACCOUNT const sfEmitCallback;
|
||||
|
||||
// account (uncommon)
|
||||
extern SF_ACCOUNT const sfHookAccount;
|
||||
extern SF_ACCOUNT const sfOtherChainSource;
|
||||
extern SF_ACCOUNT const sfOtherChainDestination;
|
||||
extern SF_ACCOUNT const sfAttestationSignerAccount;
|
||||
extern SF_ACCOUNT const sfAttestationRewardAccount;
|
||||
extern SF_ACCOUNT const sfLockingChainDoor;
|
||||
extern SF_ACCOUNT const sfIssuingChainDoor;
|
||||
|
||||
// path set
|
||||
extern SField const sfPaths;
|
||||
@@ -548,6 +563,11 @@ extern SField const sfPaths;
|
||||
// issue
|
||||
extern SF_ISSUE const sfAsset;
|
||||
extern SF_ISSUE const sfAsset2;
|
||||
extern SF_ISSUE const sfLockingChainIssue;
|
||||
extern SF_ISSUE const sfIssuingChainIssue;
|
||||
|
||||
// bridge
|
||||
extern SF_XCHAIN_BRIDGE const sfXChainBridge;
|
||||
|
||||
// vector of 256-bit
|
||||
extern SF_VECTOR256 const sfIndexes;
|
||||
@@ -582,6 +602,10 @@ extern SField const sfHookExecution;
|
||||
extern SField const sfHookDefinition;
|
||||
extern SField const sfHookParameter;
|
||||
extern SField const sfHookGrant;
|
||||
extern SField const sfXChainClaimProofSig;
|
||||
extern SField const sfXChainCreateAccountProofSig;
|
||||
extern SField const sfXChainClaimAttestationCollectionElement;
|
||||
extern SField const sfXChainCreateAccountAttestationCollectionElement;
|
||||
|
||||
// array of objects (common)
|
||||
// ARRAY/1 is reserved for end of array
|
||||
@@ -604,6 +628,8 @@ extern SField const sfDisabledValidators;
|
||||
extern SField const sfHookExecutions;
|
||||
extern SField const sfHookParameters;
|
||||
extern SField const sfHookGrants;
|
||||
extern SField const sfXChainClaimAttestations;
|
||||
extern SField const sfXChainCreateAccountAttestations;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
STAccount&
|
||||
operator=(AccountID const& value);
|
||||
|
||||
AccountID
|
||||
AccountID const&
|
||||
value() const noexcept;
|
||||
|
||||
void
|
||||
@@ -86,7 +86,7 @@ STAccount::operator=(AccountID const& value)
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AccountID
|
||||
inline AccountID const&
|
||||
STAccount::value() const noexcept
|
||||
{
|
||||
return value_;
|
||||
@@ -99,6 +99,36 @@ STAccount::setValue(AccountID const& v)
|
||||
default_ = false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator==(STAccount const& lhs, STAccount const& rhs)
|
||||
{
|
||||
return lhs.value() == rhs.value();
|
||||
}
|
||||
|
||||
inline auto
|
||||
operator<(STAccount const& lhs, STAccount const& rhs)
|
||||
{
|
||||
return lhs.value() < rhs.value();
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator==(STAccount const& lhs, AccountID const& rhs)
|
||||
{
|
||||
return lhs.value() == rhs;
|
||||
}
|
||||
|
||||
inline auto
|
||||
operator<(STAccount const& lhs, AccountID const& rhs)
|
||||
{
|
||||
return lhs.value() < rhs;
|
||||
}
|
||||
|
||||
inline auto
|
||||
operator<(AccountID const& lhs, STAccount const& rhs)
|
||||
{
|
||||
return lhs < rhs.value();
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STBase.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/json_get_or_throw.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -125,6 +126,8 @@ public:
|
||||
|
||||
explicit STAmount(std::uint64_t mantissa = 0, bool negative = false);
|
||||
|
||||
explicit STAmount(SField const& name, STAmount const& amt);
|
||||
|
||||
STAmount(
|
||||
Issue const& issue,
|
||||
std::uint64_t mantissa = 0,
|
||||
@@ -582,4 +585,18 @@ private:
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace Json {
|
||||
template <>
|
||||
inline ripple::STAmount
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
Json::StaticString const& key = field.getJsonName();
|
||||
if (!v.isMember(key))
|
||||
Throw<JsonMissingKeyError>(key);
|
||||
Json::Value const& inner = v[key];
|
||||
return amountFromJson(field, inner);
|
||||
}
|
||||
} // namespace Json
|
||||
#endif
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
STArray&
|
||||
operator=(STArray&&);
|
||||
|
||||
STArray(SField const& f, int n);
|
||||
STArray(SField const& f, std::size_t n);
|
||||
STArray(SerialIter& sit, SField const& f, int depth = 0);
|
||||
explicit STArray(int n);
|
||||
explicit STArray(SField const& f);
|
||||
|
||||
@@ -100,6 +100,10 @@ STIssue::value() const noexcept
|
||||
inline void
|
||||
STIssue::setIssue(Issue const& issue)
|
||||
{
|
||||
if (isXRP(issue_.currency) != isXRP(issue_.account))
|
||||
Throw<std::runtime_error>(
|
||||
"invalid issue: currency and account native mismatch");
|
||||
|
||||
issue_ = issue;
|
||||
}
|
||||
|
||||
|
||||
235
src/ripple/protocol/STXChainBridge.h
Normal file
235
src/ripple/protocol/STXChainBridge.h
Normal file
@@ -0,0 +1,235 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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_PROTOCOL_STXCHAINBRIDGE_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_STXCHAINBRIDGE_H_INCLUDED
|
||||
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/STBase.h>
|
||||
#include <ripple/protocol/STIssue.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Serializer;
|
||||
class STObject;
|
||||
|
||||
class STXChainBridge final : public STBase
|
||||
{
|
||||
STAccount lockingChainDoor_{sfLockingChainDoor};
|
||||
STIssue lockingChainIssue_{sfLockingChainIssue};
|
||||
STAccount issuingChainDoor_{sfIssuingChainDoor};
|
||||
STIssue issuingChainIssue_{sfIssuingChainIssue};
|
||||
|
||||
public:
|
||||
using value_type = STXChainBridge;
|
||||
|
||||
enum class ChainType { locking, issuing };
|
||||
|
||||
static ChainType
|
||||
otherChain(ChainType ct);
|
||||
|
||||
static ChainType
|
||||
srcChain(bool wasLockingChainSend);
|
||||
|
||||
static ChainType
|
||||
dstChain(bool wasLockingChainSend);
|
||||
|
||||
STXChainBridge();
|
||||
|
||||
explicit STXChainBridge(SField const& name);
|
||||
|
||||
STXChainBridge(STXChainBridge const& rhs) = default;
|
||||
|
||||
STXChainBridge(STObject const& o);
|
||||
|
||||
STXChainBridge(
|
||||
AccountID const& srcChainDoor,
|
||||
Issue const& srcChainIssue,
|
||||
AccountID const& dstChainDoor,
|
||||
Issue const& dstChainIssue);
|
||||
|
||||
explicit STXChainBridge(Json::Value const& v);
|
||||
|
||||
explicit STXChainBridge(SField const& name, Json::Value const& v);
|
||||
|
||||
explicit STXChainBridge(SerialIter& sit, SField const& name);
|
||||
|
||||
STXChainBridge&
|
||||
operator=(STXChainBridge const& rhs) = default;
|
||||
|
||||
std::string
|
||||
getText() const override;
|
||||
|
||||
STObject
|
||||
toSTObject() const;
|
||||
|
||||
AccountID const&
|
||||
lockingChainDoor() const;
|
||||
|
||||
Issue const&
|
||||
lockingChainIssue() const;
|
||||
|
||||
AccountID const&
|
||||
issuingChainDoor() const;
|
||||
|
||||
Issue const&
|
||||
issuingChainIssue() const;
|
||||
|
||||
AccountID const&
|
||||
door(ChainType ct) const;
|
||||
|
||||
Issue const&
|
||||
issue(ChainType ct) const;
|
||||
|
||||
SerializedTypeID
|
||||
getSType() const override;
|
||||
|
||||
Json::Value getJson(JsonOptions) const override;
|
||||
|
||||
void
|
||||
add(Serializer& s) const override;
|
||||
|
||||
bool
|
||||
isEquivalent(const STBase& t) const override;
|
||||
|
||||
bool
|
||||
isDefault() const override;
|
||||
|
||||
value_type const&
|
||||
value() const noexcept;
|
||||
|
||||
private:
|
||||
static std::unique_ptr<STXChainBridge>
|
||||
construct(SerialIter&, SField const& name);
|
||||
|
||||
STBase*
|
||||
copy(std::size_t n, void* buf) const override;
|
||||
STBase*
|
||||
move(std::size_t n, void* buf) override;
|
||||
|
||||
friend bool
|
||||
operator==(STXChainBridge const& lhs, STXChainBridge const& rhs);
|
||||
|
||||
friend bool
|
||||
operator<(STXChainBridge const& lhs, STXChainBridge const& rhs);
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(STXChainBridge const& lhs, STXChainBridge const& rhs)
|
||||
{
|
||||
return std::tie(
|
||||
lhs.lockingChainDoor_,
|
||||
lhs.lockingChainIssue_,
|
||||
lhs.issuingChainDoor_,
|
||||
lhs.issuingChainIssue_) ==
|
||||
std::tie(
|
||||
rhs.lockingChainDoor_,
|
||||
rhs.lockingChainIssue_,
|
||||
rhs.issuingChainDoor_,
|
||||
rhs.issuingChainIssue_);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator<(STXChainBridge const& lhs, STXChainBridge const& rhs)
|
||||
{
|
||||
return std::tie(
|
||||
lhs.lockingChainDoor_,
|
||||
lhs.lockingChainIssue_,
|
||||
lhs.issuingChainDoor_,
|
||||
lhs.issuingChainIssue_) <
|
||||
std::tie(
|
||||
rhs.lockingChainDoor_,
|
||||
rhs.lockingChainIssue_,
|
||||
rhs.issuingChainDoor_,
|
||||
rhs.issuingChainIssue_);
|
||||
}
|
||||
|
||||
inline AccountID const&
|
||||
STXChainBridge::lockingChainDoor() const
|
||||
{
|
||||
return lockingChainDoor_.value();
|
||||
};
|
||||
|
||||
inline Issue const&
|
||||
STXChainBridge::lockingChainIssue() const
|
||||
{
|
||||
return lockingChainIssue_.value();
|
||||
};
|
||||
|
||||
inline AccountID const&
|
||||
STXChainBridge::issuingChainDoor() const
|
||||
{
|
||||
return issuingChainDoor_.value();
|
||||
};
|
||||
|
||||
inline Issue const&
|
||||
STXChainBridge::issuingChainIssue() const
|
||||
{
|
||||
return issuingChainIssue_.value();
|
||||
};
|
||||
|
||||
inline STXChainBridge::value_type const&
|
||||
STXChainBridge::value() const noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AccountID const&
|
||||
STXChainBridge::door(ChainType ct) const
|
||||
{
|
||||
if (ct == ChainType::locking)
|
||||
return lockingChainDoor();
|
||||
return issuingChainDoor();
|
||||
}
|
||||
|
||||
inline Issue const&
|
||||
STXChainBridge::issue(ChainType ct) const
|
||||
{
|
||||
if (ct == ChainType::locking)
|
||||
return lockingChainIssue();
|
||||
return issuingChainIssue();
|
||||
}
|
||||
|
||||
inline STXChainBridge::ChainType
|
||||
STXChainBridge::otherChain(ChainType ct)
|
||||
{
|
||||
if (ct == ChainType::locking)
|
||||
return ChainType::issuing;
|
||||
return ChainType::locking;
|
||||
}
|
||||
|
||||
inline STXChainBridge::ChainType
|
||||
STXChainBridge::srcChain(bool wasLockingChainSend)
|
||||
{
|
||||
if (wasLockingChainSend)
|
||||
return ChainType::locking;
|
||||
return ChainType::issuing;
|
||||
}
|
||||
|
||||
inline STXChainBridge::ChainType
|
||||
STXChainBridge::dstChain(bool wasLockingChainSend)
|
||||
{
|
||||
if (wasLockingChainSend)
|
||||
return ChainType::issuing;
|
||||
return ChainType::locking;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -125,6 +125,13 @@ enum TEMcodes : TERUnderlyingType {
|
||||
temBAD_NFTOKEN_TRANSFER_FEE,
|
||||
|
||||
temBAD_AMM_TOKENS,
|
||||
|
||||
temXCHAIN_EQUAL_DOOR_ACCOUNTS,
|
||||
temXCHAIN_BAD_PROOF,
|
||||
temXCHAIN_BRIDGE_BAD_ISSUES,
|
||||
temXCHAIN_BRIDGE_NONDOOR_OWNER,
|
||||
temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT,
|
||||
temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -303,7 +310,24 @@ enum TECcodes : TERUnderlyingType {
|
||||
tecAMM_EMPTY = 166,
|
||||
tecAMM_NOT_EMPTY = 167,
|
||||
tecAMM_ACCOUNT = 168,
|
||||
tecINCOMPLETE = 169
|
||||
tecINCOMPLETE = 169,
|
||||
tecXCHAIN_BAD_TRANSFER_ISSUE = 170,
|
||||
tecXCHAIN_NO_CLAIM_ID = 171,
|
||||
tecXCHAIN_BAD_CLAIM_ID = 172,
|
||||
tecXCHAIN_CLAIM_NO_QUORUM = 173,
|
||||
tecXCHAIN_PROOF_UNKNOWN_KEY = 174,
|
||||
tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE = 175,
|
||||
tecXCHAIN_WRONG_CHAIN = 176,
|
||||
tecXCHAIN_REWARD_MISMATCH = 177,
|
||||
tecXCHAIN_NO_SIGNERS_LIST = 178,
|
||||
tecXCHAIN_SENDING_ACCOUNT_MISMATCH = 179,
|
||||
tecXCHAIN_INSUFF_CREATE_AMOUNT = 180,
|
||||
tecXCHAIN_ACCOUNT_CREATE_PAST = 181,
|
||||
tecXCHAIN_ACCOUNT_CREATE_TOO_MANY = 182,
|
||||
tecXCHAIN_PAYMENT_FAILED = 183,
|
||||
tecXCHAIN_SELF_COMMIT = 184,
|
||||
tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 185,
|
||||
tecXCHAIN_CREATE_ACCOUNT_DISABLED = 186,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -181,6 +181,10 @@ constexpr std::uint32_t tfDepositSubTx =
|
||||
constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx);
|
||||
constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx);
|
||||
|
||||
// BridgeModify flags:
|
||||
constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000;
|
||||
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount);
|
||||
|
||||
// clang-format on
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -160,6 +160,31 @@ enum TxType : std::uint16_t
|
||||
/** This transaction type deletes AMM in the empty state */
|
||||
ttAMM_DELETE = 40,
|
||||
|
||||
/** This transactions creates a crosschain sequence number */
|
||||
ttXCHAIN_CREATE_CLAIM_ID = 41,
|
||||
|
||||
/** This transactions initiates a crosschain transaction */
|
||||
ttXCHAIN_COMMIT = 42,
|
||||
|
||||
/** This transaction completes a crosschain transaction */
|
||||
ttXCHAIN_CLAIM = 43,
|
||||
|
||||
/** This transaction initiates a crosschain account create transaction */
|
||||
ttXCHAIN_ACCOUNT_CREATE_COMMIT = 44,
|
||||
|
||||
/** This transaction adds an attestation to a claimid*/
|
||||
ttXCHAIN_ADD_CLAIM_ATTESTATION = 45,
|
||||
|
||||
/** This transaction adds an attestation to a claimid*/
|
||||
ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION = 46,
|
||||
|
||||
/** This transaction modifies a sidechain */
|
||||
ttXCHAIN_MODIFY_BRIDGE = 47,
|
||||
|
||||
/** This transactions creates a sidechain */
|
||||
ttXCHAIN_CREATE_BRIDGE = 48,
|
||||
|
||||
|
||||
/** This system-generated transaction type is used to update the status of the various amendments.
|
||||
|
||||
For details, see: https://xrpl.org/amendments.html
|
||||
|
||||
514
src/ripple/protocol/XChainAttestations.h
Normal file
514
src/ripple/protocol/XChainAttestations.h
Normal file
@@ -0,0 +1,514 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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_PROTOCOL_STXATTESTATIONS_H_INCLUDED
|
||||
#define RIPPLE_PROTOCOL_STXATTESTATIONS_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Buffer.h>
|
||||
#include <ripple/basics/Expected.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STBase.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/SecretKey.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/container/vector.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace Attestations {
|
||||
|
||||
struct AttestationBase
|
||||
{
|
||||
// Account associated with the public key
|
||||
AccountID attestationSignerAccount;
|
||||
// Public key from the witness server attesting to the event
|
||||
PublicKey publicKey;
|
||||
// Signature from the witness server attesting to the event
|
||||
Buffer signature;
|
||||
// Account on the sending chain that triggered the event (sent the
|
||||
// transaction)
|
||||
AccountID sendingAccount;
|
||||
// Amount transfered on the sending chain
|
||||
STAmount sendingAmount;
|
||||
// Account on the destination chain that collects a share of the attestation
|
||||
// reward
|
||||
AccountID rewardAccount;
|
||||
// Amount was transfered on the locking chain
|
||||
bool wasLockingChainSend;
|
||||
|
||||
explicit AttestationBase(
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
Buffer signature_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_);
|
||||
|
||||
AttestationBase(AttestationBase const&) = default;
|
||||
|
||||
virtual ~AttestationBase() = default;
|
||||
|
||||
AttestationBase&
|
||||
operator=(AttestationBase const&) = default;
|
||||
|
||||
// verify that the signature attests to the data.
|
||||
bool
|
||||
verify(STXChainBridge const& bridge) const;
|
||||
|
||||
protected:
|
||||
explicit AttestationBase(STObject const& o);
|
||||
explicit AttestationBase(Json::Value const& v);
|
||||
|
||||
[[nodiscard]] static bool
|
||||
equalHelper(AttestationBase const& lhs, AttestationBase const& rhs);
|
||||
|
||||
[[nodiscard]] static bool
|
||||
sameEventHelper(AttestationBase const& lhs, AttestationBase const& rhs);
|
||||
|
||||
void
|
||||
addHelper(STObject& o) const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] virtual std::vector<std::uint8_t>
|
||||
message(STXChainBridge const& bridge) const = 0;
|
||||
};
|
||||
|
||||
// Attest to a regular cross-chain transfer
|
||||
struct AttestationClaim : AttestationBase
|
||||
{
|
||||
std::uint64_t claimID;
|
||||
std::optional<AccountID> dst;
|
||||
|
||||
explicit AttestationClaim(
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
Buffer signature_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t claimID_,
|
||||
std::optional<AccountID> const& dst_);
|
||||
|
||||
explicit AttestationClaim(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
SecretKey const& secretKey_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t claimID_,
|
||||
std::optional<AccountID> const& dst_);
|
||||
|
||||
explicit AttestationClaim(STObject const& o);
|
||||
explicit AttestationClaim(Json::Value const& v);
|
||||
|
||||
[[nodiscard]] STObject
|
||||
toSTObject() const;
|
||||
|
||||
// return true if the two attestations attest to the same thing
|
||||
[[nodiscard]] bool
|
||||
sameEvent(AttestationClaim const& rhs) const;
|
||||
|
||||
[[nodiscard]] static std::vector<std::uint8_t>
|
||||
message(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<AccountID> const& dst);
|
||||
|
||||
[[nodiscard]] bool
|
||||
validAmounts() const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::vector<std::uint8_t>
|
||||
message(STXChainBridge const& bridge) const override;
|
||||
|
||||
friend bool
|
||||
operator==(AttestationClaim const& lhs, AttestationClaim const& rhs);
|
||||
};
|
||||
|
||||
struct CmpByClaimID
|
||||
{
|
||||
bool
|
||||
operator()(AttestationClaim const& lhs, AttestationClaim const& rhs) const
|
||||
{
|
||||
return lhs.claimID < rhs.claimID;
|
||||
}
|
||||
};
|
||||
|
||||
// Attest to a cross-chain transfer that creates an account
|
||||
struct AttestationCreateAccount : AttestationBase
|
||||
{
|
||||
// createCount on the sending chain. This is the value of the `CreateCount`
|
||||
// field of the bridge on the sending chain when the transaction was
|
||||
// executed.
|
||||
std::uint64_t createCount;
|
||||
// Account to create on the destination chain
|
||||
AccountID toCreate;
|
||||
// Total amount of the reward pool
|
||||
STAmount rewardAmount;
|
||||
|
||||
explicit AttestationCreateAccount(STObject const& o);
|
||||
|
||||
explicit AttestationCreateAccount(Json::Value const& v);
|
||||
|
||||
explicit AttestationCreateAccount(
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
Buffer signature_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
STAmount const& rewardAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t createCount_,
|
||||
AccountID const& toCreate_);
|
||||
|
||||
explicit AttestationCreateAccount(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
SecretKey const& secretKey_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
STAmount const& rewardAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t createCount_,
|
||||
AccountID const& toCreate_);
|
||||
|
||||
[[nodiscard]] STObject
|
||||
toSTObject() const;
|
||||
|
||||
// return true if the two attestations attest to the same thing
|
||||
[[nodiscard]] bool
|
||||
sameEvent(AttestationCreateAccount const& rhs) const;
|
||||
|
||||
friend bool
|
||||
operator==(
|
||||
AttestationCreateAccount const& lhs,
|
||||
AttestationCreateAccount const& rhs);
|
||||
|
||||
[[nodiscard]] static std::vector<std::uint8_t>
|
||||
message(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
STAmount const& rewardAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
AccountID const& dst);
|
||||
|
||||
[[nodiscard]] bool
|
||||
validAmounts() const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::vector<std::uint8_t>
|
||||
message(STXChainBridge const& bridge) const override;
|
||||
};
|
||||
|
||||
struct CmpByCreateCount
|
||||
{
|
||||
bool
|
||||
operator()(
|
||||
AttestationCreateAccount const& lhs,
|
||||
AttestationCreateAccount const& rhs) const
|
||||
{
|
||||
return lhs.createCount < rhs.createCount;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Attestations
|
||||
|
||||
// Result when checking when two attestation match.
|
||||
enum class AttestationMatch {
|
||||
// One of the fields doesn't match, and it isn't the dst field
|
||||
nonDstMismatch,
|
||||
// all of the fields match, except the dst field
|
||||
matchExceptDst,
|
||||
// all of the fields match
|
||||
match
|
||||
};
|
||||
|
||||
struct XChainClaimAttestation
|
||||
{
|
||||
using TSignedAttestation = Attestations::AttestationClaim;
|
||||
static SField const& ArrayFieldName;
|
||||
|
||||
AccountID keyAccount;
|
||||
PublicKey publicKey;
|
||||
STAmount amount;
|
||||
AccountID rewardAccount;
|
||||
bool wasLockingChainSend;
|
||||
std::optional<AccountID> dst;
|
||||
|
||||
struct MatchFields
|
||||
{
|
||||
STAmount amount;
|
||||
bool wasLockingChainSend;
|
||||
std::optional<AccountID> dst;
|
||||
MatchFields(TSignedAttestation const& att);
|
||||
MatchFields(
|
||||
STAmount const& a,
|
||||
bool b,
|
||||
std::optional<AccountID> const& d)
|
||||
: amount{a}, wasLockingChainSend{b}, dst{d}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
explicit XChainClaimAttestation(
|
||||
AccountID const& keyAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
STAmount const& amount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::optional<AccountID> const& dst);
|
||||
|
||||
explicit XChainClaimAttestation(
|
||||
STAccount const& keyAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
STAmount const& amount_,
|
||||
STAccount const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::optional<STAccount> const& dst);
|
||||
|
||||
explicit XChainClaimAttestation(TSignedAttestation const& claimAtt);
|
||||
|
||||
explicit XChainClaimAttestation(STObject const& o);
|
||||
|
||||
explicit XChainClaimAttestation(Json::Value const& v);
|
||||
|
||||
AttestationMatch
|
||||
match(MatchFields const& rhs) const;
|
||||
|
||||
[[nodiscard]] STObject
|
||||
toSTObject() const;
|
||||
|
||||
friend bool
|
||||
operator==(
|
||||
XChainClaimAttestation const& lhs,
|
||||
XChainClaimAttestation const& rhs);
|
||||
};
|
||||
|
||||
struct XChainCreateAccountAttestation
|
||||
{
|
||||
using TSignedAttestation = Attestations::AttestationCreateAccount;
|
||||
static SField const& ArrayFieldName;
|
||||
|
||||
AccountID keyAccount;
|
||||
PublicKey publicKey;
|
||||
STAmount amount;
|
||||
STAmount rewardAmount;
|
||||
AccountID rewardAccount;
|
||||
bool wasLockingChainSend;
|
||||
AccountID dst;
|
||||
|
||||
struct MatchFields
|
||||
{
|
||||
STAmount amount;
|
||||
STAmount rewardAmount;
|
||||
bool wasLockingChainSend;
|
||||
AccountID dst;
|
||||
|
||||
MatchFields(TSignedAttestation const& att);
|
||||
};
|
||||
|
||||
explicit XChainCreateAccountAttestation(
|
||||
AccountID const& keyAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
STAmount const& amount_,
|
||||
STAmount const& rewardAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
AccountID const& dst_);
|
||||
|
||||
explicit XChainCreateAccountAttestation(TSignedAttestation const& claimAtt);
|
||||
|
||||
explicit XChainCreateAccountAttestation(STObject const& o);
|
||||
|
||||
explicit XChainCreateAccountAttestation(Json::Value const& v);
|
||||
|
||||
[[nodiscard]] STObject
|
||||
toSTObject() const;
|
||||
|
||||
AttestationMatch
|
||||
match(MatchFields const& rhs) const;
|
||||
|
||||
friend bool
|
||||
operator==(
|
||||
XChainCreateAccountAttestation const& lhs,
|
||||
XChainCreateAccountAttestation const& rhs);
|
||||
};
|
||||
|
||||
// Attestations from witness servers for a particular claimid and bridge.
|
||||
// Only one attestation per signature is allowed.
|
||||
template <class TAttestation>
|
||||
class XChainAttestationsBase
|
||||
{
|
||||
public:
|
||||
using AttCollection = std::vector<TAttestation>;
|
||||
|
||||
private:
|
||||
// Set a max number of allowed attestations to limit the amount of memory
|
||||
// allocated and processing time. This number is much larger than the actual
|
||||
// number of attestation a server would ever expect.
|
||||
static constexpr std::uint32_t maxAttestations = 256;
|
||||
AttCollection attestations_;
|
||||
|
||||
protected:
|
||||
// Prevent slicing to the base class
|
||||
~XChainAttestationsBase() = default;
|
||||
|
||||
public:
|
||||
XChainAttestationsBase() = default;
|
||||
XChainAttestationsBase(XChainAttestationsBase const& rhs) = default;
|
||||
XChainAttestationsBase&
|
||||
operator=(XChainAttestationsBase const& rhs) = default;
|
||||
|
||||
explicit XChainAttestationsBase(AttCollection&& sigs);
|
||||
|
||||
explicit XChainAttestationsBase(Json::Value const& v);
|
||||
|
||||
explicit XChainAttestationsBase(STArray const& arr);
|
||||
|
||||
[[nodiscard]] STArray
|
||||
toSTArray() const;
|
||||
|
||||
typename AttCollection::const_iterator
|
||||
begin() const;
|
||||
|
||||
typename AttCollection::const_iterator
|
||||
end() const;
|
||||
|
||||
typename AttCollection::iterator
|
||||
begin();
|
||||
|
||||
typename AttCollection::iterator
|
||||
end();
|
||||
|
||||
template <class F>
|
||||
std::size_t
|
||||
erase_if(F&& f);
|
||||
|
||||
std::size_t
|
||||
size() const;
|
||||
|
||||
bool
|
||||
empty() const;
|
||||
|
||||
AttCollection const&
|
||||
attestations() const;
|
||||
|
||||
template <class T>
|
||||
void
|
||||
emplace_back(T&& att);
|
||||
|
||||
// verify that all the signatures attest to transaction data.
|
||||
[[nodiscard]] bool
|
||||
verify() const;
|
||||
|
||||
protected:
|
||||
// Return the message that was expected to be signed by the attesters given
|
||||
// the data to be proved.
|
||||
[[nodiscard]] std::vector<std::uint8_t>
|
||||
message() const;
|
||||
};
|
||||
|
||||
template <class TAttestation>
|
||||
[[nodiscard]] inline bool
|
||||
operator==(
|
||||
XChainAttestationsBase<TAttestation> const& lhs,
|
||||
XChainAttestationsBase<TAttestation> const& rhs)
|
||||
{
|
||||
return lhs.attestations() == rhs.attestations();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
inline typename XChainAttestationsBase<TAttestation>::AttCollection const&
|
||||
XChainAttestationsBase<TAttestation>::attestations() const
|
||||
{
|
||||
return attestations_;
|
||||
};
|
||||
|
||||
template <class TAttestation>
|
||||
template <class T>
|
||||
inline void
|
||||
XChainAttestationsBase<TAttestation>::emplace_back(T&& att)
|
||||
{
|
||||
attestations_.emplace_back(std::forward<T>(att));
|
||||
};
|
||||
|
||||
template <class TAttestation>
|
||||
template <class F>
|
||||
inline std::size_t
|
||||
XChainAttestationsBase<TAttestation>::erase_if(F&& f)
|
||||
{
|
||||
return std::erase_if(attestations_, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
inline std::size_t
|
||||
XChainAttestationsBase<TAttestation>::size() const
|
||||
{
|
||||
return attestations_.size();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
inline bool
|
||||
XChainAttestationsBase<TAttestation>::empty() const
|
||||
{
|
||||
return attestations_.empty();
|
||||
}
|
||||
|
||||
class XChainClaimAttestations final
|
||||
: public XChainAttestationsBase<XChainClaimAttestation>
|
||||
{
|
||||
using TBase = XChainAttestationsBase<XChainClaimAttestation>;
|
||||
using TBase::TBase;
|
||||
};
|
||||
|
||||
class XChainCreateAccountAttestations final
|
||||
: public XChainAttestationsBase<XChainCreateAccountAttestation>
|
||||
{
|
||||
using TBase = XChainAttestationsBase<XChainCreateAccountAttestation>;
|
||||
using TBase::TBase;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif // STXCHAINATTESTATIONS_H_
|
||||
@@ -455,6 +455,7 @@ REGISTER_FIX (fixNFTokenRemint, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FIX (fixReducedOffersV1, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(Clawback, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(AMM, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(XChainBridge, Supported::no, VoteBehavior::DefaultNo);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/LedgerFormats.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/SeqProxy.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/protocol/nftPageMask.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
@@ -64,6 +68,9 @@ enum class LedgerNameSpace : std::uint16_t {
|
||||
NFTOKEN_BUY_OFFERS = 'h',
|
||||
NFTOKEN_SELL_OFFERS = 'i',
|
||||
AMM = 'A',
|
||||
BRIDGE = 'H',
|
||||
XCHAIN_CLAIM_ID = 'Q',
|
||||
XCHAIN_CREATE_ACCOUNT_CLAIM_ID = 'K',
|
||||
|
||||
// No longer used or supported. Left here to reserve the space
|
||||
// to avoid accidental reuse.
|
||||
@@ -389,6 +396,47 @@ amm(uint256 const& id) noexcept
|
||||
return {ltAMM, id};
|
||||
}
|
||||
|
||||
Keylet
|
||||
bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType)
|
||||
{
|
||||
// A door account can support multiple bridges. On the locking chain
|
||||
// there can only be one bridge per lockingChainCurrency. On the issuing
|
||||
// chain there can only be one bridge per issuingChainCurrency.
|
||||
auto const& issue = bridge.issue(chainType);
|
||||
return {
|
||||
ltBRIDGE,
|
||||
indexHash(
|
||||
LedgerNameSpace::BRIDGE, bridge.door(chainType), issue.currency)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
xChainClaimID(STXChainBridge const& bridge, std::uint64_t seq)
|
||||
{
|
||||
return {
|
||||
ltXCHAIN_OWNED_CLAIM_ID,
|
||||
indexHash(
|
||||
LedgerNameSpace::XCHAIN_CLAIM_ID,
|
||||
bridge.lockingChainDoor(),
|
||||
bridge.lockingChainIssue(),
|
||||
bridge.issuingChainDoor(),
|
||||
bridge.issuingChainIssue(),
|
||||
seq)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
xChainCreateAccountClaimID(STXChainBridge const& bridge, std::uint64_t seq)
|
||||
{
|
||||
return {
|
||||
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID,
|
||||
indexHash(
|
||||
LedgerNameSpace::XCHAIN_CREATE_ACCOUNT_CLAIM_ID,
|
||||
bridge.lockingChainDoor(),
|
||||
bridge.lockingChainIssue(),
|
||||
bridge.issuingChainDoor(),
|
||||
bridge.issuingChainIssue(),
|
||||
seq)};
|
||||
}
|
||||
|
||||
} // namespace keylet
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/InnerObjectFormats.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/SOTemplate.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -70,12 +72,62 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
|
||||
add(sfAuctionSlot.jsonName.c_str(),
|
||||
sfAuctionSlot.getCode(),
|
||||
{{sfAccount, soeREQUIRED},
|
||||
{sfExpiration, soeREQUIRED},
|
||||
{sfDiscountedFee, soeDEFAULT},
|
||||
{sfPrice, soeREQUIRED},
|
||||
{sfAuthAccounts, soeOPTIONAL}});
|
||||
|
||||
add(sfXChainClaimAttestationCollectionElement.jsonName.c_str(),
|
||||
sfXChainClaimAttestationCollectionElement.getCode(),
|
||||
{
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfSignature, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfExpiration, soeREQUIRED},
|
||||
{sfDiscountedFee, soeDEFAULT},
|
||||
{sfPrice, soeREQUIRED},
|
||||
{sfAuthAccounts, soeOPTIONAL},
|
||||
{sfAttestationRewardAccount, soeREQUIRED},
|
||||
{sfWasLockingChainSend, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfDestination, soeOPTIONAL},
|
||||
});
|
||||
|
||||
add(sfXChainCreateAccountAttestationCollectionElement.jsonName.c_str(),
|
||||
sfXChainCreateAccountAttestationCollectionElement.getCode(),
|
||||
{
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfSignature, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfAttestationRewardAccount, soeREQUIRED},
|
||||
{sfWasLockingChainSend, soeREQUIRED},
|
||||
{sfXChainAccountCreateCount, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
});
|
||||
|
||||
add(sfXChainClaimProofSig.jsonName.c_str(),
|
||||
sfXChainClaimProofSig.getCode(),
|
||||
{
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAttestationRewardAccount, soeREQUIRED},
|
||||
{sfWasLockingChainSend, soeREQUIRED},
|
||||
{sfDestination, soeOPTIONAL},
|
||||
});
|
||||
|
||||
add(sfXChainCreateAccountProofSig.jsonName.c_str(),
|
||||
sfXChainCreateAccountProofSig.getCode(),
|
||||
{
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfAttestationRewardAccount, soeREQUIRED},
|
||||
{sfWasLockingChainSend, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
});
|
||||
|
||||
add(sfAuthAccount.jsonName.c_str(),
|
||||
|
||||
@@ -79,8 +79,8 @@ issueFromJson(Json::Value const& v)
|
||||
{
|
||||
if (!v.isObject())
|
||||
{
|
||||
Throw<Json::error>(
|
||||
"issueFromJson can only be specified with a 'object' Json value");
|
||||
Throw<std::runtime_error>(
|
||||
"issueFromJson can only be specified with an 'object' Json value");
|
||||
}
|
||||
|
||||
Json::Value const curStr = v[jss::currency];
|
||||
|
||||
@@ -283,6 +283,49 @@ LedgerFormats::LedgerFormats()
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::Bridge,
|
||||
ltBRIDGE,
|
||||
{
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfMinAccountCreateAmount, soeOPTIONAL},
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfXChainAccountCreateCount, soeREQUIRED},
|
||||
{sfXChainAccountClaimCount, soeREQUIRED},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainOwnedClaimID,
|
||||
ltXCHAIN_OWNED_CLAIM_ID,
|
||||
{
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfOtherChainSource, soeREQUIRED},
|
||||
{sfXChainClaimAttestations, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainOwnedCreateAccountClaimID,
|
||||
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID,
|
||||
{
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainAccountCreateCount, soeREQUIRED},
|
||||
{sfXChainCreateAccountAttestations, soeREQUIRED},
|
||||
{sfOwnerNode, soeREQUIRED},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED}
|
||||
},
|
||||
commonFields);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ CONSTRUCT_TYPED_SFIELD(sfTransactionResult, "TransactionResult", UINT8,
|
||||
CONSTRUCT_TYPED_SFIELD(sfTickSize, "TickSize", UINT8, 16);
|
||||
CONSTRUCT_TYPED_SFIELD(sfUNLModifyDisabling, "UNLModifyDisabling", UINT8, 17);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookResult, "HookResult", UINT8, 18);
|
||||
CONSTRUCT_TYPED_SFIELD(sfWasLockingChainSend, "WasLockingChainSend", UINT8, 19);
|
||||
|
||||
// 16-bit integers
|
||||
CONSTRUCT_TYPED_SFIELD(sfLedgerEntryType, "LedgerEntryType", UINT16, 1, SField::sMD_Never);
|
||||
@@ -180,10 +181,13 @@ CONSTRUCT_TYPED_SFIELD(sfNFTokenOfferNode, "NFTokenOfferNode", UINT64,
|
||||
CONSTRUCT_TYPED_SFIELD(sfEmitBurden, "EmitBurden", UINT64, 13);
|
||||
|
||||
// 64-bit integers (uncommon)
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookOn, "HookOn", UINT64, 16);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookInstructionCount, "HookInstructionCount", UINT64, 17);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookReturnCode, "HookReturnCode", UINT64, 18);
|
||||
CONSTRUCT_TYPED_SFIELD(sfReferenceCount, "ReferenceCount", UINT64, 19);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookOn, "HookOn", UINT64, 16);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookInstructionCount, "HookInstructionCount", UINT64, 17);
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookReturnCode, "HookReturnCode", UINT64, 18);
|
||||
CONSTRUCT_TYPED_SFIELD(sfReferenceCount, "ReferenceCount", UINT64, 19);
|
||||
CONSTRUCT_TYPED_SFIELD(sfXChainClaimID, "XChainClaimID", UINT64, 20);
|
||||
CONSTRUCT_TYPED_SFIELD(sfXChainAccountCreateCount, "XChainAccountCreateCount", UINT64, 21);
|
||||
CONSTRUCT_TYPED_SFIELD(sfXChainAccountClaimCount, "XChainAccountClaimCount", UINT64, 22);
|
||||
|
||||
// 128-bit
|
||||
CONSTRUCT_TYPED_SFIELD(sfEmailHash, "EmailHash", UINT128, 1);
|
||||
@@ -263,7 +267,8 @@ CONSTRUCT_TYPED_SFIELD(sfLPTokenOut, "LPTokenOut", AMOUNT,
|
||||
CONSTRUCT_TYPED_SFIELD(sfLPTokenIn, "LPTokenIn", AMOUNT, 26);
|
||||
CONSTRUCT_TYPED_SFIELD(sfEPrice, "EPrice", AMOUNT, 27);
|
||||
CONSTRUCT_TYPED_SFIELD(sfPrice, "Price", AMOUNT, 28);
|
||||
// 29 and 30 are reserved for side-chains
|
||||
CONSTRUCT_TYPED_SFIELD(sfSignatureReward, "SignatureReward", AMOUNT, 29);
|
||||
CONSTRUCT_TYPED_SFIELD(sfMinAccountCreateAmount, "MinAccountCreateAmount", AMOUNT, 30);
|
||||
CONSTRUCT_TYPED_SFIELD(sfLPTokenBalance, "LPTokenBalance", AMOUNT, 31);
|
||||
|
||||
// variable length (common)
|
||||
@@ -308,6 +313,12 @@ CONSTRUCT_TYPED_SFIELD(sfEmitCallback, "EmitCallback", ACCOUNT,
|
||||
|
||||
// account (uncommon)
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookAccount, "HookAccount", ACCOUNT, 16);
|
||||
CONSTRUCT_TYPED_SFIELD(sfOtherChainSource, "OtherChainSource", ACCOUNT, 18);
|
||||
CONSTRUCT_TYPED_SFIELD(sfOtherChainDestination, "OtherChainDestination",ACCOUNT, 19);
|
||||
CONSTRUCT_TYPED_SFIELD(sfAttestationSignerAccount, "AttestationSignerAccount", ACCOUNT, 20);
|
||||
CONSTRUCT_TYPED_SFIELD(sfAttestationRewardAccount, "AttestationRewardAccount", ACCOUNT, 21);
|
||||
CONSTRUCT_TYPED_SFIELD(sfLockingChainDoor, "LockingChainDoor", ACCOUNT, 22);
|
||||
CONSTRUCT_TYPED_SFIELD(sfIssuingChainDoor, "IssuingChainDoor", ACCOUNT, 23);
|
||||
|
||||
// vector of 256-bit
|
||||
CONSTRUCT_TYPED_SFIELD(sfIndexes, "Indexes", VECTOR256, 1, SField::sMD_Never);
|
||||
@@ -319,9 +330,14 @@ CONSTRUCT_TYPED_SFIELD(sfNFTokenOffers, "NFTokenOffers", VECTOR25
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfPaths, "Paths", PATHSET, 1);
|
||||
|
||||
// issue
|
||||
CONSTRUCT_TYPED_SFIELD(sfLockingChainIssue, "LockingChainIssue", ISSUE, 1);
|
||||
CONSTRUCT_TYPED_SFIELD(sfIssuingChainIssue, "IssuingChainIssue", ISSUE, 2);
|
||||
CONSTRUCT_TYPED_SFIELD(sfAsset, "Asset", ISSUE, 3);
|
||||
CONSTRUCT_TYPED_SFIELD(sfAsset2, "Asset2", ISSUE, 4);
|
||||
|
||||
// Bridge
|
||||
CONSTRUCT_TYPED_SFIELD(sfXChainBridge, "XChainBridge", XCHAIN_BRIDGE,
|
||||
1);
|
||||
// inner object
|
||||
// OBJECT/1 is reserved for end of object
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfTransactionMetaData, "TransactionMetaData", OBJECT, 2);
|
||||
@@ -351,6 +367,16 @@ CONSTRUCT_UNTYPED_SFIELD(sfHookGrant, "HookGrant", OBJECT,
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfVoteEntry, "VoteEntry", OBJECT, 25);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfAuctionSlot, "AuctionSlot", OBJECT, 26);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfAuthAccount, "AuthAccount", OBJECT, 27);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfXChainClaimProofSig, "XChainClaimProofSig", OBJECT, 28);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfXChainCreateAccountProofSig,
|
||||
"XChainCreateAccountProofSig",
|
||||
OBJECT, 29);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfXChainClaimAttestationCollectionElement,
|
||||
"XChainClaimAttestationCollectionElement",
|
||||
OBJECT, 30);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfXChainCreateAccountAttestationCollectionElement,
|
||||
"XChainCreateAccountAttestationCollectionElement",
|
||||
OBJECT, 31);
|
||||
|
||||
// array of objects
|
||||
// ARRAY/1 is reserved for end of array
|
||||
@@ -372,7 +398,13 @@ CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY,
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20);
|
||||
// 21-24 is reserved for side-chains
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfXChainClaimAttestations,
|
||||
"XChainClaimAttestations",
|
||||
ARRAY, 21);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfXChainCreateAccountAttestations,
|
||||
"XChainCreateAccountAttestations",
|
||||
ARRAY, 22);
|
||||
// 23 and 24 are unused and available for use
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfAuthAccounts, "AuthAccounts", ARRAY, 25);
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -239,6 +239,17 @@ STAmount::STAmount(
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(SField const& name, STAmount const& from)
|
||||
: STBase(name)
|
||||
, mIssue(from.mIssue)
|
||||
, mValue(from.mValue)
|
||||
, mOffset(from.mOffset)
|
||||
, mIsNegative(from.mIsNegative)
|
||||
{
|
||||
assert(mValue <= std::numeric_limits<std::int64_t>::max());
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
STAmount::STAmount(std::uint64_t mantissa, bool negative)
|
||||
|
||||
@@ -46,7 +46,7 @@ STArray::STArray(SField const& f) : STBase(f)
|
||||
{
|
||||
}
|
||||
|
||||
STArray::STArray(SField const& f, int n) : STBase(f)
|
||||
STArray::STArray(SField const& f, std::size_t n) : STBase(f)
|
||||
{
|
||||
v_.reserve(n);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,18 @@
|
||||
|
||||
#include <ripple/protocol/STIssue.h>
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/protocol/SystemParameters.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/LedgerFormats.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/STArray.h>
|
||||
@@ -33,10 +34,13 @@
|
||||
#include <ripple/protocol/STParsedJSON.h>
|
||||
#include <ripple/protocol/STPathSet.h>
|
||||
#include <ripple/protocol/STVector256.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <ripple/protocol/TxFormats.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
#include <ripple/protocol/impl/STVar.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
#include <memory>
|
||||
@@ -742,6 +746,20 @@ parseLeaf(
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case STI_XCHAIN_BRIDGE:
|
||||
try
|
||||
{
|
||||
ret = detail::make_stvar<STXChainBridge>(
|
||||
STXChainBridge(field, value));
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
@@ -951,6 +969,7 @@ parseArray(
|
||||
|
||||
if (ret->getFName().fieldType != STI_OBJECT)
|
||||
{
|
||||
ss << "Field type: " << ret->getFName().fieldType << " ";
|
||||
error = non_object_in_array(ss.str(), i);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/impl/STVar.h>
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
@@ -29,7 +31,8 @@
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/STPathSet.h>
|
||||
#include <ripple/protocol/STVector256.h>
|
||||
#include <ripple/protocol/impl/STVar.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
@@ -161,6 +164,9 @@ STVar::STVar(SerialIter& sit, SField const& name, int depth)
|
||||
case STI_ISSUE:
|
||||
construct<STIssue>(sit, name);
|
||||
return;
|
||||
case STI_XCHAIN_BRIDGE:
|
||||
construct<STXChainBridge>(sit, name);
|
||||
return;
|
||||
default:
|
||||
Throw<std::runtime_error>("Unknown object type");
|
||||
}
|
||||
@@ -219,6 +225,9 @@ STVar::STVar(SerializedTypeID id, SField const& name)
|
||||
case STI_ISSUE:
|
||||
construct<STIssue>(name);
|
||||
return;
|
||||
case STI_XCHAIN_BRIDGE:
|
||||
construct<STXChainBridge>(name);
|
||||
return;
|
||||
default:
|
||||
Throw<std::runtime_error>("Unknown object type");
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ private:
|
||||
void
|
||||
construct(Args&&... args)
|
||||
{
|
||||
if (sizeof(T) > max_size)
|
||||
if constexpr (sizeof(T) > max_size)
|
||||
p_ = new T(std::forward<Args>(args)...);
|
||||
else
|
||||
p_ = new (&d_) T(std::forward<Args>(args)...);
|
||||
|
||||
227
src/ripple/protocol/impl/STXChainBridge.cpp
Normal file
227
src/ripple/protocol/impl/STXChainBridge.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/protocol/tokens.h>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
STXChainBridge::STXChainBridge() : STBase{sfXChainBridge}
|
||||
{
|
||||
}
|
||||
|
||||
STXChainBridge::STXChainBridge(SField const& name) : STBase{name}
|
||||
{
|
||||
}
|
||||
|
||||
STXChainBridge::STXChainBridge(
|
||||
AccountID const& srcChainDoor,
|
||||
Issue const& srcChainIssue,
|
||||
AccountID const& dstChainDoor,
|
||||
Issue const& dstChainIssue)
|
||||
: STBase{sfXChainBridge}
|
||||
, lockingChainDoor_{sfLockingChainDoor, srcChainDoor}
|
||||
, lockingChainIssue_{sfLockingChainIssue, srcChainIssue}
|
||||
, issuingChainDoor_{sfIssuingChainDoor, dstChainDoor}
|
||||
, issuingChainIssue_{sfIssuingChainIssue, dstChainIssue}
|
||||
{
|
||||
}
|
||||
|
||||
STXChainBridge::STXChainBridge(STObject const& o)
|
||||
: STBase{sfXChainBridge}
|
||||
, lockingChainDoor_{sfLockingChainDoor, o[sfLockingChainDoor]}
|
||||
, lockingChainIssue_{sfLockingChainIssue, o[sfLockingChainIssue]}
|
||||
, issuingChainDoor_{sfIssuingChainDoor, o[sfIssuingChainDoor]}
|
||||
, issuingChainIssue_{sfIssuingChainIssue, o[sfIssuingChainIssue]}
|
||||
{
|
||||
}
|
||||
|
||||
STXChainBridge::STXChainBridge(Json::Value const& v)
|
||||
: STXChainBridge{sfXChainBridge, v}
|
||||
{
|
||||
}
|
||||
|
||||
STXChainBridge::STXChainBridge(SField const& name, Json::Value const& v)
|
||||
: STBase{name}
|
||||
{
|
||||
if (!v.isObject())
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"STXChainBridge can only be specified with a 'object' Json value");
|
||||
}
|
||||
|
||||
auto checkExtra = [](Json::Value const& v) {
|
||||
static auto const jbridge =
|
||||
ripple::STXChainBridge().getJson(ripple::JsonOptions::none);
|
||||
for (auto it = v.begin(); it != v.end(); ++it)
|
||||
{
|
||||
std::string const name = it.memberName();
|
||||
if (!jbridge.isMember(name))
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"STXChainBridge extra field detected: " + name);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
checkExtra(v);
|
||||
|
||||
Json::Value const& lockingChainDoorStr =
|
||||
v[sfLockingChainDoor.getJsonName()];
|
||||
Json::Value const& lockingChainIssue = v[sfLockingChainIssue.getJsonName()];
|
||||
Json::Value const& issuingChainDoorStr =
|
||||
v[sfIssuingChainDoor.getJsonName()];
|
||||
Json::Value const& issuingChainIssue = v[sfIssuingChainIssue.getJsonName()];
|
||||
|
||||
if (!lockingChainDoorStr.isString())
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"STXChainBridge LockingChainDoor must be a string Json value");
|
||||
}
|
||||
if (!issuingChainDoorStr.isString())
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"STXChainBridge IssuingChainDoor must be a string Json value");
|
||||
}
|
||||
|
||||
auto const lockingChainDoor =
|
||||
parseBase58<AccountID>(lockingChainDoorStr.asString());
|
||||
auto const issuingChainDoor =
|
||||
parseBase58<AccountID>(issuingChainDoorStr.asString());
|
||||
if (!lockingChainDoor)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"STXChainBridge LockingChainDoor must be a valid account");
|
||||
}
|
||||
if (!issuingChainDoor)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"STXChainBridge IssuingChainDoor must be a valid account");
|
||||
}
|
||||
|
||||
lockingChainDoor_ = STAccount{sfLockingChainDoor, *lockingChainDoor};
|
||||
lockingChainIssue_ =
|
||||
STIssue{sfLockingChainIssue, issueFromJson(lockingChainIssue)};
|
||||
issuingChainDoor_ = STAccount{sfIssuingChainDoor, *issuingChainDoor};
|
||||
issuingChainIssue_ =
|
||||
STIssue{sfIssuingChainIssue, issueFromJson(issuingChainIssue)};
|
||||
}
|
||||
|
||||
STXChainBridge::STXChainBridge(SerialIter& sit, SField const& name)
|
||||
: STBase{name}
|
||||
, lockingChainDoor_{sit, sfLockingChainDoor}
|
||||
, lockingChainIssue_{sit, sfLockingChainIssue}
|
||||
, issuingChainDoor_{sit, sfIssuingChainDoor}
|
||||
, issuingChainIssue_{sit, sfIssuingChainIssue}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
STXChainBridge::add(Serializer& s) const
|
||||
{
|
||||
lockingChainDoor_.add(s);
|
||||
lockingChainIssue_.add(s);
|
||||
issuingChainDoor_.add(s);
|
||||
issuingChainIssue_.add(s);
|
||||
}
|
||||
|
||||
Json::Value
|
||||
STXChainBridge::getJson(JsonOptions jo) const
|
||||
{
|
||||
Json::Value v;
|
||||
v[sfLockingChainDoor.getJsonName()] = lockingChainDoor_.getJson(jo);
|
||||
v[sfLockingChainIssue.getJsonName()] = lockingChainIssue_.getJson(jo);
|
||||
v[sfIssuingChainDoor.getJsonName()] = issuingChainDoor_.getJson(jo);
|
||||
v[sfIssuingChainIssue.getJsonName()] = issuingChainIssue_.getJson(jo);
|
||||
return v;
|
||||
}
|
||||
|
||||
std::string
|
||||
STXChainBridge::getText() const
|
||||
{
|
||||
return str(
|
||||
boost::format("{ %s = %s, %s = %s, %s = %s, %s = %s }") %
|
||||
sfLockingChainDoor.getName() % lockingChainDoor_.getText() %
|
||||
sfLockingChainIssue.getName() % lockingChainIssue_.getText() %
|
||||
sfIssuingChainDoor.getName() % issuingChainDoor_.getText() %
|
||||
sfIssuingChainIssue.getName() % issuingChainIssue_.getText());
|
||||
}
|
||||
|
||||
STObject
|
||||
STXChainBridge::toSTObject() const
|
||||
{
|
||||
STObject o{sfXChainBridge};
|
||||
o[sfLockingChainDoor] = lockingChainDoor_;
|
||||
o[sfLockingChainIssue] = lockingChainIssue_;
|
||||
o[sfIssuingChainDoor] = issuingChainDoor_;
|
||||
o[sfIssuingChainIssue] = issuingChainIssue_;
|
||||
return o;
|
||||
}
|
||||
|
||||
SerializedTypeID
|
||||
STXChainBridge::getSType() const
|
||||
{
|
||||
return STI_XCHAIN_BRIDGE;
|
||||
}
|
||||
|
||||
bool
|
||||
STXChainBridge::isEquivalent(const STBase& t) const
|
||||
{
|
||||
const STXChainBridge* v = dynamic_cast<const STXChainBridge*>(&t);
|
||||
return v && (*v == *this);
|
||||
}
|
||||
|
||||
bool
|
||||
STXChainBridge::isDefault() const
|
||||
{
|
||||
return lockingChainDoor_.isDefault() && lockingChainIssue_.isDefault() &&
|
||||
issuingChainDoor_.isDefault() && issuingChainIssue_.isDefault();
|
||||
}
|
||||
|
||||
std::unique_ptr<STXChainBridge>
|
||||
STXChainBridge::construct(SerialIter& sit, SField const& name)
|
||||
{
|
||||
return std::make_unique<STXChainBridge>(sit, name);
|
||||
}
|
||||
|
||||
STBase*
|
||||
STXChainBridge::copy(std::size_t n, void* buf) const
|
||||
{
|
||||
return emplace(n, buf, *this);
|
||||
}
|
||||
|
||||
STBase*
|
||||
STXChainBridge::move(std::size_t n, void* buf)
|
||||
{
|
||||
return emplace(n, buf, std::move(*this));
|
||||
}
|
||||
} // namespace ripple
|
||||
@@ -96,6 +96,23 @@ transResults()
|
||||
MAKE_ERROR(tecOBJECT_NOT_FOUND, "A requested object could not be located."),
|
||||
MAKE_ERROR(tecINSUFFICIENT_PAYMENT, "The payment is not sufficient."),
|
||||
MAKE_ERROR(tecINCOMPLETE, "Some work was completed, but more submissions required to finish."),
|
||||
MAKE_ERROR(tecXCHAIN_BAD_TRANSFER_ISSUE, "Bad xchain transfer issue."),
|
||||
MAKE_ERROR(tecXCHAIN_NO_CLAIM_ID, "No such xchain claim id."),
|
||||
MAKE_ERROR(tecXCHAIN_BAD_CLAIM_ID, "Bad xchain claim id."),
|
||||
MAKE_ERROR(tecXCHAIN_CLAIM_NO_QUORUM, "Quorum was not reached on the xchain claim."),
|
||||
MAKE_ERROR(tecXCHAIN_PROOF_UNKNOWN_KEY, "Unknown key for the xchain proof."),
|
||||
MAKE_ERROR(tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE, "Only XRP may be used for xchain create account."),
|
||||
MAKE_ERROR(tecXCHAIN_WRONG_CHAIN, "XChain Transaction was submitted to the wrong chain."),
|
||||
MAKE_ERROR(tecXCHAIN_REWARD_MISMATCH, "The reward amount must match the reward specified in the xchain bridge."),
|
||||
MAKE_ERROR(tecXCHAIN_NO_SIGNERS_LIST, "The account did not have a signers list."),
|
||||
MAKE_ERROR(tecXCHAIN_SENDING_ACCOUNT_MISMATCH,"The sending account did not match the expected sending account."),
|
||||
MAKE_ERROR(tecXCHAIN_INSUFF_CREATE_AMOUNT, "Insufficient amount to create an account."),
|
||||
MAKE_ERROR(tecXCHAIN_ACCOUNT_CREATE_PAST, "The account create count has already passed."),
|
||||
MAKE_ERROR(tecXCHAIN_ACCOUNT_CREATE_TOO_MANY, "There are too many pending account create transactions to submit a new one."),
|
||||
MAKE_ERROR(tecXCHAIN_PAYMENT_FAILED, "Failed to transfer funds in a xchain transaction."),
|
||||
MAKE_ERROR(tecXCHAIN_SELF_COMMIT, "Account cannot commit funds to itself."),
|
||||
MAKE_ERROR(tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR, "Bad public key account pair in an xchain transaction."),
|
||||
MAKE_ERROR(tecXCHAIN_CREATE_ACCOUNT_DISABLED, "This bridge does not support account creation."),
|
||||
|
||||
MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
|
||||
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
|
||||
@@ -175,6 +192,12 @@ transResults()
|
||||
MAKE_ERROR(temINVALID_COUNT, "Malformed: Count field outside valid range."),
|
||||
MAKE_ERROR(temSEQ_AND_TICKET, "Transaction contains a TicketSequence and a non-zero Sequence."),
|
||||
MAKE_ERROR(temBAD_NFTOKEN_TRANSFER_FEE, "Malformed: The NFToken transfer fee must be between 1 and 5000, inclusive."),
|
||||
MAKE_ERROR(temXCHAIN_EQUAL_DOOR_ACCOUNTS, "Malformed: Bridge must have unique door accounts."),
|
||||
MAKE_ERROR(temXCHAIN_BAD_PROOF, "Malformed: Bad cross-chain claim proof."),
|
||||
MAKE_ERROR(temXCHAIN_BRIDGE_BAD_ISSUES, "Malformed: Bad bridge issues."),
|
||||
MAKE_ERROR(temXCHAIN_BRIDGE_NONDOOR_OWNER, "Malformed: Bridge owner must be one of the door accounts."),
|
||||
MAKE_ERROR(temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT, "Malformed: Bad min account create amount."),
|
||||
MAKE_ERROR(temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT, "Malformed: Bad reward amount."),
|
||||
|
||||
MAKE_ERROR(terRETRY, "Retry transaction."),
|
||||
MAKE_ERROR(terFUNDS_SPENT, "DEPRECATED."),
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/TxFormats.h>
|
||||
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/SOTemplate.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -390,6 +393,101 @@ TxFormats::TxFormats()
|
||||
{sfAmount, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainCreateBridge,
|
||||
ttXCHAIN_CREATE_BRIDGE,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfMinAccountCreateAmount, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainModifyBridge,
|
||||
ttXCHAIN_MODIFY_BRIDGE,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfSignatureReward, soeOPTIONAL},
|
||||
{sfMinAccountCreateAmount, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainCreateClaimID,
|
||||
ttXCHAIN_CREATE_CLAIM_ID,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
{sfOtherChainSource, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainCommit,
|
||||
ttXCHAIN_COMMIT,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfOtherChainDestination, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainClaim,
|
||||
ttXCHAIN_CLAIM,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfDestinationTag, soeOPTIONAL},
|
||||
{sfAmount, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainAddClaimAttestation,
|
||||
ttXCHAIN_ADD_CLAIM_ATTESTATION,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfSignature, soeREQUIRED},
|
||||
{sfOtherChainSource, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAttestationRewardAccount, soeREQUIRED},
|
||||
{sfWasLockingChainSend, soeREQUIRED},
|
||||
|
||||
{sfXChainClaimID, soeREQUIRED},
|
||||
{sfDestination, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainAddAccountCreateAttestation,
|
||||
ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
|
||||
{sfAttestationSignerAccount, soeREQUIRED},
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfSignature, soeREQUIRED},
|
||||
{sfOtherChainSource, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfAttestationRewardAccount, soeREQUIRED},
|
||||
{sfWasLockingChainSend, soeREQUIRED},
|
||||
|
||||
{sfXChainAccountCreateCount, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::XChainAccountCreateCommit,
|
||||
ttXCHAIN_ACCOUNT_CREATE_COMMIT,
|
||||
{
|
||||
{sfXChainBridge, soeREQUIRED},
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeREQUIRED},
|
||||
{sfSignatureReward, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
}
|
||||
|
||||
TxFormats const&
|
||||
|
||||
759
src/ripple/protocol/impl/XChainAttestations.cpp
Normal file
759
src/ripple/protocol/impl/XChainAttestations.cpp
Normal file
@@ -0,0 +1,759 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
|
||||
#include <ripple/basics/Expected.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STAccount.h>
|
||||
#include <ripple/protocol/STAmount.h>
|
||||
#include <ripple/protocol/STArray.h>
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
#include <ripple/protocol/json_get_or_throw.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
namespace ripple {
|
||||
namespace Attestations {
|
||||
|
||||
AttestationBase::AttestationBase(
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
Buffer signature_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_)
|
||||
: attestationSignerAccount{attestationSignerAccount_}
|
||||
, publicKey{publicKey_}
|
||||
, signature{std::move(signature_)}
|
||||
, sendingAccount{sendingAccount_}
|
||||
, sendingAmount{sendingAmount_}
|
||||
, rewardAccount{rewardAccount_}
|
||||
, wasLockingChainSend{wasLockingChainSend_}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationBase::equalHelper(
|
||||
AttestationBase const& lhs,
|
||||
AttestationBase const& rhs)
|
||||
{
|
||||
return std::tie(
|
||||
lhs.attestationSignerAccount,
|
||||
lhs.publicKey,
|
||||
lhs.signature,
|
||||
lhs.sendingAccount,
|
||||
lhs.sendingAmount,
|
||||
lhs.rewardAccount,
|
||||
lhs.wasLockingChainSend) ==
|
||||
std::tie(
|
||||
rhs.attestationSignerAccount,
|
||||
rhs.publicKey,
|
||||
rhs.signature,
|
||||
rhs.sendingAccount,
|
||||
rhs.sendingAmount,
|
||||
rhs.rewardAccount,
|
||||
rhs.wasLockingChainSend);
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationBase::sameEventHelper(
|
||||
AttestationBase const& lhs,
|
||||
AttestationBase const& rhs)
|
||||
{
|
||||
return std::tie(
|
||||
lhs.sendingAccount,
|
||||
lhs.sendingAmount,
|
||||
lhs.wasLockingChainSend) ==
|
||||
std::tie(
|
||||
rhs.sendingAccount, rhs.sendingAmount, rhs.wasLockingChainSend);
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationBase::verify(STXChainBridge const& bridge) const
|
||||
{
|
||||
std::vector<std::uint8_t> msg = message(bridge);
|
||||
return ripple::verify(publicKey, makeSlice(msg), signature);
|
||||
}
|
||||
|
||||
AttestationBase::AttestationBase(STObject const& o)
|
||||
: attestationSignerAccount{o[sfAttestationSignerAccount]}
|
||||
, publicKey{o[sfPublicKey]}
|
||||
, signature{o[sfSignature]}
|
||||
, sendingAccount{o[sfAccount]}
|
||||
, sendingAmount{o[sfAmount]}
|
||||
, rewardAccount{o[sfAttestationRewardAccount]}
|
||||
, wasLockingChainSend{bool(o[sfWasLockingChainSend])}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationBase::AttestationBase(Json::Value const& v)
|
||||
: attestationSignerAccount{Json::getOrThrow<AccountID>(
|
||||
v,
|
||||
sfAttestationSignerAccount)}
|
||||
, publicKey{Json::getOrThrow<PublicKey>(v, sfPublicKey)}
|
||||
, signature{Json::getOrThrow<Buffer>(v, sfSignature)}
|
||||
, sendingAccount{Json::getOrThrow<AccountID>(v, sfAccount)}
|
||||
, sendingAmount{Json::getOrThrow<STAmount>(v, sfAmount)}
|
||||
, rewardAccount{Json::getOrThrow<AccountID>(v, sfAttestationRewardAccount)}
|
||||
, wasLockingChainSend{Json::getOrThrow<bool>(v, sfWasLockingChainSend)}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AttestationBase::addHelper(STObject& o) const
|
||||
{
|
||||
o[sfAttestationSignerAccount] = attestationSignerAccount;
|
||||
o[sfPublicKey] = publicKey;
|
||||
o[sfSignature] = signature;
|
||||
o[sfAmount] = sendingAmount;
|
||||
o[sfAccount] = sendingAccount;
|
||||
o[sfAttestationRewardAccount] = rewardAccount;
|
||||
o[sfWasLockingChainSend] = wasLockingChainSend;
|
||||
}
|
||||
|
||||
AttestationClaim::AttestationClaim(
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
Buffer signature_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t claimID_,
|
||||
std::optional<AccountID> const& dst_)
|
||||
: AttestationBase(
|
||||
attestationSignerAccount_,
|
||||
publicKey_,
|
||||
std::move(signature_),
|
||||
sendingAccount_,
|
||||
sendingAmount_,
|
||||
rewardAccount_,
|
||||
wasLockingChainSend_)
|
||||
, claimID{claimID_}
|
||||
, dst{dst_}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationClaim::AttestationClaim(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
SecretKey const& secretKey_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t claimID_,
|
||||
std::optional<AccountID> const& dst_)
|
||||
: AttestationClaim{
|
||||
attestationSignerAccount_,
|
||||
publicKey_,
|
||||
Buffer{},
|
||||
sendingAccount_,
|
||||
sendingAmount_,
|
||||
rewardAccount_,
|
||||
wasLockingChainSend_,
|
||||
claimID_,
|
||||
dst_}
|
||||
{
|
||||
auto const toSign = message(bridge);
|
||||
signature = sign(publicKey_, secretKey_, makeSlice(toSign));
|
||||
}
|
||||
|
||||
AttestationClaim::AttestationClaim(STObject const& o)
|
||||
: AttestationBase(o), claimID{o[sfXChainClaimID]}, dst{o[~sfDestination]}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationClaim::AttestationClaim(Json::Value const& v)
|
||||
: AttestationBase{v}
|
||||
, claimID{Json::getOrThrow<std::uint64_t>(v, sfXChainClaimID)}
|
||||
{
|
||||
if (v.isMember(sfDestination.getJsonName()))
|
||||
dst = Json::getOrThrow<AccountID>(v, sfDestination);
|
||||
}
|
||||
|
||||
STObject
|
||||
AttestationClaim::toSTObject() const
|
||||
{
|
||||
STObject o{sfXChainClaimAttestationCollectionElement};
|
||||
addHelper(o);
|
||||
o[sfXChainClaimID] = claimID;
|
||||
if (dst)
|
||||
o[sfDestination] = *dst;
|
||||
return o;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t>
|
||||
AttestationClaim::message(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<AccountID> const& dst)
|
||||
{
|
||||
STObject o{sfGeneric};
|
||||
// Serialize in SField order to make python serializers easier to write
|
||||
o[sfXChainClaimID] = claimID;
|
||||
o[sfAmount] = sendingAmount;
|
||||
if (dst)
|
||||
o[sfDestination] = *dst;
|
||||
o[sfOtherChainSource] = sendingAccount;
|
||||
o[sfAttestationRewardAccount] = rewardAccount;
|
||||
o[sfWasLockingChainSend] = wasLockingChainSend ? 1 : 0;
|
||||
o[sfXChainBridge] = bridge;
|
||||
|
||||
Serializer s;
|
||||
o.add(s);
|
||||
|
||||
return std::move(s.modData());
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t>
|
||||
AttestationClaim::message(STXChainBridge const& bridge) const
|
||||
{
|
||||
return AttestationClaim::message(
|
||||
bridge,
|
||||
sendingAccount,
|
||||
sendingAmount,
|
||||
rewardAccount,
|
||||
wasLockingChainSend,
|
||||
claimID,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationClaim::validAmounts() const
|
||||
{
|
||||
return isLegalNet(sendingAmount);
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationClaim::sameEvent(AttestationClaim const& rhs) const
|
||||
{
|
||||
return AttestationClaim::sameEventHelper(*this, rhs) &&
|
||||
tie(claimID, dst) == tie(rhs.claimID, rhs.dst);
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(AttestationClaim const& lhs, AttestationClaim const& rhs)
|
||||
{
|
||||
return AttestationClaim::equalHelper(lhs, rhs) &&
|
||||
tie(lhs.claimID, lhs.dst) == tie(rhs.claimID, rhs.dst);
|
||||
}
|
||||
|
||||
AttestationCreateAccount::AttestationCreateAccount(STObject const& o)
|
||||
: AttestationBase(o)
|
||||
, createCount{o[sfXChainAccountCreateCount]}
|
||||
, toCreate{o[sfDestination]}
|
||||
, rewardAmount{o[sfSignatureReward]}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationCreateAccount::AttestationCreateAccount(Json::Value const& v)
|
||||
: AttestationBase{v}
|
||||
, createCount{Json::getOrThrow<std::uint64_t>(
|
||||
v,
|
||||
sfXChainAccountCreateCount)}
|
||||
, toCreate{Json::getOrThrow<AccountID>(v, sfDestination)}
|
||||
, rewardAmount{Json::getOrThrow<STAmount>(v, sfSignatureReward)}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationCreateAccount::AttestationCreateAccount(
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
Buffer signature_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
STAmount const& rewardAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t createCount_,
|
||||
AccountID const& toCreate_)
|
||||
: AttestationBase(
|
||||
attestationSignerAccount_,
|
||||
publicKey_,
|
||||
std::move(signature_),
|
||||
sendingAccount_,
|
||||
sendingAmount_,
|
||||
rewardAccount_,
|
||||
wasLockingChainSend_)
|
||||
, createCount{createCount_}
|
||||
, toCreate{toCreate_}
|
||||
, rewardAmount{rewardAmount_}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationCreateAccount::AttestationCreateAccount(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID attestationSignerAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
SecretKey const& secretKey_,
|
||||
AccountID const& sendingAccount_,
|
||||
STAmount const& sendingAmount_,
|
||||
STAmount const& rewardAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::uint64_t createCount_,
|
||||
AccountID const& toCreate_)
|
||||
: AttestationCreateAccount{
|
||||
attestationSignerAccount_,
|
||||
publicKey_,
|
||||
Buffer{},
|
||||
sendingAccount_,
|
||||
sendingAmount_,
|
||||
rewardAmount_,
|
||||
rewardAccount_,
|
||||
wasLockingChainSend_,
|
||||
createCount_,
|
||||
toCreate_}
|
||||
{
|
||||
auto const toSign = message(bridge);
|
||||
signature = sign(publicKey_, secretKey_, makeSlice(toSign));
|
||||
}
|
||||
|
||||
STObject
|
||||
AttestationCreateAccount::toSTObject() const
|
||||
{
|
||||
STObject o{sfXChainCreateAccountAttestationCollectionElement};
|
||||
addHelper(o);
|
||||
|
||||
o[sfXChainAccountCreateCount] = createCount;
|
||||
o[sfDestination] = toCreate;
|
||||
o[sfSignatureReward] = rewardAmount;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t>
|
||||
AttestationCreateAccount::message(
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
STAmount const& rewardAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
AccountID const& dst)
|
||||
{
|
||||
STObject o{sfGeneric};
|
||||
// Serialize in SField order to make python serializers easier to write
|
||||
o[sfXChainAccountCreateCount] = createCount;
|
||||
o[sfAmount] = sendingAmount;
|
||||
o[sfSignatureReward] = rewardAmount;
|
||||
o[sfDestination] = dst;
|
||||
o[sfOtherChainSource] = sendingAccount;
|
||||
o[sfAttestationRewardAccount] = rewardAccount;
|
||||
o[sfWasLockingChainSend] = wasLockingChainSend ? 1 : 0;
|
||||
o[sfXChainBridge] = bridge;
|
||||
|
||||
Serializer s;
|
||||
o.add(s);
|
||||
|
||||
return std::move(s.modData());
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t>
|
||||
AttestationCreateAccount::message(STXChainBridge const& bridge) const
|
||||
{
|
||||
return AttestationCreateAccount::message(
|
||||
bridge,
|
||||
sendingAccount,
|
||||
sendingAmount,
|
||||
rewardAmount,
|
||||
rewardAccount,
|
||||
wasLockingChainSend,
|
||||
createCount,
|
||||
toCreate);
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationCreateAccount::validAmounts() const
|
||||
{
|
||||
return isLegalNet(rewardAmount) && isLegalNet(sendingAmount);
|
||||
}
|
||||
|
||||
bool
|
||||
AttestationCreateAccount::sameEvent(AttestationCreateAccount const& rhs) const
|
||||
{
|
||||
return AttestationCreateAccount::sameEventHelper(*this, rhs) &&
|
||||
std::tie(createCount, toCreate, rewardAmount) ==
|
||||
std::tie(rhs.createCount, rhs.toCreate, rhs.rewardAmount);
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(
|
||||
AttestationCreateAccount const& lhs,
|
||||
AttestationCreateAccount const& rhs)
|
||||
{
|
||||
return AttestationCreateAccount::equalHelper(lhs, rhs) &&
|
||||
std::tie(lhs.createCount, lhs.toCreate, lhs.rewardAmount) ==
|
||||
std::tie(rhs.createCount, rhs.toCreate, rhs.rewardAmount);
|
||||
}
|
||||
|
||||
} // namespace Attestations
|
||||
|
||||
SField const& XChainClaimAttestation::ArrayFieldName{sfXChainClaimAttestations};
|
||||
SField const& XChainCreateAccountAttestation::ArrayFieldName{
|
||||
sfXChainCreateAccountAttestations};
|
||||
|
||||
XChainClaimAttestation::XChainClaimAttestation(
|
||||
AccountID const& keyAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
STAmount const& amount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::optional<AccountID> const& dst_)
|
||||
: keyAccount(keyAccount_)
|
||||
, publicKey(publicKey_)
|
||||
, amount(sfAmount, amount_)
|
||||
, rewardAccount(rewardAccount_)
|
||||
, wasLockingChainSend(wasLockingChainSend_)
|
||||
, dst(dst_)
|
||||
{
|
||||
}
|
||||
|
||||
XChainClaimAttestation::XChainClaimAttestation(
|
||||
STAccount const& keyAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
STAmount const& amount_,
|
||||
STAccount const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
std::optional<STAccount> const& dst_)
|
||||
: XChainClaimAttestation{
|
||||
keyAccount_.value(),
|
||||
publicKey_,
|
||||
amount_,
|
||||
rewardAccount_.value(),
|
||||
wasLockingChainSend_,
|
||||
dst_ ? std::optional<AccountID>{dst_->value()} : std::nullopt}
|
||||
{
|
||||
}
|
||||
|
||||
XChainClaimAttestation::XChainClaimAttestation(STObject const& o)
|
||||
: XChainClaimAttestation{
|
||||
o[sfAttestationSignerAccount],
|
||||
PublicKey{o[sfPublicKey]},
|
||||
o[sfAmount],
|
||||
o[sfAttestationRewardAccount],
|
||||
o[sfWasLockingChainSend] != 0,
|
||||
o[~sfDestination]} {};
|
||||
|
||||
XChainClaimAttestation::XChainClaimAttestation(Json::Value const& v)
|
||||
: XChainClaimAttestation{
|
||||
Json::getOrThrow<AccountID>(v, sfAttestationSignerAccount),
|
||||
Json::getOrThrow<PublicKey>(v, sfPublicKey),
|
||||
Json::getOrThrow<STAmount>(v, sfAmount),
|
||||
Json::getOrThrow<AccountID>(v, sfAttestationRewardAccount),
|
||||
Json::getOrThrow<bool>(v, sfWasLockingChainSend),
|
||||
std::nullopt}
|
||||
{
|
||||
if (v.isMember(sfDestination.getJsonName()))
|
||||
dst = Json::getOrThrow<AccountID>(v, sfDestination);
|
||||
};
|
||||
|
||||
XChainClaimAttestation::XChainClaimAttestation(
|
||||
XChainClaimAttestation::TSignedAttestation const& claimAtt)
|
||||
: XChainClaimAttestation{
|
||||
claimAtt.attestationSignerAccount,
|
||||
claimAtt.publicKey,
|
||||
claimAtt.sendingAmount,
|
||||
claimAtt.rewardAccount,
|
||||
claimAtt.wasLockingChainSend,
|
||||
claimAtt.dst}
|
||||
{
|
||||
}
|
||||
|
||||
STObject
|
||||
XChainClaimAttestation::toSTObject() const
|
||||
{
|
||||
STObject o{sfXChainClaimProofSig};
|
||||
o[sfAttestationSignerAccount] =
|
||||
STAccount{sfAttestationSignerAccount, keyAccount};
|
||||
o[sfPublicKey] = publicKey;
|
||||
o[sfAmount] = STAmount{sfAmount, amount};
|
||||
o[sfAttestationRewardAccount] =
|
||||
STAccount{sfAttestationRewardAccount, rewardAccount};
|
||||
o[sfWasLockingChainSend] = wasLockingChainSend;
|
||||
if (dst)
|
||||
o[sfDestination] = STAccount{sfDestination, *dst};
|
||||
return o;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(XChainClaimAttestation const& lhs, XChainClaimAttestation const& rhs)
|
||||
{
|
||||
return std::tie(
|
||||
lhs.keyAccount,
|
||||
lhs.publicKey,
|
||||
lhs.amount,
|
||||
lhs.rewardAccount,
|
||||
lhs.wasLockingChainSend,
|
||||
lhs.dst) ==
|
||||
std::tie(
|
||||
rhs.keyAccount,
|
||||
rhs.publicKey,
|
||||
rhs.amount,
|
||||
rhs.rewardAccount,
|
||||
rhs.wasLockingChainSend,
|
||||
rhs.dst);
|
||||
}
|
||||
|
||||
XChainClaimAttestation::MatchFields::MatchFields(
|
||||
XChainClaimAttestation::TSignedAttestation const& att)
|
||||
: amount{att.sendingAmount}
|
||||
, wasLockingChainSend{att.wasLockingChainSend}
|
||||
, dst{att.dst}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationMatch
|
||||
XChainClaimAttestation::match(
|
||||
XChainClaimAttestation::MatchFields const& rhs) const
|
||||
{
|
||||
if (std::tie(amount, wasLockingChainSend) !=
|
||||
std::tie(rhs.amount, rhs.wasLockingChainSend))
|
||||
return AttestationMatch::nonDstMismatch;
|
||||
if (dst != rhs.dst)
|
||||
return AttestationMatch::matchExceptDst;
|
||||
return AttestationMatch::match;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
XChainCreateAccountAttestation::XChainCreateAccountAttestation(
|
||||
AccountID const& keyAccount_,
|
||||
PublicKey const& publicKey_,
|
||||
STAmount const& amount_,
|
||||
STAmount const& rewardAmount_,
|
||||
AccountID const& rewardAccount_,
|
||||
bool wasLockingChainSend_,
|
||||
AccountID const& dst_)
|
||||
: keyAccount(keyAccount_)
|
||||
, publicKey(publicKey_)
|
||||
, amount(sfAmount, amount_)
|
||||
, rewardAmount(sfSignatureReward, rewardAmount_)
|
||||
, rewardAccount(rewardAccount_)
|
||||
, wasLockingChainSend(wasLockingChainSend_)
|
||||
, dst(dst_)
|
||||
{
|
||||
}
|
||||
|
||||
XChainCreateAccountAttestation::XChainCreateAccountAttestation(
|
||||
STObject const& o)
|
||||
: XChainCreateAccountAttestation{
|
||||
o[sfAttestationSignerAccount],
|
||||
PublicKey{o[sfPublicKey]},
|
||||
o[sfAmount],
|
||||
o[sfSignatureReward],
|
||||
o[sfAttestationRewardAccount],
|
||||
o[sfWasLockingChainSend] != 0,
|
||||
o[sfDestination]} {};
|
||||
|
||||
XChainCreateAccountAttestation ::XChainCreateAccountAttestation(
|
||||
Json::Value const& v)
|
||||
: XChainCreateAccountAttestation{
|
||||
Json::getOrThrow<AccountID>(v, sfAttestationSignerAccount),
|
||||
Json::getOrThrow<PublicKey>(v, sfPublicKey),
|
||||
Json::getOrThrow<STAmount>(v, sfAmount),
|
||||
Json::getOrThrow<STAmount>(v, sfSignatureReward),
|
||||
Json::getOrThrow<AccountID>(v, sfAttestationRewardAccount),
|
||||
Json::getOrThrow<bool>(v, sfWasLockingChainSend),
|
||||
Json::getOrThrow<AccountID>(v, sfDestination)}
|
||||
{
|
||||
}
|
||||
|
||||
XChainCreateAccountAttestation::XChainCreateAccountAttestation(
|
||||
XChainCreateAccountAttestation::TSignedAttestation const& createAtt)
|
||||
: XChainCreateAccountAttestation{
|
||||
createAtt.attestationSignerAccount,
|
||||
createAtt.publicKey,
|
||||
createAtt.sendingAmount,
|
||||
createAtt.rewardAmount,
|
||||
createAtt.rewardAccount,
|
||||
createAtt.wasLockingChainSend,
|
||||
createAtt.toCreate}
|
||||
{
|
||||
}
|
||||
|
||||
STObject
|
||||
XChainCreateAccountAttestation::toSTObject() const
|
||||
{
|
||||
STObject o{sfXChainCreateAccountProofSig};
|
||||
|
||||
o[sfAttestationSignerAccount] =
|
||||
STAccount{sfAttestationSignerAccount, keyAccount};
|
||||
o[sfPublicKey] = publicKey;
|
||||
o[sfAmount] = STAmount{sfAmount, amount};
|
||||
o[sfSignatureReward] = STAmount{sfSignatureReward, rewardAmount};
|
||||
o[sfAttestationRewardAccount] =
|
||||
STAccount{sfAttestationRewardAccount, rewardAccount};
|
||||
o[sfWasLockingChainSend] = wasLockingChainSend;
|
||||
o[sfDestination] = STAccount{sfDestination, dst};
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
XChainCreateAccountAttestation::MatchFields::MatchFields(
|
||||
XChainCreateAccountAttestation::TSignedAttestation const& att)
|
||||
: amount{att.sendingAmount}
|
||||
, rewardAmount(att.rewardAmount)
|
||||
, wasLockingChainSend{att.wasLockingChainSend}
|
||||
, dst{att.toCreate}
|
||||
{
|
||||
}
|
||||
|
||||
AttestationMatch
|
||||
XChainCreateAccountAttestation::match(
|
||||
XChainCreateAccountAttestation::MatchFields const& rhs) const
|
||||
{
|
||||
if (std::tie(amount, rewardAmount, wasLockingChainSend) !=
|
||||
std::tie(rhs.amount, rhs.rewardAmount, rhs.wasLockingChainSend))
|
||||
return AttestationMatch::nonDstMismatch;
|
||||
if (dst != rhs.dst)
|
||||
return AttestationMatch::matchExceptDst;
|
||||
return AttestationMatch::match;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(
|
||||
XChainCreateAccountAttestation const& lhs,
|
||||
XChainCreateAccountAttestation const& rhs)
|
||||
{
|
||||
return std::tie(
|
||||
lhs.keyAccount,
|
||||
lhs.publicKey,
|
||||
lhs.amount,
|
||||
lhs.rewardAmount,
|
||||
lhs.rewardAccount,
|
||||
lhs.wasLockingChainSend,
|
||||
lhs.dst) ==
|
||||
std::tie(
|
||||
rhs.keyAccount,
|
||||
rhs.publicKey,
|
||||
rhs.amount,
|
||||
rhs.rewardAmount,
|
||||
rhs.rewardAccount,
|
||||
rhs.wasLockingChainSend,
|
||||
rhs.dst);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
template <class TAttestation>
|
||||
XChainAttestationsBase<TAttestation>::XChainAttestationsBase(
|
||||
XChainAttestationsBase<TAttestation>::AttCollection&& atts)
|
||||
: attestations_{std::move(atts)}
|
||||
{
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
typename XChainAttestationsBase<TAttestation>::AttCollection::const_iterator
|
||||
XChainAttestationsBase<TAttestation>::begin() const
|
||||
{
|
||||
return attestations_.begin();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
typename XChainAttestationsBase<TAttestation>::AttCollection::const_iterator
|
||||
XChainAttestationsBase<TAttestation>::end() const
|
||||
{
|
||||
return attestations_.end();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
typename XChainAttestationsBase<TAttestation>::AttCollection::iterator
|
||||
XChainAttestationsBase<TAttestation>::begin()
|
||||
{
|
||||
return attestations_.begin();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
typename XChainAttestationsBase<TAttestation>::AttCollection::iterator
|
||||
XChainAttestationsBase<TAttestation>::end()
|
||||
{
|
||||
return attestations_.end();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
XChainAttestationsBase<TAttestation>::XChainAttestationsBase(
|
||||
Json::Value const& v)
|
||||
{
|
||||
if (!v.isObject())
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"XChainAttestationsBase can only be specified with an 'object' "
|
||||
"Json value");
|
||||
}
|
||||
|
||||
attestations_ = [&] {
|
||||
auto const jAtts = v[jss::attestations];
|
||||
|
||||
if (jAtts.size() > maxAttestations)
|
||||
Throw<std::runtime_error>(
|
||||
"XChainAttestationsBase exceeded max number of attestations");
|
||||
|
||||
std::vector<TAttestation> r;
|
||||
r.reserve(jAtts.size());
|
||||
for (auto const& a : jAtts)
|
||||
r.emplace_back(a);
|
||||
return r;
|
||||
}();
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
XChainAttestationsBase<TAttestation>::XChainAttestationsBase(STArray const& arr)
|
||||
{
|
||||
if (arr.size() > maxAttestations)
|
||||
Throw<std::runtime_error>(
|
||||
"XChainAttestationsBase exceeded max number of attestations");
|
||||
|
||||
attestations_.reserve(arr.size());
|
||||
for (auto const& o : arr)
|
||||
attestations_.emplace_back(o);
|
||||
}
|
||||
|
||||
template <class TAttestation>
|
||||
STArray
|
||||
XChainAttestationsBase<TAttestation>::toSTArray() const
|
||||
{
|
||||
STArray r{TAttestation::ArrayFieldName, attestations_.size()};
|
||||
for (auto const& e : attestations_)
|
||||
r.emplace_back(e.toSTObject());
|
||||
return r;
|
||||
}
|
||||
|
||||
template class XChainAttestationsBase<XChainClaimAttestation>;
|
||||
template class XChainAttestationsBase<XChainCreateAccountAttestation>;
|
||||
|
||||
} // namespace ripple
|
||||
159
src/ripple/protocol/json_get_or_throw.h
Normal file
159
src/ripple/protocol/json_get_or_throw.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#ifndef PROTOCOL_GET_OR_THROW_H_
|
||||
#define PROTOCOL_GET_OR_THROW_H_
|
||||
|
||||
#include <ripple/basics/Buffer.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
|
||||
#include <charconv>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace Json {
|
||||
struct JsonMissingKeyError : std::exception
|
||||
{
|
||||
char const* const key;
|
||||
mutable std::string msg;
|
||||
JsonMissingKeyError(Json::StaticString const& k) : key{k.c_str()}
|
||||
{
|
||||
}
|
||||
const char*
|
||||
what() const noexcept override
|
||||
{
|
||||
if (msg.empty())
|
||||
{
|
||||
msg = std::string("Missing json key: ") + key;
|
||||
}
|
||||
return msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
struct JsonTypeMismatchError : std::exception
|
||||
{
|
||||
char const* const key;
|
||||
std::string const expectedType;
|
||||
mutable std::string msg;
|
||||
JsonTypeMismatchError(Json::StaticString const& k, std::string et)
|
||||
: key{k.c_str()}, expectedType{std::move(et)}
|
||||
{
|
||||
}
|
||||
const char*
|
||||
what() const noexcept override
|
||||
{
|
||||
if (msg.empty())
|
||||
{
|
||||
msg = std::string("Type mismatch on json key: ") + key +
|
||||
"; expected type: " + expectedType;
|
||||
}
|
||||
return msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
static_assert(sizeof(T) == -1, "This function must be specialized");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
Json::StaticString const& key = field.getJsonName();
|
||||
if (!v.isMember(key))
|
||||
Throw<JsonMissingKeyError>(key);
|
||||
|
||||
Json::Value const& inner = v[key];
|
||||
if (!inner.isString())
|
||||
Throw<JsonTypeMismatchError>(key, "string");
|
||||
return inner.asString();
|
||||
}
|
||||
|
||||
// Note, this allows integer numeric fields to act as bools
|
||||
template <>
|
||||
inline bool
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
Json::StaticString const& key = field.getJsonName();
|
||||
if (!v.isMember(key))
|
||||
Throw<JsonMissingKeyError>(key);
|
||||
Json::Value const& inner = v[key];
|
||||
if (inner.isBool())
|
||||
return inner.asBool();
|
||||
if (!inner.isIntegral())
|
||||
Throw<JsonTypeMismatchError>(key, "bool");
|
||||
|
||||
return inner.asInt() != 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::uint64_t
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
Json::StaticString const& key = field.getJsonName();
|
||||
if (!v.isMember(key))
|
||||
Throw<JsonMissingKeyError>(key);
|
||||
Json::Value const& inner = v[key];
|
||||
if (inner.isUInt())
|
||||
return inner.asUInt();
|
||||
if (inner.isInt())
|
||||
{
|
||||
auto const r = inner.asInt();
|
||||
if (r < 0)
|
||||
Throw<JsonTypeMismatchError>(key, "uint64");
|
||||
return r;
|
||||
}
|
||||
if (inner.isString())
|
||||
{
|
||||
auto const s = inner.asString();
|
||||
// parse as hex
|
||||
std::uint64_t val;
|
||||
|
||||
auto [p, ec] = std::from_chars(s.data(), s.data() + s.size(), val, 16);
|
||||
|
||||
if (ec != std::errc() || (p != s.data() + s.size()))
|
||||
Throw<JsonTypeMismatchError>(key, "uint64");
|
||||
return val;
|
||||
}
|
||||
Throw<JsonTypeMismatchError>(key, "uint64");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline ripple::Buffer
|
||||
getOrThrow(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
using namespace ripple;
|
||||
std::string const hex = getOrThrow<std::string>(v, field);
|
||||
if (auto const r = strUnHex(hex))
|
||||
{
|
||||
// TODO: mismatch between a buffer and a blob
|
||||
return Buffer{r->data(), r->size()};
|
||||
}
|
||||
Throw<JsonTypeMismatchError>(field.getJsonName(), "Buffer");
|
||||
}
|
||||
|
||||
// This function may be used by external projects (like the witness server).
|
||||
template <class T>
|
||||
std::optional<T>
|
||||
getOptional(Json::Value const& v, ripple::SField const& field)
|
||||
{
|
||||
try
|
||||
{
|
||||
return getOrThrow<T>(v, field);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Json
|
||||
|
||||
#endif // PROTOCOL_GET_OR_THROW_H_
|
||||
@@ -62,6 +62,7 @@ JSS(Asset); // in: AMM Asset1
|
||||
JSS(Asset2); // in: AMM Asset2
|
||||
JSS(AuthAccount); // in: AMM Auction Slot
|
||||
JSS(AuthAccounts); // in: AMM Auction Slot
|
||||
JSS(Bridge); // ledger type.
|
||||
JSS(Check); // ledger type.
|
||||
JSS(CheckCancel); // transaction type.
|
||||
JSS(CheckCash); // transaction type.
|
||||
@@ -107,31 +108,41 @@ JSS(Paths); // in/out: TransactionSign
|
||||
JSS(PayChannel); // ledger type.
|
||||
JSS(Payment); // transaction type.
|
||||
JSS(PaymentChannelClaim); // transaction type.
|
||||
JSS(PaymentChannelCreate); // transaction type.
|
||||
JSS(PaymentChannelFund); // transaction type.
|
||||
JSS(RippleState); // ledger type.
|
||||
JSS(SLE_hit_rate); // out: GetCounts.
|
||||
JSS(SetFee); // transaction type.
|
||||
JSS(UNLModify); // transaction type.
|
||||
JSS(SettleDelay); // in: TransactionSign
|
||||
JSS(SendMax); // in: TransactionSign
|
||||
JSS(Sequence); // in/out: TransactionSign; field.
|
||||
JSS(SetFlag); // field.
|
||||
JSS(SetRegularKey); // transaction type.
|
||||
JSS(SignerList); // ledger type.
|
||||
JSS(SignerListSet); // transaction type.
|
||||
JSS(SigningPubKey); // field.
|
||||
JSS(TakerGets); // field.
|
||||
JSS(TakerPays); // field.
|
||||
JSS(Ticket); // ledger type.
|
||||
JSS(TicketCreate); // transaction type.
|
||||
JSS(TxnSignature); // field.
|
||||
JSS(TradingFee); // in/out: AMM trading fee
|
||||
JSS(TransactionType); // in: TransactionSign.
|
||||
JSS(TransferRate); // in: TransferRate.
|
||||
JSS(TrustSet); // transaction type.
|
||||
JSS(VoteSlots); // out: AMM Vote
|
||||
JSS(aborted); // out: InboundLedger
|
||||
JSS(PaymentChannelCreate); // transaction type.
|
||||
JSS(PaymentChannelFund); // transaction type.
|
||||
JSS(RippleState); // ledger type.
|
||||
JSS(SLE_hit_rate); // out: GetCounts.
|
||||
JSS(SetFee); // transaction type.
|
||||
JSS(UNLModify); // transaction type.
|
||||
JSS(SettleDelay); // in: TransactionSign
|
||||
JSS(SendMax); // in: TransactionSign
|
||||
JSS(Sequence); // in/out: TransactionSign; field.
|
||||
JSS(SetFlag); // field.
|
||||
JSS(SetRegularKey); // transaction type.
|
||||
JSS(SignerList); // ledger type.
|
||||
JSS(SignerListSet); // transaction type.
|
||||
JSS(SigningPubKey); // field.
|
||||
JSS(TakerGets); // field.
|
||||
JSS(TakerPays); // field.
|
||||
JSS(Ticket); // ledger type.
|
||||
JSS(TicketCreate); // transaction type.
|
||||
JSS(TxnSignature); // field.
|
||||
JSS(TradingFee); // in/out: AMM trading fee
|
||||
JSS(TransactionType); // in: TransactionSign.
|
||||
JSS(TransferRate); // in: TransferRate.
|
||||
JSS(TrustSet); // transaction type.
|
||||
JSS(VoteSlots); // out: AMM Vote
|
||||
JSS(XChainAddAccountCreateAttestation); // transaction type.
|
||||
JSS(XChainAddClaimAttestation); // transaction type.
|
||||
JSS(XChainAccountCreateCommit); // transaction type.
|
||||
JSS(XChainClaim); // transaction type.
|
||||
JSS(XChainCommit); // transaction type.
|
||||
JSS(XChainCreateBridge); // transaction type.
|
||||
JSS(XChainCreateClaimID); // transaction type.
|
||||
JSS(XChainModifyBridge); // transaction type.
|
||||
JSS(XChainOwnedClaimID); // ledger type.
|
||||
JSS(XChainOwnedCreateAccountClaimID); // ledger type.
|
||||
JSS(aborted); // out: InboundLedger
|
||||
JSS(accepted); // out: LedgerToJson, OwnerInfo, SubmitTransaction
|
||||
JSS(account); // in/out: many
|
||||
JSS(accountState); // out: LedgerToJson
|
||||
@@ -147,96 +158,102 @@ JSS(account_sequence_next); // out: SubmitTransaction
|
||||
JSS(account_sequence_available); // out: SubmitTransaction
|
||||
JSS(account_history_tx_stream); // in: Subscribe, Unsubscribe
|
||||
JSS(account_history_tx_index); // out: Account txn history subscribe
|
||||
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(account_history_tx_first); // out: Account txn history subscribe
|
||||
JSS(account_history_boundary); // out: Account txn history subscribe
|
||||
JSS(accounts); // in: LedgerEntry, Subscribe,
|
||||
// handlers/Ledger, Unsubscribe
|
||||
JSS(accounts_proposed); // in: Subscribe, Unsubscribe
|
||||
JSS(action);
|
||||
JSS(acquiring); // out: LedgerRequest
|
||||
JSS(address); // out: PeerImp
|
||||
JSS(affected); // out: AcceptedLedgerTx
|
||||
JSS(age); // out: NetworkOPs, Peers
|
||||
JSS(alternatives); // out: PathRequest, RipplePathFind
|
||||
JSS(amendment_blocked); // out: NetworkOPs
|
||||
JSS(amendments); // in: AccountObjects, out: NetworkOPs
|
||||
JSS(amm); // out: amm_info
|
||||
JSS(amm_account); // in: amm_info
|
||||
JSS(amount); // out: AccountChannels, amm_info
|
||||
JSS(amount2); // out: amm_info
|
||||
JSS(api_version); // in: many, out: Version
|
||||
JSS(api_version_low); // out: Version
|
||||
JSS(applied); // out: SubmitTransaction
|
||||
JSS(asks); // out: Subscribe
|
||||
JSS(asset); // in: amm_info
|
||||
JSS(asset2); // in: amm_info
|
||||
JSS(assets); // out: GatewayBalances
|
||||
JSS(asset_frozen); // out: amm_info
|
||||
JSS(asset2_frozen); // out: amm_info
|
||||
JSS(auction_slot); // out: amm_info
|
||||
JSS(authorized); // out: AccountLines
|
||||
JSS(auth_accounts); // out: amm_info
|
||||
JSS(auth_change); // out: AccountInfo
|
||||
JSS(auth_change_queued); // out: AccountInfo
|
||||
JSS(available); // out: ValidatorList
|
||||
JSS(avg_bps_recv); // out: Peers
|
||||
JSS(avg_bps_sent); // out: Peers
|
||||
JSS(balance); // out: AccountLines
|
||||
JSS(balances); // out: GatewayBalances
|
||||
JSS(base); // out: LogLevel
|
||||
JSS(base_fee); // out: NetworkOPs
|
||||
JSS(base_fee_xrp); // out: NetworkOPs
|
||||
JSS(bids); // out: Subscribe
|
||||
JSS(binary); // in: AccountTX, LedgerEntry,
|
||||
// AccountTxOld, Tx LedgerData
|
||||
JSS(blob); // out: ValidatorList
|
||||
JSS(blobs_v2); // out: ValidatorList
|
||||
// in: UNL
|
||||
JSS(books); // in: Subscribe, Unsubscribe
|
||||
JSS(both); // in: Subscribe, Unsubscribe
|
||||
JSS(both_sides); // in: Subscribe, Unsubscribe
|
||||
JSS(broadcast); // out: SubmitTransaction
|
||||
JSS(build_path); // in: TransactionSign
|
||||
JSS(build_version); // out: NetworkOPs
|
||||
JSS(cancel_after); // out: AccountChannels
|
||||
JSS(can_delete); // out: CanDelete
|
||||
JSS(changes); // out: BookChanges
|
||||
JSS(channel_id); // out: AccountChannels
|
||||
JSS(channels); // out: AccountChannels
|
||||
JSS(check); // in: AccountObjects
|
||||
JSS(check_nodes); // in: LedgerCleaner
|
||||
JSS(clear); // in/out: FetchInfo
|
||||
JSS(close); // out: BookChanges
|
||||
JSS(close_flags); // out: LedgerToJson
|
||||
JSS(close_time); // in: Application, out: NetworkOPs,
|
||||
// RCLCxPeerPos, LedgerToJson
|
||||
JSS(close_time_estimated); // in: Application, out: LedgerToJson
|
||||
JSS(close_time_human); // out: LedgerToJson
|
||||
JSS(close_time_offset); // out: NetworkOPs
|
||||
JSS(close_time_resolution); // in: Application; out: LedgerToJson
|
||||
JSS(closed); // out: NetworkOPs, LedgerToJson,
|
||||
// handlers/Ledger
|
||||
JSS(closed_ledger); // out: NetworkOPs
|
||||
JSS(cluster); // out: PeerImp
|
||||
JSS(code); // out: errors
|
||||
JSS(command); // in: RPCHandler
|
||||
JSS(complete); // out: NetworkOPs, InboundLedger
|
||||
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
|
||||
JSS(complete_shards); // out: OverlayImpl, PeerImp
|
||||
JSS(consensus); // out: NetworkOPs, LedgerConsensus
|
||||
JSS(converge_time); // out: NetworkOPs
|
||||
JSS(converge_time_s); // out: NetworkOPs
|
||||
JSS(cookie); // out: NetworkOPs
|
||||
JSS(count); // in: AccountTx*, ValidatorList
|
||||
JSS(counters); // in/out: retrieve counters
|
||||
JSS(ctid); // in/out: Tx RPC
|
||||
JSS(currency_a); // out: BookChanges
|
||||
JSS(currency_b); // out: BookChanges
|
||||
JSS(currentShard); // out: NodeToShardStatus
|
||||
JSS(currentShardIndex); // out: NodeToShardStatus
|
||||
JSS(currency); // in: paths/PathRequest, STAmount
|
||||
// out: STPathSet, STAmount,
|
||||
// AccountLines
|
||||
JSS(current); // out: OwnerInfo
|
||||
JSS(acquiring); // out: LedgerRequest
|
||||
JSS(address); // out: PeerImp
|
||||
JSS(affected); // out: AcceptedLedgerTx
|
||||
JSS(age); // out: NetworkOPs, Peers
|
||||
JSS(alternatives); // out: PathRequest, RipplePathFind
|
||||
JSS(amendment_blocked); // out: NetworkOPs
|
||||
JSS(amendments); // in: AccountObjects, out: NetworkOPs
|
||||
JSS(amm); // out: amm_info
|
||||
JSS(amm_account); // in: amm_info
|
||||
JSS(amount); // out: AccountChannels, amm_info
|
||||
JSS(amount2); // out: amm_info
|
||||
JSS(api_version); // in: many, out: Version
|
||||
JSS(api_version_low); // out: Version
|
||||
JSS(applied); // out: SubmitTransaction
|
||||
JSS(asks); // out: Subscribe
|
||||
JSS(asset); // in: amm_info
|
||||
JSS(asset2); // in: amm_info
|
||||
JSS(assets); // out: GatewayBalances
|
||||
JSS(asset_frozen); // out: amm_info
|
||||
JSS(asset2_frozen); // out: amm_info
|
||||
JSS(attestations); //
|
||||
JSS(attestation_reward_account); //
|
||||
JSS(auction_slot); // out: amm_info
|
||||
JSS(authorized); // out: AccountLines
|
||||
JSS(auth_accounts); // out: amm_info
|
||||
JSS(auth_change); // out: AccountInfo
|
||||
JSS(auth_change_queued); // out: AccountInfo
|
||||
JSS(available); // out: ValidatorList
|
||||
JSS(avg_bps_recv); // out: Peers
|
||||
JSS(avg_bps_sent); // out: Peers
|
||||
JSS(balance); // out: AccountLines
|
||||
JSS(balances); // out: GatewayBalances
|
||||
JSS(base); // out: LogLevel
|
||||
JSS(base_fee); // out: NetworkOPs
|
||||
JSS(base_fee_xrp); // out: NetworkOPs
|
||||
JSS(bids); // out: Subscribe
|
||||
JSS(binary); // in: AccountTX, LedgerEntry,
|
||||
// AccountTxOld, Tx LedgerData
|
||||
JSS(blob); // out: ValidatorList
|
||||
JSS(blobs_v2); // out: ValidatorList
|
||||
// in: UNL
|
||||
JSS(books); // in: Subscribe, Unsubscribe
|
||||
JSS(both); // in: Subscribe, Unsubscribe
|
||||
JSS(both_sides); // in: Subscribe, Unsubscribe
|
||||
JSS(broadcast); // out: SubmitTransaction
|
||||
JSS(bridge); // in: LedgerEntry
|
||||
JSS(bridge_account); // in: LedgerEntry
|
||||
JSS(build_path); // in: TransactionSign
|
||||
JSS(build_version); // out: NetworkOPs
|
||||
JSS(cancel_after); // out: AccountChannels
|
||||
JSS(can_delete); // out: CanDelete
|
||||
JSS(changes); // out: BookChanges
|
||||
JSS(channel_id); // out: AccountChannels
|
||||
JSS(channels); // out: AccountChannels
|
||||
JSS(check); // in: AccountObjects
|
||||
JSS(check_nodes); // in: LedgerCleaner
|
||||
JSS(clear); // in/out: FetchInfo
|
||||
JSS(close); // out: BookChanges
|
||||
JSS(close_flags); // out: LedgerToJson
|
||||
JSS(close_time); // in: Application, out: NetworkOPs,
|
||||
// RCLCxPeerPos, LedgerToJson
|
||||
JSS(close_time_estimated); // in: Application, out: LedgerToJson
|
||||
JSS(close_time_human); // out: LedgerToJson
|
||||
JSS(close_time_offset); // out: NetworkOPs
|
||||
JSS(close_time_resolution); // in: Application; out: LedgerToJson
|
||||
JSS(closed); // out: NetworkOPs, LedgerToJson,
|
||||
// handlers/Ledger
|
||||
JSS(closed_ledger); // out: NetworkOPs
|
||||
JSS(cluster); // out: PeerImp
|
||||
JSS(code); // out: errors
|
||||
JSS(command); // in: RPCHandler
|
||||
JSS(complete); // out: NetworkOPs, InboundLedger
|
||||
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
|
||||
JSS(complete_shards); // out: OverlayImpl, PeerImp
|
||||
JSS(consensus); // out: NetworkOPs, LedgerConsensus
|
||||
JSS(converge_time); // out: NetworkOPs
|
||||
JSS(converge_time_s); // out: NetworkOPs
|
||||
JSS(cookie); // out: NetworkOPs
|
||||
JSS(count); // in: AccountTx*, ValidatorList
|
||||
JSS(counters); // in/out: retrieve counters
|
||||
JSS(ctid); // in/out: Tx RPC
|
||||
JSS(currency_a); // out: BookChanges
|
||||
JSS(currency_b); // out: BookChanges
|
||||
JSS(currentShard); // out: NodeToShardStatus
|
||||
JSS(currentShardIndex); // out: NodeToShardStatus
|
||||
JSS(currency); // in: paths/PathRequest, STAmount
|
||||
// out: STPathSet, STAmount,
|
||||
// AccountLines
|
||||
JSS(current); // out: OwnerInfo
|
||||
JSS(current_activities);
|
||||
JSS(current_ledger_size); // out: TxQ
|
||||
JSS(current_queue_size); // out: TxQ
|
||||
@@ -698,8 +715,10 @@ JSS(vote_weight); // out: amm_info
|
||||
JSS(warning); // rpc:
|
||||
JSS(warnings); // out: server_info, server_state
|
||||
JSS(workers);
|
||||
JSS(write_load); // out: GetCounts
|
||||
JSS(NegativeUNL); // out: ValidatorList; ledger type
|
||||
JSS(write_load); // out: GetCounts
|
||||
JSS(xchain_owned_claim_id); // in: LedgerEntry, AccountObjects
|
||||
JSS(xchain_owned_create_account_claim_id); // in: LedgerEntry
|
||||
JSS(NegativeUNL); // out: ValidatorList; ledger type
|
||||
#undef JSS
|
||||
|
||||
} // namespace jss
|
||||
|
||||
@@ -197,7 +197,11 @@ doAccountObjects(RPC::JsonContext& context)
|
||||
{jss::escrow, ltESCROW},
|
||||
{jss::nft_page, ltNFTOKEN_PAGE},
|
||||
{jss::payment_channel, ltPAYCHAN},
|
||||
{jss::state, ltRIPPLE_STATE}};
|
||||
{jss::state, ltRIPPLE_STATE},
|
||||
{jss::xchain_owned_claim_id, ltXCHAIN_OWNED_CLAIM_ID},
|
||||
{jss::xchain_owned_create_account_claim_id,
|
||||
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID},
|
||||
{jss::bridge, ltBRIDGE}};
|
||||
|
||||
typeFilter.emplace();
|
||||
typeFilter->reserve(std::size(deletionBlockers));
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ripple/net/RPCErr.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/GRPCHandlers.h>
|
||||
@@ -390,6 +391,203 @@ doLedgerEntry(RPC::JsonContext& context)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (context.params.isMember(jss::bridge))
|
||||
{
|
||||
expectedType = ltBRIDGE;
|
||||
|
||||
// return the keylet for the specified bridge or nullopt if the
|
||||
// request is malformed
|
||||
auto const maybeKeylet = [&]() -> std::optional<Keylet> {
|
||||
try
|
||||
{
|
||||
if (!context.params.isMember(jss::bridge_account))
|
||||
return std::nullopt;
|
||||
|
||||
auto const& jsBridgeAccount =
|
||||
context.params[jss::bridge_account];
|
||||
if (!jsBridgeAccount.isString())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto const account =
|
||||
parseBase58<AccountID>(jsBridgeAccount.asString());
|
||||
if (!account || account->isZero())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// This may throw and is the reason for the `try` block. The
|
||||
// try block has a larger scope so the `bridge` variable
|
||||
// doesn't need to be an optional.
|
||||
STXChainBridge const bridge(context.params[jss::bridge]);
|
||||
STXChainBridge::ChainType const chainType =
|
||||
STXChainBridge::srcChain(
|
||||
account == bridge.lockingChainDoor());
|
||||
if (account != bridge.door(chainType))
|
||||
return std::nullopt;
|
||||
|
||||
return keylet::bridge(bridge, chainType);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}();
|
||||
|
||||
if (maybeKeylet)
|
||||
{
|
||||
uNodeIndex = maybeKeylet->key;
|
||||
}
|
||||
else
|
||||
{
|
||||
uNodeIndex = beast::zero;
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
}
|
||||
else if (context.params.isMember(jss::xchain_owned_claim_id))
|
||||
{
|
||||
expectedType = ltXCHAIN_OWNED_CLAIM_ID;
|
||||
auto& claim_id = context.params[jss::xchain_owned_claim_id];
|
||||
if (claim_id.isString())
|
||||
{
|
||||
// we accept a node id as specifier of a xchain claim id
|
||||
if (!uNodeIndex.parseHex(claim_id.asString()))
|
||||
{
|
||||
uNodeIndex = beast::zero;
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
}
|
||||
else if (
|
||||
!claim_id.isObject() ||
|
||||
!(claim_id.isMember(sfIssuingChainDoor.getJsonName()) &&
|
||||
claim_id[sfIssuingChainDoor.getJsonName()].isString()) ||
|
||||
!(claim_id.isMember(sfLockingChainDoor.getJsonName()) &&
|
||||
claim_id[sfLockingChainDoor.getJsonName()].isString()) ||
|
||||
!claim_id.isMember(sfIssuingChainIssue.getJsonName()) ||
|
||||
!claim_id.isMember(sfLockingChainIssue.getJsonName()) ||
|
||||
!claim_id.isMember(jss::xchain_owned_claim_id))
|
||||
{
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not specified with a node id, a claim_id is specified by
|
||||
// four strings defining the bridge (locking_chain_door,
|
||||
// locking_chain_issue, issuing_chain_door, issuing_chain_issue)
|
||||
// and the claim id sequence number.
|
||||
auto lockingChainDoor = parseBase58<AccountID>(
|
||||
claim_id[sfLockingChainDoor.getJsonName()].asString());
|
||||
auto issuingChainDoor = parseBase58<AccountID>(
|
||||
claim_id[sfIssuingChainDoor.getJsonName()].asString());
|
||||
Issue lockingChainIssue, issuingChainIssue;
|
||||
bool valid = lockingChainDoor && issuingChainDoor;
|
||||
if (valid)
|
||||
{
|
||||
try
|
||||
{
|
||||
lockingChainIssue = issueFromJson(
|
||||
claim_id[sfLockingChainIssue.getJsonName()]);
|
||||
issuingChainIssue = issueFromJson(
|
||||
claim_id[sfIssuingChainIssue.getJsonName()]);
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
valid = false;
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
}
|
||||
|
||||
if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral())
|
||||
{
|
||||
auto seq = claim_id[jss::xchain_owned_claim_id].asUInt();
|
||||
|
||||
STXChainBridge bridge_spec(
|
||||
*lockingChainDoor,
|
||||
lockingChainIssue,
|
||||
*issuingChainDoor,
|
||||
issuingChainIssue);
|
||||
Keylet keylet = keylet::xChainClaimID(bridge_spec, seq);
|
||||
uNodeIndex = keylet.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (context.params.isMember(
|
||||
jss::xchain_owned_create_account_claim_id))
|
||||
{
|
||||
// see object definition in LedgerFormats.cpp
|
||||
expectedType = ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID;
|
||||
auto& claim_id =
|
||||
context.params[jss::xchain_owned_create_account_claim_id];
|
||||
if (claim_id.isString())
|
||||
{
|
||||
// we accept a node id as specifier of a xchain create account
|
||||
// claim_id
|
||||
if (!uNodeIndex.parseHex(claim_id.asString()))
|
||||
{
|
||||
uNodeIndex = beast::zero;
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
}
|
||||
else if (
|
||||
!claim_id.isObject() ||
|
||||
!(claim_id.isMember(sfIssuingChainDoor.getJsonName()) &&
|
||||
claim_id[sfIssuingChainDoor.getJsonName()].isString()) ||
|
||||
!(claim_id.isMember(sfLockingChainDoor.getJsonName()) &&
|
||||
claim_id[sfLockingChainDoor.getJsonName()].isString()) ||
|
||||
!claim_id.isMember(sfIssuingChainIssue.getJsonName()) ||
|
||||
!claim_id.isMember(sfLockingChainIssue.getJsonName()) ||
|
||||
!claim_id.isMember(jss::xchain_owned_create_account_claim_id))
|
||||
{
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not specified with a node id, a create account claim_id is
|
||||
// specified by four strings defining the bridge
|
||||
// (locking_chain_door, locking_chain_issue, issuing_chain_door,
|
||||
// issuing_chain_issue) and the create account claim id sequence
|
||||
// number.
|
||||
auto lockingChainDoor = parseBase58<AccountID>(
|
||||
claim_id[sfLockingChainDoor.getJsonName()].asString());
|
||||
auto issuingChainDoor = parseBase58<AccountID>(
|
||||
claim_id[sfIssuingChainDoor.getJsonName()].asString());
|
||||
Issue lockingChainIssue, issuingChainIssue;
|
||||
bool valid = lockingChainDoor && issuingChainDoor;
|
||||
if (valid)
|
||||
{
|
||||
try
|
||||
{
|
||||
lockingChainIssue = issueFromJson(
|
||||
claim_id[sfLockingChainIssue.getJsonName()]);
|
||||
issuingChainIssue = issueFromJson(
|
||||
claim_id[sfIssuingChainIssue.getJsonName()]);
|
||||
}
|
||||
catch (std::runtime_error const& ex)
|
||||
{
|
||||
valid = false;
|
||||
jvResult[jss::error] = "malformedRequest";
|
||||
}
|
||||
}
|
||||
|
||||
if (valid &&
|
||||
claim_id[jss::xchain_owned_create_account_claim_id]
|
||||
.isIntegral())
|
||||
{
|
||||
auto seq =
|
||||
claim_id[jss::xchain_owned_create_account_claim_id]
|
||||
.asUInt();
|
||||
|
||||
STXChainBridge bridge_spec(
|
||||
*lockingChainDoor,
|
||||
lockingChainIssue,
|
||||
*issuingChainDoor,
|
||||
issuingChainIssue);
|
||||
Keylet keylet =
|
||||
keylet::xChainCreateAccountClaimID(bridge_spec, seq);
|
||||
uNodeIndex = keylet.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.params.isMember("params") &&
|
||||
|
||||
@@ -982,7 +982,7 @@ chooseLedgerEntryType(Json::Value const& params)
|
||||
std::pair<RPC::Status, LedgerEntryType> result{RPC::Status::OK, ltANY};
|
||||
if (params.isMember(jss::type))
|
||||
{
|
||||
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 16>
|
||||
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 19>
|
||||
types{
|
||||
{{jss::account, ltACCOUNT_ROOT},
|
||||
{jss::amendments, ltAMENDMENTS},
|
||||
@@ -999,7 +999,11 @@ chooseLedgerEntryType(Json::Value const& params)
|
||||
{jss::ticket, ltTICKET},
|
||||
{jss::nft_offer, ltNFTOKEN_OFFER},
|
||||
{jss::nft_page, ltNFTOKEN_PAGE},
|
||||
{jss::amm, ltAMM}}};
|
||||
{jss::amm, ltAMM},
|
||||
{jss::bridge, ltBRIDGE},
|
||||
{jss::xchain_owned_claim_id, ltXCHAIN_OWNED_CLAIM_ID},
|
||||
{jss::xchain_owned_create_account_claim_id,
|
||||
ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}}};
|
||||
|
||||
auto const& p = params[jss::type];
|
||||
if (!p.isString())
|
||||
@@ -1103,11 +1107,13 @@ getLedgerByContext(RPC::JsonContext& context)
|
||||
return RPC::make_param_error("Ledger index too small");
|
||||
|
||||
auto const j = context.app.journal("RPCHandler");
|
||||
// Try to get the hash of the desired ledger from the validated ledger
|
||||
// Try to get the hash of the desired ledger from the validated
|
||||
// ledger
|
||||
auto neededHash = hashOfSeq(*ledger, ledgerIndex, j);
|
||||
if (!neededHash)
|
||||
{
|
||||
// Find a ledger more likely to have the hash of the desired ledger
|
||||
// Find a ledger more likely to have the hash of the desired
|
||||
// ledger
|
||||
auto const refIndex = getCandidateLedger(ledgerIndex);
|
||||
auto refHash = hashOfSeq(*ledger, refIndex, j);
|
||||
assert(refHash);
|
||||
@@ -1115,8 +1121,8 @@ getLedgerByContext(RPC::JsonContext& context)
|
||||
ledger = ledgerMaster.getLedgerByHash(*refHash);
|
||||
if (!ledger)
|
||||
{
|
||||
// We don't have the ledger we need to figure out which ledger
|
||||
// they want. Try to get it.
|
||||
// We don't have the ledger we need to figure out which
|
||||
// ledger they want. Try to get it.
|
||||
|
||||
if (auto il = context.app.getInboundLedgers().acquire(
|
||||
*refHash, refIndex, InboundLedger::Reason::GENERIC))
|
||||
|
||||
5121
src/test/app/XChain_test.cpp
Normal file
5121
src/test/app/XChain_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
67
src/test/jtx/attester.h
Normal file
67
src/test/jtx/attester.h
Normal file
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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_TEST_JTX_ATTESTER_H_INCLUDED
|
||||
#define RIPPLE_TEST_JTX_ATTESTER_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/Buffer.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class PublicKey;
|
||||
class SecretKey;
|
||||
class STXChainBridge;
|
||||
class STAmount;
|
||||
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
Buffer
|
||||
sign_claim_attestation(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<AccountID> const& dst);
|
||||
|
||||
Buffer
|
||||
sign_create_account_attestation(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
STAmount const& rewardAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
AccountID const& dst);
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
82
src/test/jtx/impl/attester.cpp
Normal file
82
src/test/jtx/impl/attester.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx/attester.h>
|
||||
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/SecretKey.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
Buffer
|
||||
sign_claim_attestation(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<AccountID> const& dst)
|
||||
{
|
||||
auto const toSign = Attestations::AttestationClaim::message(
|
||||
bridge,
|
||||
sendingAccount,
|
||||
sendingAmount,
|
||||
rewardAccount,
|
||||
wasLockingChainSend,
|
||||
claimID,
|
||||
dst);
|
||||
return sign(pk, sk, makeSlice(toSign));
|
||||
}
|
||||
|
||||
Buffer
|
||||
sign_create_account_attestation(
|
||||
PublicKey const& pk,
|
||||
SecretKey const& sk,
|
||||
STXChainBridge const& bridge,
|
||||
AccountID const& sendingAccount,
|
||||
STAmount const& sendingAmount,
|
||||
STAmount const& rewardAmount,
|
||||
AccountID const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
AccountID const& dst)
|
||||
{
|
||||
auto const toSign = Attestations::AttestationCreateAccount::message(
|
||||
bridge,
|
||||
sendingAccount,
|
||||
sendingAmount,
|
||||
rewardAmount,
|
||||
rewardAccount,
|
||||
wasLockingChainSend,
|
||||
createCount,
|
||||
dst);
|
||||
return sign(pk, sk, makeSlice(toSign));
|
||||
}
|
||||
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
516
src/test/jtx/impl/xchain_bridge.cpp
Normal file
516
src/test/jtx/impl/xchain_bridge.cpp
Normal file
@@ -0,0 +1,516 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx/xchain_bridge.h>
|
||||
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/Issue.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/STBase.h>
|
||||
#include <ripple/protocol/STInteger.h>
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/attester.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
// use this for creating a bridge for a transaction
|
||||
Json::Value
|
||||
bridge(
|
||||
Account const& lockingChainDoor,
|
||||
Issue const& lockingChainIssue,
|
||||
Account const& issuingChainDoor,
|
||||
Issue const& issuingChainIssue)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human();
|
||||
jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue);
|
||||
jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human();
|
||||
jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue);
|
||||
return jv;
|
||||
}
|
||||
|
||||
// use this for creating a bridge for a rpc query
|
||||
Json::Value
|
||||
bridge_rpc(
|
||||
Account const& lockingChainDoor,
|
||||
Issue const& lockingChainIssue,
|
||||
Account const& issuingChainDoor,
|
||||
Issue const& issuingChainIssue)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human();
|
||||
jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue);
|
||||
jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human();
|
||||
jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue);
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
bridge_create(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
STAmount const& reward,
|
||||
std::optional<STAmount> const& minAccountCreate)
|
||||
{
|
||||
Json::Value jv;
|
||||
|
||||
jv[jss::Account] = acc.human();
|
||||
jv[sfXChainBridge.getJsonName()] = bridge;
|
||||
jv[sfSignatureReward.getJsonName()] = reward.getJson(JsonOptions::none);
|
||||
if (minAccountCreate)
|
||||
jv[sfMinAccountCreateAmount.getJsonName()] =
|
||||
minAccountCreate->getJson(JsonOptions::none);
|
||||
|
||||
jv[jss::TransactionType] = jss::XChainCreateBridge;
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
bridge_modify(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
std::optional<STAmount> const& reward,
|
||||
std::optional<STAmount> const& minAccountCreate)
|
||||
{
|
||||
Json::Value jv;
|
||||
|
||||
jv[jss::Account] = acc.human();
|
||||
jv[sfXChainBridge.getJsonName()] = bridge;
|
||||
if (reward)
|
||||
jv[sfSignatureReward.getJsonName()] =
|
||||
reward->getJson(JsonOptions::none);
|
||||
if (minAccountCreate)
|
||||
jv[sfMinAccountCreateAmount.getJsonName()] =
|
||||
minAccountCreate->getJson(JsonOptions::none);
|
||||
|
||||
jv[jss::TransactionType] = jss::XChainModifyBridge;
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
xchain_create_claim_id(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
STAmount const& reward,
|
||||
Account const& otherChainSource)
|
||||
{
|
||||
Json::Value jv;
|
||||
|
||||
jv[jss::Account] = acc.human();
|
||||
jv[sfXChainBridge.getJsonName()] = bridge;
|
||||
jv[sfSignatureReward.getJsonName()] = reward.getJson(JsonOptions::none);
|
||||
jv[sfOtherChainSource.getJsonName()] = otherChainSource.human();
|
||||
|
||||
jv[jss::TransactionType] = jss::XChainCreateClaimID;
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
xchain_commit(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
std::uint32_t claimID,
|
||||
AnyAmount const& amt,
|
||||
std::optional<Account> const& dst)
|
||||
{
|
||||
Json::Value jv;
|
||||
|
||||
jv[jss::Account] = acc.human();
|
||||
jv[sfXChainBridge.getJsonName()] = bridge;
|
||||
jv[sfXChainClaimID.getJsonName()] = claimID;
|
||||
jv[jss::Amount] = amt.value.getJson(JsonOptions::none);
|
||||
if (dst)
|
||||
jv[sfOtherChainDestination.getJsonName()] = dst->human();
|
||||
|
||||
jv[jss::TransactionType] = jss::XChainCommit;
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
xchain_claim(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
std::uint32_t claimID,
|
||||
AnyAmount const& amt,
|
||||
Account const& dst)
|
||||
{
|
||||
Json::Value jv;
|
||||
|
||||
jv[sfAccount.getJsonName()] = acc.human();
|
||||
jv[sfXChainBridge.getJsonName()] = bridge;
|
||||
jv[sfXChainClaimID.getJsonName()] = claimID;
|
||||
jv[sfDestination.getJsonName()] = dst.human();
|
||||
jv[sfAmount.getJsonName()] = amt.value.getJson(JsonOptions::none);
|
||||
|
||||
jv[jss::TransactionType] = jss::XChainClaim;
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
sidechain_xchain_account_create(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
Account const& dst,
|
||||
AnyAmount const& amt,
|
||||
AnyAmount const& reward)
|
||||
{
|
||||
Json::Value jv;
|
||||
|
||||
jv[sfAccount.getJsonName()] = acc.human();
|
||||
jv[sfXChainBridge.getJsonName()] = bridge;
|
||||
jv[sfDestination.getJsonName()] = dst.human();
|
||||
jv[sfAmount.getJsonName()] = amt.value.getJson(JsonOptions::none);
|
||||
jv[sfSignatureReward.getJsonName()] =
|
||||
reward.value.getJson(JsonOptions::none);
|
||||
|
||||
jv[jss::TransactionType] = jss::XChainAccountCreateCommit;
|
||||
jv[jss::Flags] = tfUniversal;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
claim_attestation(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
jtx::Account const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<jtx::Account> const& dst,
|
||||
jtx::signer const& signer)
|
||||
{
|
||||
STXChainBridge const stBridge(jvBridge);
|
||||
|
||||
auto const& pk = signer.account.pk();
|
||||
auto const& sk = signer.account.sk();
|
||||
auto const sig = sign_claim_attestation(
|
||||
pk,
|
||||
sk,
|
||||
stBridge,
|
||||
sendingAccount,
|
||||
sendingAmount.value,
|
||||
rewardAccount,
|
||||
wasLockingChainSend,
|
||||
claimID,
|
||||
dst);
|
||||
|
||||
Json::Value result;
|
||||
|
||||
result[sfAccount.getJsonName()] = submittingAccount.human();
|
||||
result[sfXChainBridge.getJsonName()] = jvBridge;
|
||||
|
||||
result[sfAttestationSignerAccount.getJsonName()] = signer.account.human();
|
||||
result[sfPublicKey.getJsonName()] = strHex(pk.slice());
|
||||
result[sfSignature.getJsonName()] = strHex(sig);
|
||||
result[sfOtherChainSource.getJsonName()] = toBase58(sendingAccount);
|
||||
result[sfAmount.getJsonName()] =
|
||||
sendingAmount.value.getJson(JsonOptions::none);
|
||||
result[sfAttestationRewardAccount.getJsonName()] = toBase58(rewardAccount);
|
||||
result[sfWasLockingChainSend.getJsonName()] = wasLockingChainSend ? 1 : 0;
|
||||
|
||||
result[sfXChainClaimID.getJsonName()] =
|
||||
STUInt64{claimID}.getJson(JsonOptions::none);
|
||||
if (dst)
|
||||
result[sfDestination.getJsonName()] = toBase58(*dst);
|
||||
|
||||
result[jss::TransactionType] = jss::XChainAddClaimAttestation;
|
||||
result[jss::Flags] = tfUniversal;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
create_account_attestation(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
jtx::AnyAmount const& rewardAmount,
|
||||
jtx::Account const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
jtx::Account const& dst,
|
||||
jtx::signer const& signer)
|
||||
{
|
||||
STXChainBridge const stBridge(jvBridge);
|
||||
|
||||
auto const& pk = signer.account.pk();
|
||||
auto const& sk = signer.account.sk();
|
||||
auto const sig = jtx::sign_create_account_attestation(
|
||||
pk,
|
||||
sk,
|
||||
stBridge,
|
||||
sendingAccount,
|
||||
sendingAmount.value,
|
||||
rewardAmount.value,
|
||||
rewardAccount,
|
||||
wasLockingChainSend,
|
||||
createCount,
|
||||
dst);
|
||||
|
||||
Json::Value result;
|
||||
|
||||
result[sfAccount.getJsonName()] = submittingAccount.human();
|
||||
result[sfXChainBridge.getJsonName()] = jvBridge;
|
||||
|
||||
result[sfAttestationSignerAccount.getJsonName()] = signer.account.human();
|
||||
result[sfPublicKey.getJsonName()] = strHex(pk.slice());
|
||||
result[sfSignature.getJsonName()] = strHex(sig);
|
||||
result[sfOtherChainSource.getJsonName()] = toBase58(sendingAccount);
|
||||
result[sfAmount.getJsonName()] =
|
||||
sendingAmount.value.getJson(JsonOptions::none);
|
||||
result[sfAttestationRewardAccount.getJsonName()] = toBase58(rewardAccount);
|
||||
result[sfWasLockingChainSend.getJsonName()] = wasLockingChainSend ? 1 : 0;
|
||||
|
||||
result[sfXChainAccountCreateCount.getJsonName()] =
|
||||
STUInt64{createCount}.getJson(JsonOptions::none);
|
||||
result[sfDestination.getJsonName()] = toBase58(dst);
|
||||
result[sfSignatureReward.getJsonName()] =
|
||||
rewardAmount.value.getJson(JsonOptions::none);
|
||||
|
||||
result[jss::TransactionType] = jss::XChainAddAccountCreateAttestation;
|
||||
result[jss::Flags] = tfUniversal;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JValueVec
|
||||
claim_attestations(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
std::vector<jtx::Account> const& rewardAccounts,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<jtx::Account> const& dst,
|
||||
std::vector<jtx::signer> const& signers,
|
||||
std::size_t const numAtts,
|
||||
std::size_t const fromIdx)
|
||||
{
|
||||
assert(fromIdx + numAtts <= rewardAccounts.size());
|
||||
assert(fromIdx + numAtts <= signers.size());
|
||||
JValueVec vec;
|
||||
vec.reserve(numAtts);
|
||||
for (auto i = fromIdx; i < fromIdx + numAtts; ++i)
|
||||
vec.emplace_back(claim_attestation(
|
||||
submittingAccount,
|
||||
jvBridge,
|
||||
sendingAccount,
|
||||
sendingAmount,
|
||||
rewardAccounts[i],
|
||||
wasLockingChainSend,
|
||||
claimID,
|
||||
dst,
|
||||
signers[i]));
|
||||
return vec;
|
||||
}
|
||||
|
||||
JValueVec
|
||||
create_account_attestations(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
jtx::AnyAmount const& rewardAmount,
|
||||
std::vector<jtx::Account> const& rewardAccounts,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
jtx::Account const& dst,
|
||||
std::vector<jtx::signer> const& signers,
|
||||
std::size_t const numAtts,
|
||||
std::size_t const fromIdx)
|
||||
{
|
||||
assert(fromIdx + numAtts <= rewardAccounts.size());
|
||||
assert(fromIdx + numAtts <= signers.size());
|
||||
JValueVec vec;
|
||||
vec.reserve(numAtts);
|
||||
for (auto i = fromIdx; i < fromIdx + numAtts; ++i)
|
||||
vec.emplace_back(create_account_attestation(
|
||||
submittingAccount,
|
||||
jvBridge,
|
||||
sendingAccount,
|
||||
sendingAmount,
|
||||
rewardAmount,
|
||||
rewardAccounts[i],
|
||||
wasLockingChainSend,
|
||||
createCount,
|
||||
dst,
|
||||
signers[i]));
|
||||
return vec;
|
||||
}
|
||||
|
||||
XChainBridgeObjects::XChainBridgeObjects()
|
||||
: mcDoor("mcDoor")
|
||||
, mcAlice("mcAlice")
|
||||
, mcBob("mcBob")
|
||||
, mcCarol("mcCarol")
|
||||
, mcGw("mcGw")
|
||||
, scDoor("scDoor")
|
||||
, scAlice("scAlice")
|
||||
, scBob("scBob")
|
||||
, scCarol("scCarol")
|
||||
, scGw("scGw")
|
||||
, scAttester("scAttester")
|
||||
, scReward("scReward")
|
||||
, mcuDoor("mcuDoor")
|
||||
, mcuAlice("mcuAlice")
|
||||
, mcuBob("mcuBob")
|
||||
, mcuCarol("mcuCarol")
|
||||
, mcuGw("mcuGw")
|
||||
, scuDoor("scuDoor")
|
||||
, scuAlice("scuAlice")
|
||||
, scuBob("scuBob")
|
||||
, scuCarol("scuCarol")
|
||||
, scuGw("scuGw")
|
||||
, mcUSD(mcGw["USD"])
|
||||
, scUSD(scGw["USD"])
|
||||
, jvXRPBridgeRPC(
|
||||
bridge_rpc(mcDoor, xrpIssue(), Account::master, xrpIssue()))
|
||||
, jvb(bridge(mcDoor, xrpIssue(), Account::master, xrpIssue()))
|
||||
, jvub(bridge(mcuDoor, xrpIssue(), Account::master, xrpIssue()))
|
||||
, features(supported_amendments() | FeatureBitset{featureXChainBridge})
|
||||
, signers([] {
|
||||
constexpr int numSigners = UT_XCHAIN_DEFAULT_NUM_SIGNERS;
|
||||
std::vector<signer> result;
|
||||
result.reserve(numSigners);
|
||||
for (int i = 0; i < numSigners; ++i)
|
||||
{
|
||||
using namespace std::literals;
|
||||
auto const a = Account(
|
||||
"signer_"s + std::to_string(i),
|
||||
(i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
|
||||
result.emplace_back(a);
|
||||
}
|
||||
return result;
|
||||
}())
|
||||
, alt_signers([] {
|
||||
constexpr int numSigners = UT_XCHAIN_DEFAULT_NUM_SIGNERS;
|
||||
std::vector<signer> result;
|
||||
result.reserve(numSigners);
|
||||
for (int i = 0; i < numSigners; ++i)
|
||||
{
|
||||
using namespace std::literals;
|
||||
auto const a = Account(
|
||||
"alt_signer_"s + std::to_string(i),
|
||||
(i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
|
||||
result.emplace_back(a);
|
||||
}
|
||||
return result;
|
||||
}())
|
||||
, payee([&] {
|
||||
std::vector<Account> r;
|
||||
r.reserve(signers.size());
|
||||
for (int i = 0, e = signers.size(); i != e; ++i)
|
||||
{
|
||||
r.push_back(scReward);
|
||||
}
|
||||
return r;
|
||||
}())
|
||||
, payees([&] {
|
||||
std::vector<Account> r;
|
||||
r.reserve(signers.size());
|
||||
for (int i = 0, e = signers.size(); i != e; ++i)
|
||||
{
|
||||
using namespace std::literals;
|
||||
auto const a = Account("reward_"s + std::to_string(i));
|
||||
r.push_back(a);
|
||||
}
|
||||
return r;
|
||||
}())
|
||||
, quorum(UT_XCHAIN_DEFAULT_QUORUM)
|
||||
, reward(XRP(1))
|
||||
, split_reward_quorum(
|
||||
divide(reward, STAmount(UT_XCHAIN_DEFAULT_QUORUM), reward.issue()))
|
||||
, split_reward_everyone(divide(
|
||||
reward,
|
||||
STAmount(UT_XCHAIN_DEFAULT_NUM_SIGNERS),
|
||||
reward.issue()))
|
||||
, tiny_reward(drops(37))
|
||||
, tiny_reward_split((divide(
|
||||
tiny_reward,
|
||||
STAmount(UT_XCHAIN_DEFAULT_QUORUM),
|
||||
tiny_reward.issue())))
|
||||
, tiny_reward_remainder(
|
||||
tiny_reward -
|
||||
multiply(
|
||||
tiny_reward_split,
|
||||
STAmount(UT_XCHAIN_DEFAULT_QUORUM),
|
||||
tiny_reward.issue()))
|
||||
, one_xrp(XRP(1))
|
||||
, xrp_dust(divide(one_xrp, STAmount(10000), one_xrp.issue()))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
XChainBridgeObjects::createMcBridgeObjects(Env& mcEnv)
|
||||
{
|
||||
STAmount xrp_funds{XRP(10000)};
|
||||
mcEnv.fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
|
||||
|
||||
// Signer's list must match the attestation signers
|
||||
mcEnv(jtx::signers(mcDoor, signers.size(), signers));
|
||||
|
||||
// create XRP bridges in both direction
|
||||
auto const reward = XRP(1);
|
||||
STAmount const minCreate = XRP(20);
|
||||
|
||||
mcEnv(bridge_create(mcDoor, jvb, reward, minCreate));
|
||||
mcEnv.close();
|
||||
}
|
||||
|
||||
void
|
||||
XChainBridgeObjects::createScBridgeObjects(Env& scEnv)
|
||||
{
|
||||
STAmount xrp_funds{XRP(10000)};
|
||||
scEnv.fund(
|
||||
xrp_funds, scDoor, scAlice, scBob, scCarol, scGw, scAttester, scReward);
|
||||
|
||||
// Signer's list must match the attestation signers
|
||||
scEnv(jtx::signers(Account::master, signers.size(), signers));
|
||||
|
||||
// create XRP bridges in both direction
|
||||
auto const reward = XRP(1);
|
||||
STAmount const minCreate = XRP(20);
|
||||
|
||||
scEnv(bridge_create(Account::master, jvb, reward, minCreate));
|
||||
scEnv.close();
|
||||
}
|
||||
|
||||
void
|
||||
XChainBridgeObjects::createBridgeObjects(Env& mcEnv, Env& scEnv)
|
||||
{
|
||||
createMcBridgeObjects(mcEnv);
|
||||
createScBridgeObjects(scEnv);
|
||||
}
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
260
src/test/jtx/xchain_bridge.h
Normal file
260
src/test/jtx/xchain_bridge.h
Normal file
@@ -0,0 +1,260 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_TEST_JTX_XCHAINBRIDGE_H_INCLUDED
|
||||
#define RIPPLE_TEST_JTX_XCHAINBRIDGE_H_INCLUDED
|
||||
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <ripple/protocol/XChainAttestations.h>
|
||||
#include <test/jtx/Account.h>
|
||||
#include <test/jtx/amount.h>
|
||||
#include <test/jtx/multisign.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
using JValueVec = std::vector<Json::Value>;
|
||||
|
||||
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS = 5;
|
||||
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM = 4;
|
||||
|
||||
Json::Value
|
||||
bridge(
|
||||
Account const& lockingChainDoor,
|
||||
Issue const& lockingChainIssue,
|
||||
Account const& issuingChainDoor,
|
||||
Issue const& issuingChainIssue);
|
||||
|
||||
Json::Value
|
||||
bridge_create(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
STAmount const& reward,
|
||||
std::optional<STAmount> const& minAccountCreate = std::nullopt);
|
||||
|
||||
Json::Value
|
||||
bridge_modify(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
std::optional<STAmount> const& reward,
|
||||
std::optional<STAmount> const& minAccountCreate = std::nullopt);
|
||||
|
||||
Json::Value
|
||||
xchain_create_claim_id(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
STAmount const& reward,
|
||||
Account const& otherChainSource);
|
||||
|
||||
Json::Value
|
||||
xchain_commit(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
std::uint32_t claimID,
|
||||
AnyAmount const& amt,
|
||||
std::optional<Account> const& dst = std::nullopt);
|
||||
|
||||
Json::Value
|
||||
xchain_claim(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
std::uint32_t claimID,
|
||||
AnyAmount const& amt,
|
||||
Account const& dst);
|
||||
|
||||
Json::Value
|
||||
sidechain_xchain_account_create(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
Account const& dst,
|
||||
AnyAmount const& amt,
|
||||
AnyAmount const& xChainFee);
|
||||
|
||||
Json::Value
|
||||
sidechain_xchain_account_claim(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge,
|
||||
Account const& dst,
|
||||
AnyAmount const& amt);
|
||||
|
||||
Json::Value
|
||||
claim_attestation(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
jtx::Account const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<jtx::Account> const& dst,
|
||||
jtx::signer const& signer);
|
||||
|
||||
Json::Value
|
||||
create_account_attestation(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
jtx::AnyAmount const& rewardAmount,
|
||||
jtx::Account const& rewardAccount,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
jtx::Account const& dst,
|
||||
jtx::signer const& signer);
|
||||
|
||||
JValueVec
|
||||
claim_attestations(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
std::vector<jtx::Account> const& rewardAccounts,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t claimID,
|
||||
std::optional<jtx::Account> const& dst,
|
||||
std::vector<jtx::signer> const& signers,
|
||||
std::size_t const numAtts = UT_XCHAIN_DEFAULT_QUORUM,
|
||||
std::size_t const fromIdx = 0);
|
||||
|
||||
JValueVec
|
||||
create_account_attestations(
|
||||
jtx::Account const& submittingAccount,
|
||||
Json::Value const& jvBridge,
|
||||
jtx::Account const& sendingAccount,
|
||||
jtx::AnyAmount const& sendingAmount,
|
||||
jtx::AnyAmount const& rewardAmount,
|
||||
std::vector<jtx::Account> const& rewardAccounts,
|
||||
bool wasLockingChainSend,
|
||||
std::uint64_t createCount,
|
||||
jtx::Account const& dst,
|
||||
std::vector<jtx::signer> const& signers,
|
||||
std::size_t const numAtts = UT_XCHAIN_DEFAULT_QUORUM,
|
||||
std::size_t const fromIdx = 0);
|
||||
|
||||
struct XChainBridgeObjects
|
||||
{
|
||||
// funded accounts
|
||||
Account const mcDoor;
|
||||
Account const mcAlice;
|
||||
Account const mcBob;
|
||||
Account const mcCarol;
|
||||
Account const mcGw;
|
||||
Account const scDoor;
|
||||
Account const scAlice;
|
||||
Account const scBob;
|
||||
Account const scCarol;
|
||||
Account const scGw;
|
||||
Account const scAttester;
|
||||
Account const scReward;
|
||||
|
||||
// unfunded accounts
|
||||
Account const mcuDoor;
|
||||
Account const mcuAlice;
|
||||
Account const mcuBob;
|
||||
Account const mcuCarol;
|
||||
Account const mcuGw;
|
||||
Account const scuDoor;
|
||||
Account const scuAlice;
|
||||
Account const scuBob;
|
||||
Account const scuCarol;
|
||||
Account const scuGw;
|
||||
|
||||
IOU const mcUSD;
|
||||
IOU const scUSD;
|
||||
|
||||
Json::Value const jvXRPBridgeRPC;
|
||||
Json::Value jvb; // standard xrp bridge def for tx
|
||||
Json::Value jvub; // standard xrp bridge def for tx, unfunded accounts
|
||||
|
||||
FeatureBitset const features;
|
||||
std::vector<signer> const signers;
|
||||
std::vector<signer> const alt_signers;
|
||||
std::vector<Account> const payee;
|
||||
std::vector<Account> const payees;
|
||||
std::uint32_t const quorum;
|
||||
|
||||
STAmount const reward; // 1 xrp
|
||||
STAmount const split_reward_quorum; // 250,000 drops
|
||||
STAmount const split_reward_everyone; // 200,000 drops
|
||||
|
||||
const STAmount tiny_reward; // 37 drops
|
||||
const STAmount tiny_reward_split; // 9 drops
|
||||
const STAmount tiny_reward_remainder; // 1 drops
|
||||
|
||||
const STAmount one_xrp;
|
||||
const STAmount xrp_dust;
|
||||
|
||||
static constexpr int drop_per_xrp = 1000000;
|
||||
|
||||
XChainBridgeObjects();
|
||||
|
||||
void
|
||||
createMcBridgeObjects(Env& mcEnv);
|
||||
|
||||
void
|
||||
createScBridgeObjects(Env& scEnv);
|
||||
|
||||
void
|
||||
createBridgeObjects(Env& mcEnv, Env& scEnv);
|
||||
|
||||
JValueVec
|
||||
att_create_acct_vec(
|
||||
std::uint64_t createCount,
|
||||
jtx::AnyAmount const& amt,
|
||||
jtx::Account const& dst,
|
||||
std::size_t const numAtts,
|
||||
std::size_t const fromIdx = 0)
|
||||
{
|
||||
return create_account_attestations(
|
||||
scAttester,
|
||||
jvb,
|
||||
mcCarol,
|
||||
amt,
|
||||
reward,
|
||||
payees,
|
||||
true,
|
||||
createCount,
|
||||
dst,
|
||||
signers,
|
||||
numAtts,
|
||||
fromIdx);
|
||||
}
|
||||
|
||||
Json::Value
|
||||
create_bridge(
|
||||
Account const& acc,
|
||||
Json::Value const& bridge = Json::nullValue,
|
||||
STAmount const& _reward = XRP(1),
|
||||
std::optional<STAmount> const& minAccountCreate = std::nullopt)
|
||||
{
|
||||
return bridge_create(
|
||||
acc,
|
||||
bridge == Json::nullValue ? jvb : bridge,
|
||||
_reward,
|
||||
minAccountCreate);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/AMM.h>
|
||||
#include <test/jtx/xchain_bridge.h>
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
@@ -550,7 +551,9 @@ public:
|
||||
Account const gw{"gateway"};
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
Env env(*this);
|
||||
auto const features =
|
||||
supported_amendments() | FeatureBitset{featureXChainBridge};
|
||||
Env env(*this, features);
|
||||
|
||||
// Make a lambda we can use to get "account_objects" easily.
|
||||
auto acct_objs = [&env](
|
||||
@@ -676,6 +679,142 @@ public:
|
||||
BEAST_EXPECT(escrow[sfDestination.jsonName] == gw.human());
|
||||
BEAST_EXPECT(escrow[sfAmount.jsonName].asUInt() == 100'000'000);
|
||||
}
|
||||
{
|
||||
// Create a bridge
|
||||
test::jtx::XChainBridgeObjects x;
|
||||
Env scEnv(*this, envconfig(port_increment, 3), features);
|
||||
x.createScBridgeObjects(scEnv);
|
||||
|
||||
auto scenv_acct_objs = [&](Account const& acct, char const* type) {
|
||||
Json::Value params;
|
||||
params[jss::account] = acct.human();
|
||||
params[jss::type] = type;
|
||||
params[jss::ledger_index] = "validated";
|
||||
return scEnv.rpc("json", "account_objects", to_string(params));
|
||||
};
|
||||
|
||||
Json::Value const resp =
|
||||
scenv_acct_objs(Account::master, jss::bridge);
|
||||
|
||||
BEAST_EXPECT(acct_objs_is_size(resp, 1));
|
||||
auto const& acct_bridge =
|
||||
resp[jss::result][jss::account_objects][0u];
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfAccount.jsonName] == Account::master.human());
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfLedgerEntryType.getJsonName()] == "Bridge");
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfXChainClaimID.getJsonName()].asUInt() == 0);
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfXChainAccountClaimCount.getJsonName()].asUInt() ==
|
||||
0);
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfXChainAccountCreateCount.getJsonName()]
|
||||
.asUInt() == 0);
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfMinAccountCreateAmount.getJsonName()].asUInt() ==
|
||||
20000000);
|
||||
BEAST_EXPECT(
|
||||
acct_bridge[sfSignatureReward.getJsonName()].asUInt() ==
|
||||
1000000);
|
||||
BEAST_EXPECT(acct_bridge[sfXChainBridge.getJsonName()] == x.jvb);
|
||||
}
|
||||
{
|
||||
// Alice and Bob create a xchain sequence number that we can look
|
||||
// for in the ledger.
|
||||
test::jtx::XChainBridgeObjects x;
|
||||
Env scEnv(*this, envconfig(port_increment, 3), features);
|
||||
x.createScBridgeObjects(scEnv);
|
||||
|
||||
scEnv(
|
||||
xchain_create_claim_id(x.scAlice, x.jvb, x.reward, x.mcAlice));
|
||||
scEnv.close();
|
||||
scEnv(xchain_create_claim_id(x.scBob, x.jvb, x.reward, x.mcBob));
|
||||
scEnv.close();
|
||||
|
||||
auto scenv_acct_objs = [&](Account const& acct, char const* type) {
|
||||
Json::Value params;
|
||||
params[jss::account] = acct.human();
|
||||
params[jss::type] = type;
|
||||
params[jss::ledger_index] = "validated";
|
||||
return scEnv.rpc("json", "account_objects", to_string(params));
|
||||
};
|
||||
|
||||
{
|
||||
// Find the xchain sequence number for Andrea.
|
||||
Json::Value const resp =
|
||||
scenv_acct_objs(x.scAlice, jss::xchain_owned_claim_id);
|
||||
BEAST_EXPECT(acct_objs_is_size(resp, 1));
|
||||
|
||||
auto const& xchain_seq =
|
||||
resp[jss::result][jss::account_objects][0u];
|
||||
BEAST_EXPECT(
|
||||
xchain_seq[sfAccount.jsonName] == x.scAlice.human());
|
||||
BEAST_EXPECT(
|
||||
xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 1);
|
||||
}
|
||||
{
|
||||
// and the one for Bob
|
||||
Json::Value const resp =
|
||||
scenv_acct_objs(x.scBob, jss::xchain_owned_claim_id);
|
||||
BEAST_EXPECT(acct_objs_is_size(resp, 1));
|
||||
|
||||
auto const& xchain_seq =
|
||||
resp[jss::result][jss::account_objects][0u];
|
||||
BEAST_EXPECT(xchain_seq[sfAccount.jsonName] == x.scBob.human());
|
||||
BEAST_EXPECT(
|
||||
xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 2);
|
||||
}
|
||||
}
|
||||
{
|
||||
test::jtx::XChainBridgeObjects x;
|
||||
Env scEnv(*this, envconfig(port_increment, 3), features);
|
||||
x.createScBridgeObjects(scEnv);
|
||||
auto const amt = XRP(1000);
|
||||
|
||||
// send first batch of account create attestations, so the
|
||||
// xchain_create_account_claim_id should be present on the door
|
||||
// account (Account::master) to collect the signatures until a
|
||||
// quorum is reached
|
||||
scEnv(test::jtx::create_account_attestation(
|
||||
x.scAttester,
|
||||
x.jvb,
|
||||
x.mcCarol,
|
||||
amt,
|
||||
x.reward,
|
||||
x.payees[0],
|
||||
true,
|
||||
1,
|
||||
x.scuAlice,
|
||||
x.signers[0]));
|
||||
scEnv.close();
|
||||
|
||||
auto scenv_acct_objs = [&](Account const& acct, char const* type) {
|
||||
Json::Value params;
|
||||
params[jss::account] = acct.human();
|
||||
params[jss::type] = type;
|
||||
params[jss::ledger_index] = "validated";
|
||||
return scEnv.rpc("json", "account_objects", to_string(params));
|
||||
};
|
||||
|
||||
{
|
||||
// Find the xchain_create_account_claim_id
|
||||
Json::Value const resp = scenv_acct_objs(
|
||||
Account::master, jss::xchain_owned_create_account_claim_id);
|
||||
BEAST_EXPECT(acct_objs_is_size(resp, 1));
|
||||
|
||||
auto const& xchain_create_account_claim_id =
|
||||
resp[jss::result][jss::account_objects][0u];
|
||||
BEAST_EXPECT(
|
||||
xchain_create_account_claim_id[sfAccount.jsonName] ==
|
||||
Account::master.human());
|
||||
BEAST_EXPECT(
|
||||
xchain_create_account_claim_id[sfXChainAccountCreateCount
|
||||
.getJsonName()]
|
||||
.asUInt() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
// gw creates an offer that we can look for in the ledger.
|
||||
env(offer(gw, USD(7), XRP(14)));
|
||||
env.close();
|
||||
@@ -690,7 +829,8 @@ public:
|
||||
BEAST_EXPECT(offer[sfTakerPays.jsonName][jss::value].asUInt() == 7);
|
||||
}
|
||||
{
|
||||
// Create a payment channel from qw to alice that we can look for.
|
||||
// Create a payment channel from qw to alice that we can look
|
||||
// for.
|
||||
Json::Value jvPayChan;
|
||||
jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
|
||||
jvPayChan[jss::Flags] = tfUniversal;
|
||||
@@ -715,7 +855,7 @@ public:
|
||||
payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
|
||||
}
|
||||
// Make gw multisigning by adding a signerList.
|
||||
env(signers(gw, 6, {{alice, 7}}));
|
||||
env(jtx::signers(gw, 6, {{alice, 7}}));
|
||||
env.close();
|
||||
{
|
||||
// Find the signer list.
|
||||
|
||||
@@ -21,12 +21,331 @@
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/STXChainBridge.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/attester.h>
|
||||
#include <test/jtx/multisign.h>
|
||||
#include <test/jtx/xchain_bridge.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LedgerRPC_XChain_test : public beast::unit_test::suite,
|
||||
public test::jtx::XChainBridgeObjects
|
||||
{
|
||||
void
|
||||
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 (msg.empty())
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
jv[jss::error_message] == Json::nullValue ||
|
||||
jv[jss::error_message] == "");
|
||||
}
|
||||
else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
|
||||
BEAST_EXPECT(jv[jss::error_message] == msg);
|
||||
}
|
||||
|
||||
void
|
||||
testLedgerEntryBridge()
|
||||
{
|
||||
testcase("ledger_entry: bridge");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env mcEnv{*this, features};
|
||||
Env scEnv(*this, envconfig(port_increment, 3), features);
|
||||
|
||||
createBridgeObjects(mcEnv, scEnv);
|
||||
|
||||
std::string const ledgerHash{to_string(mcEnv.closed()->info().hash)};
|
||||
std::string bridge_index;
|
||||
Json::Value mcBridge;
|
||||
{
|
||||
// request the bridge via RPC
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::bridge_account] = mcDoor.human();
|
||||
jvParams[jss::bridge] = jvb;
|
||||
Json::Value const jrr = mcEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||
auto r = jrr[jss::node];
|
||||
// std::cout << to_string(r) << '\n';
|
||||
|
||||
BEAST_EXPECT(r.isMember(jss::Account));
|
||||
BEAST_EXPECT(r[jss::Account] == mcDoor.human());
|
||||
|
||||
BEAST_EXPECT(r.isMember(jss::Flags));
|
||||
|
||||
BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
|
||||
BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
|
||||
|
||||
// we not created an account yet
|
||||
BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
|
||||
BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
|
||||
|
||||
// we have not claimed a locking chain tx yet
|
||||
BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
|
||||
BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
|
||||
|
||||
BEAST_EXPECT(r.isMember(jss::index));
|
||||
bridge_index = r[jss::index].asString();
|
||||
mcBridge = r;
|
||||
}
|
||||
{
|
||||
// request the bridge via RPC by index
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::index] = bridge_index;
|
||||
Json::Value const jrr = mcEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||
BEAST_EXPECT(jrr[jss::node] == mcBridge);
|
||||
}
|
||||
{
|
||||
// swap door accounts and make sure we get an error value
|
||||
Json::Value jvParams;
|
||||
// Sidechain door account is "master", not scDoor
|
||||
jvParams[jss::bridge_account] = Account::master.human();
|
||||
jvParams[jss::bridge] = jvb;
|
||||
jvParams[jss::ledger_hash] = ledgerHash;
|
||||
Json::Value const jrr = mcEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
checkErrorValue(jrr, "entryNotFound", "");
|
||||
}
|
||||
{
|
||||
// create two claim ids and verify that the bridge counter was
|
||||
// incremented
|
||||
mcEnv(xchain_create_claim_id(mcAlice, jvb, reward, scAlice));
|
||||
mcEnv.close();
|
||||
mcEnv(xchain_create_claim_id(mcBob, jvb, reward, scBob));
|
||||
mcEnv.close();
|
||||
|
||||
// request the bridge via RPC
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::bridge_account] = mcDoor.human();
|
||||
jvParams[jss::bridge] = jvb;
|
||||
// std::cout << to_string(jvParams) << '\n';
|
||||
Json::Value const jrr = mcEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||
auto r = jrr[jss::node];
|
||||
|
||||
// we executed two create claim id txs
|
||||
BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
|
||||
BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testLedgerEntryClaimID()
|
||||
{
|
||||
testcase("ledger_entry: xchain_claim_id");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env mcEnv{*this, features};
|
||||
Env scEnv(*this, envconfig(port_increment, 3), features);
|
||||
|
||||
createBridgeObjects(mcEnv, scEnv);
|
||||
|
||||
scEnv(xchain_create_claim_id(scAlice, jvb, reward, mcAlice));
|
||||
scEnv.close();
|
||||
scEnv(xchain_create_claim_id(scBob, jvb, reward, mcBob));
|
||||
scEnv.close();
|
||||
|
||||
std::string bridge_index;
|
||||
{
|
||||
// request the xchain_claim_id via RPC
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
|
||||
jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
|
||||
1;
|
||||
// std::cout << to_string(jvParams) << '\n';
|
||||
Json::Value const jrr = scEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||
auto r = jrr[jss::node];
|
||||
// std::cout << to_string(r) << '\n';
|
||||
|
||||
BEAST_EXPECT(r.isMember(jss::Account));
|
||||
BEAST_EXPECT(r[jss::Account] == scAlice.human());
|
||||
BEAST_EXPECT(
|
||||
r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
|
||||
BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
|
||||
BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// request the xchain_claim_id via RPC
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
|
||||
jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
|
||||
2;
|
||||
Json::Value const jrr = scEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
|
||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||
auto r = jrr[jss::node];
|
||||
// std::cout << to_string(r) << '\n';
|
||||
|
||||
BEAST_EXPECT(r.isMember(jss::Account));
|
||||
BEAST_EXPECT(r[jss::Account] == scBob.human());
|
||||
BEAST_EXPECT(
|
||||
r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
|
||||
BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
|
||||
BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testLedgerEntryCreateAccountClaimID()
|
||||
{
|
||||
testcase("ledger_entry: xchain_create_account_claim_id");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env mcEnv{*this, features};
|
||||
Env scEnv(*this, envconfig(port_increment, 3), features);
|
||||
|
||||
// note: signers.size() and quorum are both 5 in createBridgeObjects
|
||||
createBridgeObjects(mcEnv, scEnv);
|
||||
|
||||
auto scCarol =
|
||||
Account("scCarol"); // Don't fund it - it will be created with the
|
||||
// xchain transaction
|
||||
auto const amt = XRP(1000);
|
||||
mcEnv(sidechain_xchain_account_create(
|
||||
mcAlice, jvb, scCarol, amt, reward));
|
||||
mcEnv.close();
|
||||
|
||||
// send less than quorum of attestations (otherwise funds are
|
||||
// immediately transferred and no "claim" object is created)
|
||||
size_t constexpr num_attest = 3;
|
||||
auto attestations = create_account_attestations(
|
||||
scAttester,
|
||||
jvb,
|
||||
mcAlice,
|
||||
amt,
|
||||
reward,
|
||||
payee,
|
||||
/*wasLockingChainSend*/ true,
|
||||
1,
|
||||
scCarol,
|
||||
signers,
|
||||
UT_XCHAIN_DEFAULT_NUM_SIGNERS);
|
||||
for (size_t i = 0; i < num_attest; ++i)
|
||||
{
|
||||
scEnv(attestations[i]);
|
||||
}
|
||||
scEnv.close();
|
||||
|
||||
{
|
||||
// request the create account claim_id via RPC
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::xchain_owned_create_account_claim_id] =
|
||||
jvXRPBridgeRPC;
|
||||
jvParams[jss::xchain_owned_create_account_claim_id]
|
||||
[jss::xchain_owned_create_account_claim_id] = 1;
|
||||
// std::cout << to_string(jvParams) << '\n';
|
||||
Json::Value const jrr = scEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
// std::cout << to_string(jrr) << '\n';
|
||||
|
||||
BEAST_EXPECT(jrr.isMember(jss::node));
|
||||
auto r = jrr[jss::node];
|
||||
|
||||
BEAST_EXPECT(r.isMember(jss::Account));
|
||||
BEAST_EXPECT(r[jss::Account] == Account::master.human());
|
||||
|
||||
BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
|
||||
BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
|
||||
|
||||
BEAST_EXPECT(
|
||||
r.isMember(sfXChainCreateAccountAttestations.jsonName));
|
||||
auto attest = r[sfXChainCreateAccountAttestations.jsonName];
|
||||
BEAST_EXPECT(attest.isArray());
|
||||
BEAST_EXPECT(attest.size() == 3);
|
||||
BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(
|
||||
sfXChainCreateAccountProofSig.jsonName));
|
||||
Json::Value a[num_attest];
|
||||
for (size_t i = 0; i < num_attest; ++i)
|
||||
{
|
||||
a[i] = attest[Json::Value::UInt(0)]
|
||||
[sfXChainCreateAccountProofSig.jsonName];
|
||||
BEAST_EXPECT(
|
||||
a[i].isMember(jss::Amount) &&
|
||||
a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
|
||||
BEAST_EXPECT(
|
||||
a[i].isMember(jss::Destination) &&
|
||||
a[i][jss::Destination] == scCarol.human());
|
||||
BEAST_EXPECT(
|
||||
a[i].isMember(sfAttestationSignerAccount.jsonName) &&
|
||||
std::any_of(
|
||||
signers.begin(), signers.end(), [&](signer const& s) {
|
||||
return a[i][sfAttestationSignerAccount.jsonName] ==
|
||||
s.account.human();
|
||||
}));
|
||||
BEAST_EXPECT(
|
||||
a[i].isMember(sfAttestationRewardAccount.jsonName) &&
|
||||
std::any_of(
|
||||
payee.begin(),
|
||||
payee.end(),
|
||||
[&](Account const& account) {
|
||||
return a[i][sfAttestationRewardAccount.jsonName] ==
|
||||
account.human();
|
||||
}));
|
||||
BEAST_EXPECT(
|
||||
a[i].isMember(sfWasLockingChainSend.jsonName) &&
|
||||
a[i][sfWasLockingChainSend.jsonName] == 1);
|
||||
BEAST_EXPECT(
|
||||
a[i].isMember(sfSignatureReward.jsonName) &&
|
||||
a[i][sfSignatureReward.jsonName].asInt() ==
|
||||
1 * drop_per_xrp);
|
||||
}
|
||||
}
|
||||
|
||||
// complete attestations quorum - CreateAccountClaimID should not be
|
||||
// present anymore
|
||||
for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
|
||||
{
|
||||
scEnv(attestations[i]);
|
||||
}
|
||||
scEnv.close();
|
||||
{
|
||||
// request the create account claim_id via RPC
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::xchain_owned_create_account_claim_id] =
|
||||
jvXRPBridgeRPC;
|
||||
jvParams[jss::xchain_owned_create_account_claim_id]
|
||||
[jss::xchain_owned_create_account_claim_id] = 1;
|
||||
// std::cout << to_string(jvParams) << '\n';
|
||||
Json::Value const jrr = scEnv.rpc(
|
||||
"json", "ledger_entry", to_string(jvParams))[jss::result];
|
||||
checkErrorValue(jrr, "entryNotFound", "");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testLedgerEntryBridge();
|
||||
testLedgerEntryClaimID();
|
||||
testLedgerEntryCreateAccountClaimID();
|
||||
}
|
||||
};
|
||||
|
||||
class LedgerRPC_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
@@ -1940,5 +2259,6 @@ public:
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(LedgerRPC, app, ripple);
|
||||
BEAST_DEFINE_TESTSUITE(LedgerRPC_XChain, app, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/WSClient.h>
|
||||
#include <test/jtx/envconfig.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -743,7 +744,7 @@ public:
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
using IdxHashVec = std::vector<std::pair<int, std::string>>;
|
||||
using IdxHashVec = std::vector<std::tuple<int, std::string, bool, int>>;
|
||||
|
||||
Account alice("alice");
|
||||
Account bob("bob");
|
||||
@@ -781,11 +782,14 @@ public:
|
||||
idx = r[jss::account_history_tx_index].asInt();
|
||||
if (r.isMember(jss::account_history_tx_first))
|
||||
first_flag = true;
|
||||
bool boundary = r.isMember(jss::account_history_boundary);
|
||||
int ledger_idx = r[jss::ledger_index].asInt();
|
||||
if (r.isMember(jss::transaction) &&
|
||||
r[jss::transaction].isMember(jss::hash))
|
||||
{
|
||||
auto t{r[jss::transaction]};
|
||||
v.emplace_back(
|
||||
idx, r[jss::transaction][jss::hash].asString());
|
||||
idx, t[jss::hash].asString(), boundary, ledger_idx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -838,13 +842,13 @@ public:
|
||||
hash_map<std::string, int> txHistoryMap;
|
||||
for (auto const& tx : txHistoryVec)
|
||||
{
|
||||
txHistoryMap.emplace(tx.second, tx.first);
|
||||
txHistoryMap.emplace(std::get<1>(tx), std::get<0>(tx));
|
||||
}
|
||||
|
||||
auto getHistoryIndex = [&](std::size_t i) -> std::optional<int> {
|
||||
if (i >= accountVec.size())
|
||||
return {};
|
||||
auto it = txHistoryMap.find(accountVec[i].second);
|
||||
auto it = txHistoryMap.find(std::get<1>(accountVec[i]));
|
||||
if (it == txHistoryMap.end())
|
||||
return {};
|
||||
return it->second;
|
||||
@@ -862,6 +866,48 @@ public:
|
||||
return true;
|
||||
};
|
||||
|
||||
// example of vector created from the return of `subscribe` rpc
|
||||
// with jss::accounts
|
||||
// boundary == true on last tx of ledger
|
||||
// ------------------------------------------------------------
|
||||
// (0, "E5B8B...", false, 4
|
||||
// (0, "39E1C...", false, 4
|
||||
// (0, "14EF1...", false, 4
|
||||
// (0, "386E6...", false, 4
|
||||
// (0, "00F3B...", true, 4
|
||||
// (0, "1DCDC...", false, 5
|
||||
// (0, "BD02A...", false, 5
|
||||
// (0, "D3E16...", false, 5
|
||||
// (0, "CB593...", false, 5
|
||||
// (0, "8F28B...", true, 5
|
||||
//
|
||||
// example of vector created from the return of `subscribe` rpc
|
||||
// with jss::account_history_tx_stream.
|
||||
// boundary == true on first tx of ledger
|
||||
// ------------------------------------------------------------
|
||||
// (-1, "8F28B...", false, 5
|
||||
// (-2, "CB593...", false, 5
|
||||
// (-3, "D3E16...", false, 5
|
||||
// (-4, "BD02A...", false, 5
|
||||
// (-5, "1DCDC...", true, 5
|
||||
// (-6, "00F3B...", false, 4
|
||||
// (-7, "386E6...", false, 4
|
||||
// (-8, "14EF1...", false, 4
|
||||
// (-9, "39E1C...", false, 4
|
||||
// (-10, "E5B8B...", true, 4
|
||||
|
||||
auto checkBoundary = [](IdxHashVec const& vec, bool /* forward */) {
|
||||
size_t num_tx = vec.size();
|
||||
for (size_t i = 0; i < num_tx; ++i)
|
||||
{
|
||||
auto [idx, hash, boundary, ledger] = vec[i];
|
||||
if ((i + 1 == num_tx || ledger != std::get<3>(vec[i + 1])) !=
|
||||
boundary)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
{
|
||||
@@ -880,6 +926,7 @@ public:
|
||||
auto jv = wscTxHistory->invoke("subscribe", request);
|
||||
if (!BEAST_EXPECT(goodSubRPC(jv)))
|
||||
return;
|
||||
|
||||
jv = wscTxHistory->invoke("subscribe", request);
|
||||
BEAST_EXPECT(!goodSubRPC(jv));
|
||||
|
||||
@@ -911,7 +958,6 @@ public:
|
||||
r = getTxHash(*wscTxHistory, vec, 1);
|
||||
BEAST_EXPECT(!r.first);
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
* subscribe genesis account tx history without txns
|
||||
@@ -950,8 +996,8 @@ public:
|
||||
if (!BEAST_EXPECT(r.first && r.second))
|
||||
return;
|
||||
BEAST_EXPECT(
|
||||
bobFullHistoryVec.back().second ==
|
||||
genesisFullHistoryVec.back().second);
|
||||
std::get<1>(bobFullHistoryVec.back()) ==
|
||||
std::get<1>(genesisFullHistoryVec.back()));
|
||||
|
||||
/*
|
||||
* unsubscribe to prepare next test
|
||||
@@ -987,8 +1033,8 @@ public:
|
||||
jv = wscTxHistory->invoke("unsubscribe", request);
|
||||
|
||||
BEAST_EXPECT(
|
||||
bobFullHistoryVec.back().second ==
|
||||
genesisFullHistoryVec.back().second);
|
||||
std::get<1>(bobFullHistoryVec.back()) ==
|
||||
std::get<1>(genesisFullHistoryVec.back()));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1030,11 +1076,17 @@ public:
|
||||
if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec, true)))
|
||||
return;
|
||||
|
||||
// check boundary tags
|
||||
// only account_history_tx_stream has ledger boundary information.
|
||||
if (!BEAST_EXPECT(checkBoundary(txHistoryVec, false)))
|
||||
return;
|
||||
|
||||
{
|
||||
// take out all history txns from stream to prepare next test
|
||||
IdxHashVec initFundTxns;
|
||||
if (!BEAST_EXPECT(
|
||||
getTxHash(*wscTxHistory, initFundTxns, 10).second))
|
||||
getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
|
||||
!BEAST_EXPECT(checkBoundary(initFundTxns, false)))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1046,6 +1098,12 @@ public:
|
||||
return;
|
||||
if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec, true)))
|
||||
return;
|
||||
|
||||
// check boundary tags
|
||||
// only account_history_tx_stream has ledger boundary information.
|
||||
if (!BEAST_EXPECT(checkBoundary(txHistoryVec, false)))
|
||||
return;
|
||||
|
||||
wscTxHistory->invoke("unsubscribe", request);
|
||||
wscAccount->invoke("unsubscribe", stream);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user