Side chain ledgerentry (#1144)

Fix #861
This commit is contained in:
cyan317
2024-02-01 09:12:24 +00:00
committed by GitHub
parent 3fda74e3f7
commit dc5aacfe39
12 changed files with 1196 additions and 33 deletions

View File

@@ -1350,6 +1350,19 @@ parseTaker(boost::json::value const& taker)
return Status{RippledError::rpcBAD_ISSUER, "invalidTakerAccount"}; return Status{RippledError::rpcBAD_ISSUER, "invalidTakerAccount"};
return *takerID; return *takerID;
} }
ripple::Issue
parseIssue(boost::json::object const& issue)
{
Json::Value jv;
if (issue.contains(JS(issuer)) && issue.at(JS(issuer)).is_string())
jv["issuer"] = issue.at(JS(issuer)).as_string().c_str();
if (issue.contains(JS(currency)) && issue.at(JS(currency)).is_string())
jv["currency"] = issue.at(JS(currency)).as_string().c_str();
return ripple::issueFromJson(jv);
}
bool bool
specifiesCurrentOrClosedLedger(boost::json::object const& request) specifiesCurrentOrClosedLedger(boost::json::object const& request)
{ {

View File

@@ -25,18 +25,55 @@
*/ */
#include "data/BackendInterface.h" #include "data/BackendInterface.h"
#include "data/Types.h"
#include "rpc/Amendments.h" #include "rpc/Amendments.h"
#include "rpc/Errors.h"
#include "rpc/JS.h" #include "rpc/JS.h"
#include "rpc/common/Types.h" #include "rpc/common/Types.h"
#include "util/JsonUtils.h" #include "util/JsonUtils.h"
#include "util/log/Logger.h"
#include "web/Context.h" #include "web/Context.h"
#include <boost/asio/spawn.hpp>
#include <boost/json/array.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/regex/v5/regex_fwd.hpp>
#include <boost/regex/v5/regex_match.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <ripple/basics/XRPAmount.h>
#include <ripple/basics/base_uint.h>
#include <ripple/json/json_value.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Book.h>
#include <ripple/protocol/Fees.h>
#include <ripple/protocol/Indexes.h> #include <ripple/protocol/Indexes.h>
#include <ripple/protocol/Issue.h>
#include <ripple/protocol/Keylet.h>
#include <ripple/protocol/LedgerHeader.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/Rate.h> #include <ripple/protocol/Rate.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/STBase.h>
#include <ripple/protocol/STLedgerEntry.h> #include <ripple/protocol/STLedgerEntry.h>
#include <ripple/protocol/STObject.h>
#include <ripple/protocol/STTx.h> #include <ripple/protocol/STTx.h>
#include <ripple/protocol/SecretKey.h>
#include <ripple/protocol/TxMeta.h>
#include <ripple/protocol/UintTypes.h>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <variant>
#include <vector>
namespace rpc { namespace rpc {
@@ -264,6 +301,16 @@ parseBook(boost::json::object const& request);
std::variant<Status, ripple::AccountID> std::variant<Status, ripple::AccountID>
parseTaker(boost::json::value const& taker); parseTaker(boost::json::value const& taker);
/**
* @brief Parse the json object into a ripple::Issue object.
* @param issue The json object to parse. The accepted format is { "currency" : "USD", "issuer" :
* "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59" } or {"currency" : "XRP"}
* @return The ripple::Issue object.
* @exception raise Json::error exception if the json object is not in the accepted format.
*/
ripple::Issue
parseIssue(boost::json::object const& issue);
bool bool
specifiesCurrentOrClosedLedger(boost::json::object const& request); specifiesCurrentOrClosedLedger(boost::json::object const& request);

View File

@@ -19,14 +19,21 @@
#pragma once #pragma once
#include "rpc/common/Concepts.h" #include "rpc/Errors.h"
#include "rpc/common/Specs.h" #include "rpc/common/Specs.h"
#include "rpc/common/Types.h" #include "rpc/common/Types.h"
#include "rpc/common/Validators.h" #include "rpc/common/Validators.h"
#include <boost/json/value.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <optional>
#include <string_view>
#include <utility> #include <utility>
#include <vector>
namespace rpc::meta { namespace rpc::meta {
@@ -180,6 +187,23 @@ public:
return {}; return {};
} }
/**
* @brief Runs the stored validator and produces a custom error if the wrapped validator fails. This is an overload
* for the requirement which can modify the value. Such as IfType.
*
* @param value The JSON value representing the outer object, this value can be modified by the requirement inside
* @param key The key used to retrieve the element from the outer object
* @return Possibly an error
*/
[[nodiscard]] MaybeError
verify(boost::json::value& value, std::string_view key) const
{
if (auto const res = requirement.verify(value, key); not res)
return Error{error};
return {};
}
}; };
} // namespace rpc::meta } // namespace rpc::meta

View File

@@ -217,19 +217,13 @@ CustomValidator SubscribeAccountsValidator =
return MaybeError{}; return MaybeError{};
}}; }};
CustomValidator AMMAssetValidator = CustomValidator CurrencyIssueValidator =
CustomValidator{[](boost::json::value const& value, std::string_view key) -> MaybeError { CustomValidator{[](boost::json::value const& value, std::string_view key) -> MaybeError {
if (not value.is_object()) if (not value.is_object())
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotObject"}}; return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotObject"}};
Json::Value jvAsset;
if (value.as_object().contains(JS(issuer)))
jvAsset["issuer"] = value.at(JS(issuer)).as_string().c_str();
if (value.as_object().contains(JS(currency)))
jvAsset["currency"] = value.at(JS(currency)).as_string().c_str();
// same as rippled
try { try {
ripple::issueFromJson(jvAsset); parseIssue(value.as_object());
} catch (std::runtime_error const&) { } catch (std::runtime_error const&) {
return Error{Status{ClioError::rpcMALFORMED_REQUEST}}; return Error{Status{ClioError::rpcMALFORMED_REQUEST}};
} }

View File

@@ -513,6 +513,6 @@ extern CustomValidator SubscribeAccountsValidator;
* *
* Used by amm_info. * Used by amm_info.
*/ */
extern CustomValidator AMMAssetValidator; extern CustomValidator CurrencyIssueValidator;
} // namespace rpc::validation } // namespace rpc::validation

View File

@@ -232,7 +232,7 @@ AMMInfoHandler::spec([[maybe_unused]] uint32_t apiVersion)
}, },
meta::IfType<std::string>{stringIssueValidator}, meta::IfType<std::string>{stringIssueValidator},
meta::IfType<boost::json::object>{ meta::IfType<boost::json::object>{
meta::WithCustomError{validation::AMMAssetValidator, Status(RippledError::rpcISSUE_MALFORMED)}, meta::WithCustomError{validation::CurrencyIssueValidator, Status(RippledError::rpcISSUE_MALFORMED)},
}}, }},
{JS(asset2), {JS(asset2),
meta::WithCustomError{ meta::WithCustomError{
@@ -240,7 +240,7 @@ AMMInfoHandler::spec([[maybe_unused]] uint32_t apiVersion)
}, },
meta::IfType<std::string>{stringIssueValidator}, meta::IfType<std::string>{stringIssueValidator},
meta::IfType<boost::json::object>{ meta::IfType<boost::json::object>{
meta::WithCustomError{validation::AMMAssetValidator, Status(RippledError::rpcISSUE_MALFORMED)}, meta::WithCustomError{validation::CurrencyIssueValidator, Status(RippledError::rpcISSUE_MALFORMED)},
}}, }},
{JS(amm_account), meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}}, {JS(amm_account), meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}},
{JS(account), meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}}, {JS(account), meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}},
@@ -297,24 +297,11 @@ tag_invoke(boost::json::value_to_tag<AMMInfoHandler::Input>, boost::json::value
} }
} }
auto getIssue = [](boost::json::value const& request) {
if (request.is_string())
return ripple::issueFromJson(request.as_string().c_str());
// Note: no checks needed as we already validated the input if we made it here
auto const currency = ripple::to_currency(request.at(JS(currency)).as_string().c_str());
if (ripple::isXRP(currency)) {
return ripple::xrpIssue();
}
auto const issuer = ripple::parseBase58<ripple::AccountID>(request.at(JS(issuer)).as_string().c_str());
return ripple::Issue{currency, *issuer};
};
if (jsonObject.contains(JS(asset))) if (jsonObject.contains(JS(asset)))
input.issue1 = getIssue(jsonObject.at(JS(asset))); input.issue1 = parseIssue(jsonObject.at(JS(asset)).as_object());
if (jsonObject.contains(JS(asset2))) if (jsonObject.contains(JS(asset2)))
input.issue2 = getIssue(jsonObject.at(JS(asset2))); input.issue2 = parseIssue(jsonObject.at(JS(asset2)).as_object());
if (jsonObject.contains(JS(account))) if (jsonObject.contains(JS(account)))
input.accountID = accountFromStringStrict(jsonObject.at(JS(account)).as_string().c_str()); input.accountID = accountFromStringStrict(jsonObject.at(JS(account)).as_string().c_str());

View File

@@ -30,13 +30,16 @@
#include <boost/json/value_to.hpp> #include <boost/json/value_to.hpp>
#include <ripple/basics/base_uint.h> #include <ripple/basics/base_uint.h>
#include <ripple/basics/strHex.h> #include <ripple/basics/strHex.h>
#include <ripple/json/json_value.h>
#include <ripple/protocol/AccountID.h> #include <ripple/protocol/AccountID.h>
#include <ripple/protocol/ErrorCodes.h> #include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/Indexes.h> #include <ripple/protocol/Indexes.h>
#include <ripple/protocol/Issue.h> #include <ripple/protocol/Issue.h>
#include <ripple/protocol/LedgerFormats.h> #include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/LedgerHeader.h> #include <ripple/protocol/LedgerHeader.h>
#include <ripple/protocol/SField.h>
#include <ripple/protocol/STLedgerEntry.h> #include <ripple/protocol/STLedgerEntry.h>
#include <ripple/protocol/STXChainBridge.h>
#include <ripple/protocol/Serializer.h> #include <ripple/protocol/Serializer.h>
#include <ripple/protocol/UintTypes.h> #include <ripple/protocol/UintTypes.h>
#include <ripple/protocol/jss.h> #include <ripple/protocol/jss.h>
@@ -111,6 +114,24 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
getIssuerFromJson(input.amm->at(JS(asset))), getIssuerFromJson(input.amm->at(JS(asset2))) getIssuerFromJson(input.amm->at(JS(asset))), getIssuerFromJson(input.amm->at(JS(asset2)))
) )
.key; .key;
} else if (input.bridge) {
if (!input.bridgeAccount && !input.chainClaimId && !input.createAccountClaimId)
return Error{Status{ClioError::rpcMALFORMED_REQUEST}};
if (input.bridgeAccount) {
auto const bridgeAccount = ripple::parseBase58<ripple::AccountID>(*(input.bridgeAccount));
auto const chainType = ripple::STXChainBridge::srcChain(bridgeAccount == input.bridge->lockingChainDoor());
if (bridgeAccount != input.bridge->door(chainType))
return Error{Status{ClioError::rpcMALFORMED_REQUEST}};
key = ripple::keylet::bridge(input.bridge->value(), chainType).key;
} else if (input.chainClaimId) {
key = ripple::keylet::xChainClaimID(input.bridge->value(), input.chainClaimId.value()).key;
} else {
key = ripple::keylet::xChainCreateAccountClaimID(input.bridge->value(), input.createAccountClaimId.value())
.key;
}
} else { } else {
// Must specify 1 of the following fields to indicate what type // Must specify 1 of the following fields to indicate what type
if (ctx.apiVersion == 1) if (ctx.apiVersion == 1)
@@ -225,7 +246,24 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
{JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH}, {JS(deposit_preauth), ripple::ltDEPOSIT_PREAUTH},
{JS(ticket), ripple::ltTICKET}, {JS(ticket), ripple::ltTICKET},
{JS(nft_page), ripple::ltNFTOKEN_PAGE}, {JS(nft_page), ripple::ltNFTOKEN_PAGE},
{JS(amm), ripple::ltAMM} {JS(amm), ripple::ltAMM},
{JS(xchain_owned_create_account_claim_id), ripple::ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID},
{JS(xchain_owned_claim_id), ripple::ltXCHAIN_OWNED_CLAIM_ID},
};
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
auto const lockingDoor = *ripple::parseBase58<ripple::AccountID>(
bridgeJson.at(ripple::sfLockingChainDoor.getJsonName().c_str()).as_string().c_str()
);
auto const issuingDoor = *ripple::parseBase58<ripple::AccountID>(
bridgeJson.at(ripple::sfIssuingChainDoor.getJsonName().c_str()).as_string().c_str()
);
auto const lockingIssue =
parseIssue(bridgeJson.at(ripple::sfLockingChainIssue.getJsonName().c_str()).as_object());
auto const issuingIssue =
parseIssue(bridgeJson.at(ripple::sfIssuingChainIssue.getJsonName().c_str()).as_object());
return ripple::STXChainBridge{lockingDoor, lockingIssue, issuingDoor, issuingIssue};
}; };
auto const indexFieldType = auto const indexFieldType =
@@ -259,6 +297,19 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
input.ticket = jv.at(JS(ticket)).as_object(); input.ticket = jv.at(JS(ticket)).as_object();
} else if (jsonObject.contains(JS(amm))) { } else if (jsonObject.contains(JS(amm))) {
input.amm = jv.at(JS(amm)).as_object(); input.amm = jv.at(JS(amm)).as_object();
} else if (jsonObject.contains(JS(bridge))) {
input.bridge.emplace(parseBridgeFromJson(jv.at(JS(bridge))));
if (jsonObject.contains(JS(bridge_account)))
input.bridgeAccount = jv.at(JS(bridge_account)).as_string().c_str();
} else if (jsonObject.contains(JS(xchain_owned_claim_id))) {
input.bridge.emplace(parseBridgeFromJson(jv.at(JS(xchain_owned_claim_id))));
input.chainClaimId =
boost::json::value_to<std::int32_t>(jv.at(JS(xchain_owned_claim_id)).at(JS(xchain_owned_claim_id)));
} else if (jsonObject.contains(JS(xchain_owned_create_account_claim_id))) {
input.bridge.emplace(parseBridgeFromJson(jv.at(JS(xchain_owned_create_account_claim_id))));
input.createAccountClaimId = boost::json::value_to<std::int32_t>(
jv.at(JS(xchain_owned_create_account_claim_id)).at(JS(xchain_owned_create_account_claim_id))
);
} }
return input; return input;

View File

@@ -20,11 +20,31 @@
#pragma once #pragma once
#include "data/BackendInterface.h" #include "data/BackendInterface.h"
#include "rpc/RPCHelpers.h" #include "rpc/Errors.h"
#include "rpc/JS.h"
#include "rpc/common/MetaProcessors.h" #include "rpc/common/MetaProcessors.h"
#include "rpc/common/Types.h" #include "rpc/common/Types.h"
#include "rpc/common/Validators.h" #include "rpc/common/Validators.h"
#include <boost/json/conversion.hpp>
#include <boost/json/object.hpp>
#include <boost/json/value.hpp>
#include <ripple/basics/base_uint.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/Issue.h>
#include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/SField.h>
#include <ripple/protocol/jss.h>
#include <ripple/protocol/tokens.h>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <variant>
namespace rpc { namespace rpc {
/** /**
@@ -66,6 +86,10 @@ public:
std::optional<boost::json::object> depositPreauth; std::optional<boost::json::object> depositPreauth;
std::optional<boost::json::object> ticket; std::optional<boost::json::object> ticket;
std::optional<boost::json::object> amm; std::optional<boost::json::object> amm;
std::optional<ripple::STXChainBridge> bridge;
std::optional<std::string> bridgeAccount;
std::optional<uint32_t> chainClaimId;
std::optional<uint32_t> createAccountClaimId;
}; };
using Result = HandlerReturnType<Output>; using Result = HandlerReturnType<Output>;
@@ -103,6 +127,24 @@ public:
static auto const malformedRequestIntValidator = static auto const malformedRequestIntValidator =
meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::rpcMALFORMED_REQUEST)}; meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::rpcMALFORMED_REQUEST)};
static auto const bridgeJsonValidator = meta::WithCustomError{
meta::IfType<boost::json::object>{meta::Section{
{ripple::sfLockingChainDoor.getJsonName().c_str(),
validation::Required{},
validation::AccountBase58Validator},
{ripple::sfIssuingChainDoor.getJsonName().c_str(),
validation::Required{},
validation::AccountBase58Validator},
{ripple::sfLockingChainIssue.getJsonName().c_str(),
validation::Required{},
validation::CurrencyIssueValidator},
{ripple::sfIssuingChainIssue.getJsonName().c_str(),
validation::Required{},
validation::CurrencyIssueValidator},
}},
Status(ClioError::rpcMALFORMED_REQUEST)
};
static auto const rpcSpec = RpcSpec{ static auto const rpcSpec = RpcSpec{
{JS(binary), validation::Type<bool>{}}, {JS(binary), validation::Type<bool>{}},
{JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_hash), validation::Uint256HexStringValidator},
@@ -177,15 +219,44 @@ public:
meta::WithCustomError{ meta::WithCustomError{
validation::Type<boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST) validation::Type<boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST)
}, },
validation::AMMAssetValidator}, validation::CurrencyIssueValidator},
{JS(asset2), {JS(asset2),
meta::WithCustomError{validation::Required{}, Status(ClioError::rpcMALFORMED_REQUEST)}, meta::WithCustomError{validation::Required{}, Status(ClioError::rpcMALFORMED_REQUEST)},
meta::WithCustomError{ meta::WithCustomError{
validation::Type<boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST) validation::Type<boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST)
}, },
validation::AMMAssetValidator}, validation::CurrencyIssueValidator},
}, },
}} }},
{JS(bridge),
meta::WithCustomError{validation::Type<boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST)},
bridgeJsonValidator},
{JS(bridge_account),
meta::WithCustomError{validation::AccountBase58Validator, Status(ClioError::rpcMALFORMED_REQUEST)}},
{JS(xchain_owned_claim_id),
meta::WithCustomError{
validation::Type<std::string, boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST)
},
meta::IfType<std::string>{malformedRequestHexStringValidator},
bridgeJsonValidator,
meta::WithCustomError{
meta::IfType<boost::json::object>{
meta::Section{{JS(xchain_owned_claim_id), validation::Required{}, validation::Type<uint32_t>{}}}
},
Status(ClioError::rpcMALFORMED_REQUEST)
}},
{JS(xchain_owned_create_account_claim_id),
meta::WithCustomError{
validation::Type<std::string, boost::json::object>{}, Status(ClioError::rpcMALFORMED_REQUEST)
},
meta::IfType<std::string>{malformedRequestHexStringValidator},
bridgeJsonValidator,
meta::WithCustomError{
meta::IfType<boost::json::object>{meta::Section{
{JS(xchain_owned_create_account_claim_id), validation::Required{}, validation::Type<uint32_t>{}}
}},
Status(ClioError::rpcMALFORMED_REQUEST)
}},
}; };
return rpcSpec; return rpcSpec;

View File

@@ -36,11 +36,13 @@
#include <ripple/protocol/Indexes.h> #include <ripple/protocol/Indexes.h>
#include <ripple/protocol/SField.h> #include <ripple/protocol/SField.h>
#include <ripple/protocol/STObject.h> #include <ripple/protocol/STObject.h>
#include <ripple/protocol/UintTypes.h>
#include <ripple/protocol/jss.h> #include <ripple/protocol/jss.h>
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <stdexcept>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <variant> #include <variant>
@@ -502,3 +504,38 @@ TEST_F(RPCHelpersTest, TransactionAndMetadataBinaryJsonV2)
EXPECT_TRUE(json.contains(JS(tx_blob))); EXPECT_TRUE(json.contains(JS(tx_blob)));
EXPECT_TRUE(json.contains(JS(meta_blob))); EXPECT_TRUE(json.contains(JS(meta_blob)));
} }
TEST_F(RPCHelpersTest, ParseIssue)
{
auto issue = parseIssue(boost::json::parse(
R"({
"issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
"currency": "JPY"
})"
)
.as_object());
EXPECT_TRUE(issue.account == GetAccountIDWithString(ACCOUNT2));
issue = parseIssue(boost::json::parse(R"({"currency": "XRP"})").as_object());
EXPECT_TRUE(ripple::isXRP(issue.currency));
EXPECT_THROW(parseIssue(boost::json::parse(R"({"currency": 2})").as_object()), std::runtime_error);
EXPECT_THROW(parseIssue(boost::json::parse(R"({"currency": "XRP2"})").as_object()), std::runtime_error);
EXPECT_THROW(
parseIssue(boost::json::parse(
R"({
"issuer": "abcd",
"currency": "JPY"
})"
)
.as_object()),
std::runtime_error
);
EXPECT_THROW(
parseIssue(boost::json::parse(R"({"issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"})").as_object()),
std::runtime_error
);
}

View File

@@ -33,8 +33,10 @@
#include <ripple/basics/strHex.h> #include <ripple/basics/strHex.h>
#include <ripple/protocol/AccountID.h> #include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Indexes.h> #include <ripple/protocol/Indexes.h>
#include <ripple/protocol/Issue.h>
#include <ripple/protocol/LedgerFormats.h> #include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/STObject.h> #include <ripple/protocol/STObject.h>
#include <ripple/protocol/STXChainBridge.h>
#include <ripple/protocol/UintTypes.h> #include <ripple/protocol/UintTypes.h>
#include <optional> #include <optional>
@@ -49,6 +51,7 @@ using namespace testing;
constexpr static auto INDEX1 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD"; constexpr static auto INDEX1 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD";
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"; constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"; constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
constexpr static auto ACCOUNT3 = "rhzcyub9SbyZ4YF1JYskN5rLrTDUuLZG6D";
constexpr static auto RANGEMIN = 10; constexpr static auto RANGEMIN = 10;
constexpr static auto RANGEMAX = 30; constexpr static auto RANGEMAX = 30;
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652"; constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
@@ -881,6 +884,668 @@ generateTestValuesForParametersTest()
"malformedRequest", "malformedRequest",
"Malformed request." "Malformed request."
}, },
ParamTestCaseBundle{
"BridgeMissingBridgeAccount",
fmt::format(
R"({{
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeCurrencyIsNumber",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": {},
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT,
1,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeIssuerIsNumber",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": {}
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT,
"JPY",
2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeIssuingChainIssueIsNotObject",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue": 1
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeWithInvalidBridgeAccount",
fmt::format(
R"({{
"bridge_account": "abcd",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeDoorInvalid",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "abcd",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeIssuerInvalid",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "invalid"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT,
"JPY"
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeIssueCurrencyInvalid",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "JPJPJP",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT2,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeIssueXRPCurrencyInvalid",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP",
"issuer": "{}"
}},
"IssuingChainIssue":
{{
"currency": "JPY",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT2,
ACCOUNT2,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeIssueJPYCurrencyInvalid",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "JPY"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeMissingLockingChainDoor",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP",
"issuer": "{}"
}},
"IssuingChainIssue":
{{
"currency": "JPY",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT2,
ACCOUNT2,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeMissingIssuingChainDoor",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"LockingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "JPY",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeMissingLockingChainIssue",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"IssuingChainDoor": "{}",
"LockingChainDoor": "{}",
"IssuingChainIssue":
{{
"currency": "JPY",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeMissingIssuingChainIssue",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge":
{{
"IssuingChainDoor": "{}",
"LockingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "JPY",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT,
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"BridgeInvalidType",
fmt::format(
R"({{
"bridge_account": "{}",
"bridge": "invalid"
}})",
ACCOUNT
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedClaimIdInvalidType",
R"({
"xchain_owned_claim_id": 123
})",
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedClaimIdJsonMissingClaimId",
fmt::format(
R"({{
"xchain_owned_claim_id":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedClaimIdJsonMissingDoor",
fmt::format(
R"({{
"xchain_owned_claim_id":
{{
"xchain_owned_claim_id": 10,
"LockingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedClaimIdJsonMissingIssue",
fmt::format(
R"({{
"xchain_owned_claim_id":
{{
"xchain_owned_claim_id": 10,
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}}
}}
}})",
ACCOUNT,
ACCOUNT
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedClaimIdJsonInvalidDoor",
fmt::format(
R"({{
"xchain_owned_claim_id":
{{
"xchain_owned_claim_id": 10,
"LockingChainDoor": "abcd",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedClaimIdJsonInvalidIssue",
fmt::format(
R"({{
"xchain_owned_claim_id":
{{
"xchain_owned_claim_id": 10,
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY"
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedCreateAccountClaimIdInvalidType",
R"({
"xchain_owned_create_account_claim_id": 123
})",
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedCreateAccountClaimIdJsonMissingClaimId",
fmt::format(
R"({{
"xchain_owned_create_account_claim_id":
{{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedCreateAccountClaimIdJsonMissingDoor",
fmt::format(
R"({{
"xchain_owned_create_account_claim_id":
{{
"xchain_owned_create_account_claim_id": 10,
"LockingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedCreateAccountClaimIdJsonMissingIssue",
fmt::format(
R"({{
"xchain_owned_create_account_claim_id":
{{
"xchain_owned_create_account_claim_id": 10,
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}}
}}
}})",
ACCOUNT,
ACCOUNT
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedCreateAccountClaimIdJsonInvalidDoor",
fmt::format(
R"({{
"xchain_owned_create_account_claim_id":
{{
"xchain_owned_create_account_claim_id": 10,
"LockingChainDoor": "abcd",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}",
"issuer": "{}"
}}
}}
}})",
ACCOUNT,
"JPY",
ACCOUNT2
),
"malformedRequest",
"Malformed request."
},
ParamTestCaseBundle{
"OwnedCreateAccountClaimIdJsonInvalidIssue",
fmt::format(
R"({{
"xchain_owned_create_account_claim_id":
{{
"xchain_owned_create_account_claim_id": 10,
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue":
{{
"currency": "XRP"
}},
"IssuingChainIssue":
{{
"currency": "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
"JPY"
),
"malformedRequest",
"Malformed request."
},
}; };
} }
@@ -1314,7 +1979,145 @@ generateTestValuesForNormalPathTest()
), ),
ripple::keylet::amm(GetIssue("XRP", ripple::toBase58(ripple::xrpAccount())), GetIssue("JPY", ACCOUNT2)).key, ripple::keylet::amm(GetIssue("XRP", ripple::toBase58(ripple::xrpAccount())), GetIssue("JPY", ACCOUNT2)).key,
CreateAMMObject(ACCOUNT, "XRP", ripple::toBase58(ripple::xrpAccount()), "JPY", ACCOUNT2) CreateAMMObject(ACCOUNT, "XRP", ripple::toBase58(ripple::xrpAccount()), "JPY", ACCOUNT2)
} },
NormalPathTestBundle{
"BridgeLocking",
fmt::format(
R"({{
"binary": true,
"bridge_account": "{}",
"bridge": {{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue": {{
"currency" : "XRP"
}},
"IssuingChainIssue": {{
"currency" : "JPY",
"issuer" : "{}"
}}
}}
}})",
ACCOUNT,
ACCOUNT,
ACCOUNT2,
ACCOUNT3
),
ripple::keylet::bridge(
ripple::STXChainBridge(
GetAccountIDWithString(ACCOUNT),
ripple::xrpIssue(),
GetAccountIDWithString(ACCOUNT2),
GetIssue("JPY", ACCOUNT3)
),
ripple::STXChainBridge::ChainType::locking
)
.key,
CreateBridgeObject(ACCOUNT, ACCOUNT, ACCOUNT2, "JPY", ACCOUNT3)
},
NormalPathTestBundle{
"BridgeIssuing",
fmt::format(
R"({{
"binary": true,
"bridge_account": "{}",
"bridge": {{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue": {{
"currency" : "XRP"
}},
"IssuingChainIssue": {{
"currency" : "JPY",
"issuer" : "{}"
}}
}}
}})",
ACCOUNT2,
ACCOUNT,
ACCOUNT2,
ACCOUNT3
),
ripple::keylet::bridge(
ripple::STXChainBridge(
GetAccountIDWithString(ACCOUNT),
ripple::xrpIssue(),
GetAccountIDWithString(ACCOUNT2),
GetIssue("JPY", ACCOUNT3)
),
ripple::STXChainBridge::ChainType::issuing
)
.key,
CreateBridgeObject(ACCOUNT, ACCOUNT, ACCOUNT2, "JPY", ACCOUNT3)
},
NormalPathTestBundle{
"XChainOwnedClaimId",
fmt::format(
R"({{
"binary": true,
"xchain_owned_claim_id": {{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue": {{
"currency" : "XRP"
}},
"IssuingChainIssue": {{
"currency" : "JPY",
"issuer" : "{}"
}},
"xchain_owned_claim_id": 10
}}
}})",
ACCOUNT,
ACCOUNT2,
ACCOUNT3
),
ripple::keylet::xChainClaimID(
ripple::STXChainBridge(
GetAccountIDWithString(ACCOUNT),
ripple::xrpIssue(),
GetAccountIDWithString(ACCOUNT2),
GetIssue("JPY", ACCOUNT3)
),
10
)
.key,
CreateChainOwnedClaimIDObject(ACCOUNT, ACCOUNT, ACCOUNT2, "JPY", ACCOUNT3, ACCOUNT)
},
NormalPathTestBundle{
"XChainOwnedCreateAccountClaimId",
fmt::format(
R"({{
"binary": true,
"xchain_owned_create_account_claim_id": {{
"LockingChainDoor": "{}",
"IssuingChainDoor": "{}",
"LockingChainIssue": {{
"currency" : "XRP"
}},
"IssuingChainIssue": {{
"currency" : "JPY",
"issuer" : "{}"
}},
"xchain_owned_create_account_claim_id": 10
}}
}})",
ACCOUNT,
ACCOUNT2,
ACCOUNT3
),
ripple::keylet::xChainCreateAccountClaimID(
ripple::STXChainBridge(
GetAccountIDWithString(ACCOUNT),
ripple::xrpIssue(),
GetAccountIDWithString(ACCOUNT2),
GetIssue("JPY", ACCOUNT3)
),
10
)
.key,
CreateChainOwnedClaimIDObject(ACCOUNT, ACCOUNT, ACCOUNT2, "JPY", ACCOUNT3, ACCOUNT)
},
}; };
} }

View File

@@ -27,6 +27,7 @@
#include <ripple/basics/Slice.h> #include <ripple/basics/Slice.h>
#include <ripple/basics/base_uint.h> #include <ripple/basics/base_uint.h>
#include <ripple/basics/chrono.h> #include <ripple/basics/chrono.h>
#include <ripple/json/json_value.h>
#include <ripple/protocol/AMMCore.h> #include <ripple/protocol/AMMCore.h>
#include <ripple/protocol/AccountID.h> #include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Indexes.h> #include <ripple/protocol/Indexes.h>
@@ -838,6 +839,113 @@ CreateAMMObject(
return amm; return amm;
} }
ripple::STObject
CreateBridgeObject(
std::string_view accountId,
std::string_view lockingDoor,
std::string_view issuingDoor,
std::string_view issuingCurrency,
std::string_view issuingIssuer
)
{
auto bridge = ripple::STObject(ripple::sfLedgerEntry);
bridge.setFieldU16(ripple::sfLedgerEntryType, ripple::ltBRIDGE);
bridge.setAccountID(ripple::sfAccount, GetAccountIDWithString(accountId));
bridge.setFieldAmount(ripple::sfSignatureReward, ripple::STAmount(10, false));
bridge.setFieldU64(ripple::sfXChainClaimID, 100);
bridge.setFieldU64(ripple::sfXChainAccountCreateCount, 100);
bridge.setFieldU64(ripple::sfXChainAccountClaimCount, 100);
bridge.setFieldU64(ripple::sfOwnerNode, 100);
bridge.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{});
bridge.setFieldU32(ripple::sfPreviousTxnLgrSeq, 0);
bridge.setFieldU32(ripple::sfFlags, 0);
Json::Value lockingIssue;
lockingIssue["currency"] = "XRP";
Json::Value issuingIssue;
issuingIssue["currency"] = std::string(issuingCurrency);
issuingIssue["issuer"] = std::string(issuingIssuer);
bridge[ripple::sfXChainBridge] = ripple::STXChainBridge(
GetAccountIDWithString(lockingDoor),
ripple::issueFromJson(lockingIssue),
GetAccountIDWithString(issuingDoor),
ripple::issueFromJson(issuingIssue)
);
bridge.setFieldU32(ripple::sfFlags, 0);
return bridge;
}
ripple::STObject
CreateChainOwnedClaimIDObject(
std::string_view accountId,
std::string_view lockingDoor,
std::string_view issuingDoor,
std::string_view issuingCurrency,
std::string_view issuingIssuer,
std::string_view otherChainSource
)
{
auto chainOwnedClaimID = ripple::STObject(ripple::sfLedgerEntry);
chainOwnedClaimID.setFieldU16(ripple::sfLedgerEntryType, ripple::ltXCHAIN_OWNED_CLAIM_ID);
chainOwnedClaimID.setAccountID(ripple::sfAccount, GetAccountIDWithString(accountId));
chainOwnedClaimID.setFieldAmount(ripple::sfSignatureReward, ripple::STAmount(10, false));
chainOwnedClaimID.setFieldU64(ripple::sfXChainClaimID, 100);
chainOwnedClaimID.setFieldU64(ripple::sfOwnerNode, 100);
chainOwnedClaimID.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{});
chainOwnedClaimID.setFieldU32(ripple::sfPreviousTxnLgrSeq, 0);
chainOwnedClaimID.setFieldU32(ripple::sfFlags, 0);
Json::Value lockingIssue;
lockingIssue["currency"] = "XRP";
Json::Value issuingIssue;
issuingIssue["currency"] = std::string(issuingCurrency);
issuingIssue["issuer"] = std::string(issuingIssuer);
chainOwnedClaimID[ripple::sfXChainBridge] = ripple::STXChainBridge(
GetAccountIDWithString(lockingDoor),
ripple::issueFromJson(lockingIssue),
GetAccountIDWithString(issuingDoor),
ripple::issueFromJson(issuingIssue)
);
chainOwnedClaimID.setFieldU32(ripple::sfFlags, 0);
chainOwnedClaimID.setAccountID(ripple::sfOtherChainSource, GetAccountIDWithString(otherChainSource));
chainOwnedClaimID.setFieldArray(ripple::sfXChainClaimAttestations, ripple::STArray{});
return chainOwnedClaimID;
}
ripple::STObject
CreateChainOwnedCreateAccountClaimID(
std::string_view accountId,
std::string_view lockingDoor,
std::string_view issuingDoor,
std::string_view issuingCurrency,
std::string_view issuingIssuer
)
{
auto chainOwnedCreateAccountClaimID = ripple::STObject(ripple::sfLedgerEntry);
chainOwnedCreateAccountClaimID.setFieldU16(ripple::sfLedgerEntryType, ripple::ltXCHAIN_OWNED_CLAIM_ID);
chainOwnedCreateAccountClaimID.setAccountID(ripple::sfAccount, GetAccountIDWithString(accountId));
chainOwnedCreateAccountClaimID.setFieldU64(ripple::sfXChainAccountCreateCount, 100);
chainOwnedCreateAccountClaimID.setFieldU64(ripple::sfOwnerNode, 100);
chainOwnedCreateAccountClaimID.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{});
chainOwnedCreateAccountClaimID.setFieldU32(ripple::sfPreviousTxnLgrSeq, 0);
chainOwnedCreateAccountClaimID.setFieldU32(ripple::sfFlags, 0);
Json::Value lockingIssue;
lockingIssue["currency"] = "XRP";
Json::Value issuingIssue;
issuingIssue["currency"] = std::string(issuingCurrency);
issuingIssue["issuer"] = std::string(issuingIssuer);
chainOwnedCreateAccountClaimID[ripple::sfXChainBridge] = ripple::STXChainBridge(
GetAccountIDWithString(lockingDoor),
ripple::issueFromJson(lockingIssue),
GetAccountIDWithString(issuingDoor),
ripple::issueFromJson(issuingIssue)
);
chainOwnedCreateAccountClaimID.setFieldU32(ripple::sfFlags, 0);
chainOwnedCreateAccountClaimID.setFieldArray(ripple::sfXChainCreateAccountAttestations, ripple::STArray{});
return chainOwnedCreateAccountClaimID;
}
void void
AMMAddVoteSlot(ripple::STObject& amm, ripple::AccountID const& accountId, uint16_t tradingFee, uint32_t voteWeight) AMMAddVoteSlot(ripple::STObject& amm, ripple::AccountID const& accountId, uint16_t tradingFee, uint32_t voteWeight)
{ {

View File

@@ -303,6 +303,34 @@ CreateAMMObject(
uint64_t ownerNode = 0u uint64_t ownerNode = 0u
); );
[[nodiscard]] ripple::STObject
CreateBridgeObject(
std::string_view accountId,
std::string_view lockingDoor,
std::string_view issuingDoor,
std::string_view issuingCurrency,
std::string_view issuingIssuer
);
[[nodiscard]] ripple::STObject
CreateChainOwnedClaimIDObject(
std::string_view accountId,
std::string_view lockingDoor,
std::string_view issuingDoor,
std::string_view issuingCurrency,
std::string_view issuingIssuer,
std::string_view otherChainSource
);
[[nodiscard]] ripple::STObject
CreateChainOwnedCreateAccountClaimID(
std::string_view accountId,
std::string_view lockingDoor,
std::string_view issuingDoor,
std::string_view issuingCurrency,
std::string_view issuingIssuer
);
void void
AMMAddVoteSlot(ripple::STObject& amm, ripple::AccountID const& accountId, uint16_t tradingFee, uint32_t voteWeight); AMMAddVoteSlot(ripple::STObject& amm, ripple::AccountID const& accountId, uint16_t tradingFee, uint32_t voteWeight);