Files
xahaud/src/test/app/Import_test.cpp
RichardAH b4022c35b3 make account starting seq the parent close time to prevent replay attacks in reset networks (#131)
* make account starting seq the parent close time to prevent replay attacks in reset networks

* add tests for activation

---------

Co-authored-by: Denis Angell <dangell@transia.co>
2023-10-09 14:18:23 +02:00

5581 lines
238 KiB
C++

//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 XRPL Labs
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/app/ledger/LedgerMaster.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/app/tx/impl/Import.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/json/json_reader.h>
#include <ripple/json/json_writer.h>
#include <ripple/ledger/Directory.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/Import.h>
#include <ripple/protocol/jss.h>
#include <test/app/Import_json.h>
#include <test/jtx.h>
#define BEAST_REQUIRE(x) \
{ \
BEAST_EXPECT(!!(x)); \
if (!(x)) \
return; \
}
#define M(m) memo(m, "", "")
namespace ripple {
namespace test {
class Import_test : public beast::unit_test::suite
{
std::vector<std::string> const keys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1"};
std::unique_ptr<Config>
makeNetworkConfig(uint32_t networkID)
{
using namespace jtx;
return envconfig([&](std::unique_ptr<Config> cfg) {
cfg->NETWORK_ID = networkID;
Section config;
config.append(
{"reference_fee = 10",
"account_reserve = 1000000",
"owner_reserve = 200000"});
auto setup = setup_FeeVote(config);
cfg->FEES = setup;
return cfg;
});
}
std::unique_ptr<Config>
makeNetworkVLConfig(uint32_t networkID, std::vector<std::string> keys)
{
using namespace jtx;
return envconfig([&](std::unique_ptr<Config> cfg) {
cfg->NETWORK_ID = networkID;
Section config;
config.append(
{"reference_fee = 10",
"account_reserve = 1000000",
"owner_reserve = 200000"});
auto setup = setup_FeeVote(config);
cfg->FEES = setup;
for (auto const& strPk : keys)
{
auto pkHex = strUnHex(strPk);
if (!pkHex)
Throw<std::runtime_error>(
"Import VL Key '" + strPk + "' was not valid hex.");
auto const pkType = publicKeyType(makeSlice(*pkHex));
if (!pkType)
Throw<std::runtime_error>(
"Import VL Key '" + strPk +
"' was not a valid key type.");
cfg->IMPORT_VL_KEYS.emplace(strPk, makeSlice(*pkHex));
}
return cfg;
});
}
std::unique_ptr<Config>
makeMaxFeeConfig(uint32_t networkID, std::vector<std::string> keys)
{
using namespace jtx;
return envconfig([&](std::unique_ptr<Config> cfg) {
cfg->NETWORK_ID = networkID;
Section config;
config.append(
{"reference_fee = 50",
"account_reserve = 10000000",
"owner_reserve = 2000000"});
auto setup = setup_FeeVote(config);
cfg->FEES = setup;
for (auto const& strPk : keys)
{
auto pkHex = strUnHex(strPk);
if (!pkHex)
Throw<std::runtime_error>(
"Import VL Key '" + strPk + "' was not valid hex.");
auto const pkType = publicKeyType(makeSlice(*pkHex));
if (!pkType)
Throw<std::runtime_error>(
"Import VL Key '" + strPk +
"' was not a valid key type.");
cfg->IMPORT_VL_KEYS.emplace(strPk, makeSlice(*pkHex));
}
return cfg;
});
}
static Json::Value
import(jtx::Account const& account, Json::Value const& xpop)
{
using namespace jtx;
Json::Value jv;
std::string strJson = Json::FastWriter().write(xpop);
jv[jss::TransactionType] = jss::Import;
jv[jss::Account] = account.human();
jv[sfBlob.jsonName] = strHex(strJson);
return jv;
}
static std::pair<uint256, std::shared_ptr<SLE const>>
accountKeyAndSle(ReadView const& view, jtx::Account const& account)
{
auto const k = keylet::account(account);
return {k.key, view.read(k)};
}
static std::pair<uint256, std::shared_ptr<SLE const>>
feesKeyAndSle(ReadView const& view)
{
auto const k = keylet::fees();
return {k.key, view.read(k)};
}
static std::pair<uint256, std::shared_ptr<SLE const>>
signersKeyAndSle(ReadView const& view, jtx::Account const& account)
{
auto const k = keylet::signers(account);
return {k.key, view.read(k)};
}
static std::size_t
ownerDirCount(ReadView const& view, jtx::Account const& acct)
{
ripple::Dir const ownerDir(view, keylet::ownerDir(acct.id()));
return std::distance(ownerDir.begin(), ownerDir.end());
};
static Json::Value
loadXpop(std::string content)
{
// If the string is empty, return an empty Json::Value
if (content.empty())
{
std::cout << "JSON string was empty"
<< "\n";
return {};
}
Json::Value jsonValue;
Json::Reader reader;
reader.parse(content, jsonValue);
return jsonValue;
}
static std::uint32_t
importVLSequence(jtx::Env const& env, PublicKey const& pk)
{
auto const sle = env.le(keylet::import_vlseq(pk));
if (sle->isFieldPresent(sfImportSequence))
return (*sle)[sfImportSequence];
return 0;
}
STTx
createUNLReportTx(
LedgerIndex seq,
PublicKey const& importKey,
PublicKey const& valKey)
{
auto fill = [&](auto& obj) {
obj.setFieldU32(sfLedgerSequence, seq);
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfActiveValidator);
inner->setFieldVL(sfPublicKey, valKey);
return inner;
})());
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfImportVLKey);
inner->setFieldVL(sfPublicKey, importKey);
return inner;
})());
};
return STTx(ttUNL_REPORT, fill);
}
bool
hasUNLReport(jtx::Env const& env)
{
auto const slep = env.le(keylet::UNLReport());
return slep != nullptr;
}
void
incLgrSeqForAccDel(
jtx::Env& env,
jtx::Account const& acc,
std::uint32_t margin = 0)
{
int const delta = [&]() -> int {
if (env.seq(acc) + 255 > env.current()->seq())
return env.seq(acc) - env.current()->seq() + 255 - margin;
return 0;
}();
BEAST_EXPECT(margin == 0 || delta >= 0);
for (int i = 0; i < delta; ++i)
env.close();
BEAST_EXPECT(env.current()->seq() == env.seq(acc) + 255 - margin);
}
void
testComputeStartingBalance(FeatureBitset features)
{
testcase("import header - computeStartingBonus");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// old fee
XRPAmount const value = Import::computeStartingBonus(*env.current());
BEAST_EXPECT(value == drops(2000000));
// todo: new fee
}
void
testIsHex(FeatureBitset features)
{
testcase("import utils - isHex");
bool const result = isHex("DEADBEEF");
BEAST_EXPECT(result == true);
bool const result1 = isHex("Hello world");
BEAST_EXPECT(result1 == false);
}
void
testIsBase58(FeatureBitset features)
{
testcase("import utils - isBase58");
bool const result = isBase58("hE45rTtDcGvFz");
BEAST_EXPECT(result == true);
bool const result1 = isBase58("Hello world");
BEAST_EXPECT(result1 == false);
}
void
testIsBase64(FeatureBitset features)
{
testcase("import utils - isBase64");
bool const result = isBase64("SGVsbG8gV29ybGQh");
BEAST_EXPECT(result == true);
bool const result1 = isBase64("Hello world");
BEAST_EXPECT(result1 == false);
}
void
testParseUint64(FeatureBitset features)
{
testcase("import utils - parseUint64");
std::optional<uint64_t> result1 = parse_uint64("1234");
BEAST_EXPECT(result1 == 1234);
// std::optional<uint64_t> result2 = parse_uint64("0xFFAABBCCDD22");
// std::cout << "RESULT: " << *result2 << "\n";
// BEAST_EXPECT(result2 == 0xFFAABBCCDD22);
std::optional<uint64_t> result3 = parse_uint64("2147483647");
BEAST_EXPECT(result3 == 2147483647);
std::optional<uint64_t> result4 = parse_uint64("18446744073709551615");
BEAST_EXPECT(result4 == 18446744073709551615ull);
}
void
testSyntaxCheckProofArray(FeatureBitset features)
{
}
void
testSyntaxCheckProofObject(FeatureBitset features)
{
testcase("import utils - syntaxCheckProof: is object");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
std::string strJson = R"json({
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
})json";
Json::Value proof;
Json::Reader reader;
reader.parse(strJson, proof);
// XPOP.transaction.proof list entry has wrong format
{
BEAST_EXPECT(syntaxCheckProof(proof, env.journal, 65) == false);
}
// XPOP.transaction.proof invalid branch size
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["30"] = tmpProof[jss::children]["3"];
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof child node was not 0-F
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["not a hex"] = tmpProof[jss::children]["3"];
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof tree node has wrong format
// invalid child (must be object)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"] = "not object";
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof tree node has wrong format
// invalid hash (must be string)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"][jss::hash] = 1234;
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof tree node has wrong format
// invalid hash size (must be 64)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"][jss::hash] =
"00000000000000000000000000000000000000000000000000000000000000"
"000";
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof tree node has wrong format
// invalid key (must be string)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"][jss::key] = 1234;
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof tree node has wrong format
// invalid key size (must be 64)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"][jss::key] =
"00000000000000000000000000000000000000000000000000000000000000"
"000";
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof tree node has wrong format
// invalid node children (must be object)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"][jss::children] = "bad object";
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// XPOP.transaction.proof bad children format
// invalid node child children (must be hex)
{
Json::Value tmpProof = proof;
tmpProof[jss::children]["3"][jss::children] =
tmpProof[jss::children]["3"];
BEAST_EXPECT(syntaxCheckProof(tmpProof, env.journal) == false);
}
// success
{
BEAST_EXPECT(syntaxCheckProof(proof, env.journal) == true);
}
}
void
testSyntaxCheckXPOP(FeatureBitset features)
{
testcase("import utils - syntaxCheckXPOP");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// blob empty
{
Blob raw{};
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// blob string empty
{
Blob raw{0};
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP failed to parse string json
{
Blob raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP is not a JSON object
{
std::string strJson = R"json([])json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger is not a JSON object
{
std::string strJson = R"json({
"ledger": "not object"
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.transaction is not a JSON object
{
std::string strJson = R"json({
"ledger": {},
"transaction": "not object"
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation is not a JSON object
{
std::string strJson = R"json({
"ledger": {},
"transaction": {},
"validation": "not object"
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.LEDGER
// XPOP.ledger.acroot missing or wrong format
// invalid xpop.ledger.acroot (must be string)
{
std::string strJson = R"json({
"ledger": {
"acroot": {}
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.acroot missing or wrong format
// invalid xpop.ledger.acroot (must be 64)
{
std::string strJson = R"json({
"ledger": {
"acroot": "00000000000000000000000000000000000000000000000000000000000000000"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.acroot missing or wrong format
// invalid xpop.ledger.acroot (must be hex)
{
std::string strJson = R"json({
"ledger": {
"acroot": "this string will pass a length check but fail when checking hex."
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.txroot missing or wrong format
// invalid xpop.ledger.txroot (must be string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": {}
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.txroot missing or wrong format
// invalid xpop.ledger.txroot (must be 64)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "00000000000000000000000000000000000000000000000000000000000000000"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.txroot missing or wrong format
// invalid xpop.ledger.txroot (must be hex)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "this string will pass a length check but fail when checking hex."
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.phash missing or wrong format
// invalid xpop.ledger.phash (must be string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": {}
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.phash missing or wrong format
// invalid xpop.ledger.phash (must be 64)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "00000000000000000000000000000000000000000000000000000000000000000"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.phash missing or wrong format
// invalid xpop.ledger.phash (must be hex)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "this string will pass a length check but fail when checking hex."
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.close missing or wrong format
// invalid xpop.ledger.close (must be int)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": "738786851"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.coins missing or wrong format
// invalid xpop.ledger.coins (must be int or string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": {}
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.coins missing or wrong format
// invalid xpop.ledger.coins (must be int or string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "not an int string"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.cres missing or wrong format
// invalid xpop.ledger.cres (must be int)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": "not an int"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.flags missing or wrong format
// invalid xpop.ledger.flags (must be int)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": "not an int"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.ledger.pclose missing or wrong format
// invalid xpop.ledger.pclose (must be int)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": "not an int"
},
"transaction": {},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.TRANSACTION
// XPOP.transaction.blob missing or wrong format
// invalid xpop.transaction.blob (must be hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": {}
},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.transaction.blob missing or wrong format
// invalid xpop.transaction.blob (must be hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "not a hex"
},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.transaction.meta missing or wrong format
// invalid xpop.transaction.meta (must be hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": {}
},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.transaction.meta missing or wrong format
// invalid xpop.transaction.meta (must be hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "not a hex"
},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.transaction.proof failed syntax check
// invalid xpop.transaction.proof (must pass syntaxCheckProof)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {}
},
"validation": {}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.data missing or wrong format
// invalid xpop.validation.data (must be object)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": "not an object"
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl missing or wrong format
// invalid xpop.validation.unl (must be object)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": "not an object"
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.TRANSACTION.DATA
// XPOP.validation.data entry has wrong format
// invalid xpop.validation.data key/value (must be base58 hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A"
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"not base 58": "",
},
"unl": {}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.public_key missing or
// invalid xpop.validation.data key/value (must be base58 hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": 1,
},
"unl": {}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.public_key invalid key type.
// invalid xpop.validation.data key/value (must be base58 hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "not a hex",
},
"unl": {}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.TRANSACTION.UNL
// XPOP.validation.unl.public_key missing or
// invalid xpop.validation.unl.public_key (must be hex string with valid
// key type)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": 1
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.public_key missing or
// invalid xpop.validation.unl.public_key (must be hex string with valid
// key type)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "not a hex"
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.public_key missing or
// invalid xpop.validation.unl.public_key (must be hex string with valid
// key type)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "0074D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1"
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.manifest missing or wrong
// invalid xpop.validation.unl.manifest (must be string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": 1
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.blob missing or wrong
// invalid xpop.validation.unl.blob (must be base 64 string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": 1
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.blob missing or wrong
// invalid xpop.validation.unl.blob (must be base 64 string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": "not a base 64 string"
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.signature missing or wrong
// invalid xpop.validation.unl.signature (must be hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9DdFNBND0ifV19",
"signature": 1
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.signature missing or wrong
// invalid xpop.validation.unl.signature (must be hex string)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9DdFNBND0ifV19",
"signature": "not a hex"
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl.version missing or
// invalid xpop.validation.unl.version (must be int)
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9DdFNBND0ifV19",
"signature": "849F6B8DA6E11C213B561659C16F13D35385E8EA9E775483ADC84578F6D578943DE5EB681584B2C03EFFFDFD216F9E0B21576E482F941C7195893B72B5B1F70D",
"version": "not an int"
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(
syntaxCheckXPOP(raw, env.journal).has_value() == false);
}
// XPOP.validation.unl entry has wrong format
// valid xpop
{
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9DdFNBND0ifV19",
"signature": "849F6B8DA6E11C213B561659C16F13D35385E8EA9E775483ADC84578F6D578943DE5EB681584B2C03EFFFDFD216F9E0B21576E482F941C7195893B72B5B1F70D",
"version": 1
}
}
})json";
Blob raw = Blob(strJson.begin(), strJson.end());
BEAST_EXPECT(syntaxCheckXPOP(raw, env.journal).has_value() == true);
}
}
void
testGetVLInfo(FeatureBitset features)
{
testcase("import utils - getVLInfo");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
std::string strJson = R"json({
"ledger": {
"acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
"txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
"close": 738786851,
"coins": "99999998999999868",
"cres": 10,
"flags": 10,
"pclose": 738786851
},
"transaction": {
"blob": "12000322000000002400000002201B0000006C201D0000535968400000003B9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519560F81148EA87CA747AF74EE03A7E36C0F8EB1C1568D588A",
"meta": "201C00000000F8E51100612500000052553CEFE169D8DB251A7757C8A80214D7466E81FB2EF6A91E0970B428326B2134EB56A92E4B1340C656FE01A66D529BB7957EAD34D9E193D190EA62AAE4764B5B08C5E624000000026240000000773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481148EA87CA747AF74EE03A7E36C0F8EB1C1568D588AE1E1F1031000",
"proof": {
"children": {
"3": {
"children": {},
"hash": "AA126BA0486ADAE575BBC5335E42E236275A452037CA9A876D1A8CDACA1AE542",
"key": "39D6F6AB7D0A827DD1502B7B9571626A2B601DBE5BC786EF8ADD00E0CB7FCEB3"
}
},
"hash": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
"key": "0000000000000000000000000000000000000000000000000000000000000000"
}
},
"validation": {
"data": {
"n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN": "22800000012600000059292C08FE225149FB62BB67011E9C3F641848E32A209CEAD98BD4D73A78A11F2F92E4AFA65F6A50177BC1723CF6FEDBE539067FF3EA2CF5ADF8FD611DA22B682617DB782041BCA913732103D4A61C3C4E882313665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76473045022100E53006BDE6CB3ECB7FD08711B103DFD793438751DDFBED5DC421A3C7DE3545C4022045A2332222261A55153A338930545A5F463DAC2E1BF56C4CE5E02726E919A714"
},
"unl": {
"public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1",
"manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==",
"blob": "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9DdFNBND0ifV19",
"signature": "849F6B8DA6E11C213B561659C16F13D35385E8EA9E775483ADC84578F6D578943DE5EB681584B2C03EFFFDFD216F9E0B21576E482F941C7195893B72B5B1F70D",
"version": 1
}
}
})json";
Json::Value xpop;
Json::Reader reader;
reader.parse(strJson, xpop);
Json::FastWriter writer;
// Import: unl blob was not valid json (after base64 decoding)
{
Json::Value tmpXpop = xpop;
tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
std::string strJson = writer.write(tmpXpop);
Blob raw(strJson.begin(), strJson.end());
auto const xpop = syntaxCheckXPOP(raw, env.journal);
BEAST_EXPECT(getVLInfo(*xpop, env.journal).has_value() == false);
}
// Import: failed to deserialize manifest
{
Json::Value tmpXpop = xpop;
tmpXpop[jss::validation][jss::unl][jss::manifest] = "YmFkSnNvbg==";
std::string strJson = writer.write(tmpXpop);
Blob raw(strJson.begin(), strJson.end());
auto const xpop = syntaxCheckXPOP(raw, env.journal);
BEAST_EXPECT(getVLInfo(*xpop, env.journal).has_value() == false);
}
// Import: valid unl blob
{
Json::Value tmpXpop = xpop;
std::string strJson = writer.write(tmpXpop);
Blob raw(strJson.begin(), strJson.end());
auto const xpop = syntaxCheckXPOP(raw, env.journal);
auto const [seq, masterKey] = *getVLInfo(*xpop, env.journal);
BEAST_EXPECT(std::to_string(seq) == "2");
BEAST_EXPECT(
strHex(masterKey) ==
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC1");
}
}
void
testEnabled(FeatureBitset features)
{
testcase("enabled");
using namespace jtx;
using namespace std::literals::chrono_literals;
for (bool const withImport : {false, true})
{
// If the Import amendment is not enabled, you should not be able
// to import.
auto const amend = withImport ? features : features - featureImport;
Env env{*this, makeNetworkVLConfig(21337, keys), amend};
auto const feeDrops = env.current()->fees().base;
// setup env
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// burn 1000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(1'000'000'000), ter(tesSUCCESS));
env.close();
auto const txResult =
withImport ? ter(tesSUCCESS) : ter(temDISABLED);
// IMPORT - Account Set
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
txResult);
env.close();
}
}
void
testInvalidPreflight(FeatureBitset features)
{
testcase("import invalid preflight");
using namespace test::jtx;
using namespace std::literals;
//----------------------------------------------------------------------
// preflight
// temDISABLED - disabled
// !ctx.rules.enabled(featureImport)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys), features - featureImport};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)), ter(temDISABLED));
}
// temDISABLED - disabled
// !ctx.rules.enabled(featureHooksUpdate1) &&
// ctx.tx.isFieldPresent(sfIssuer)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys), features - featureHooksUpdate1};
auto const alice = Account("alice");
auto const issuer = Account("issuer");
env.fund(XRP(1000), alice, issuer);
env.close();
auto tx = import(alice, loadXpop(ImportTCAccountSet::w_seed));
tx[sfIssuer.jsonName] = issuer.human();
env(tx, ter(temDISABLED));
}
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// burn 1000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(1'000'000'000), ter(tesSUCCESS));
env.close();
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// temMALFORMED
// Issuer cannot be the source account.
{
auto tx = import(alice, loadXpop(ImportTCAccountSet::w_seed));
tx[sfIssuer.jsonName] = alice.human();
env(tx, ter(temMALFORMED));
}
// telINSUF_FEE_P - sfFee cannot be 0
{
Json::Value tx =
import(alice, loadXpop(ImportTCAccountSet::w_seed));
STAmount const& fee = XRP(0);
tx[jss::Fee] = fee.getJson(JsonOptions::none);
env(tx, ter(telINSUF_FEE_P));
}
// DA: blob.resize(513 * 1024); fails with Boost
// temMALFORMED
// blob was more than 512kib
// {
// ripple::Blob blob;
// blob.resize(513 * 1024);
// Json::Value tx = import(alice, loadXpop(ImportTCAccountSet::w_seed));
// tx[sfBlob.jsonName] = strHex(blob);
// env(tx, ter(temMALFORMED));
// }
// temMALFORMED - sfAmount field must be in drops
{
Json::Value tx =
import(alice, loadXpop(ImportTCAccountSet::w_seed));
STAmount const& amount = XRP(-1);
tx[jss::Amount] = amount.getJson(JsonOptions::none);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - !xpop | XPOP.validation is not a JSON object
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation] = {}; // one of many ways to throw error
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: validation.unl.public_key was not valid hex
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::public_key] = "not a hex";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: validation.unl.public_key was not a recognised
// public key type
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::public_key] =
"0084D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC1";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// getInnerTxn - !xpop
// DA: Duplicate -
// getInnerTxn - failed to deserialize tx blob inside xpop (invalid hex)
// DA: Duplicate - "XPOP.transaction.blob missing or wrong format"
// getInnerTxn - failed to deserialize tx meta inside xpop (invalid hex)
// DA: Duplicate - "XPOP.transaction.meta missing or wrong format"
// getInnerTxn - failed to deserialize tx blob/meta inside xpop
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] = "DEADBEEF";
tmpXpop[jss::transaction][jss::meta] = "DEADBEEF";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// // temMALFORMED - !stpTrans
// // DA: Duplicate - getInnerTxn (Any Failure)
// temMALFORMED - Import: attempted to import xpop containing an emitted
// or pseudo txn.
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"12000322000000002400000002201B00000069201D0000535968400000003B"
"9ACA0073210388935426E0D08083314842EDFBB2D517BD47699F9A4527318A"
"8E10468C97C0527446304402200E40CA821A7BFE347448FFA6540F67C7FFBF"
"0287756D324DFBADCEDE2B23782C02207BE1B1294F1A1D5AC21990740B78F9"
"C0693431237D6E07FE84228082986E50FF8114AE123A8556F3CF9115471137"
"6AFB0F894F832B3DED202E000000013D00000000000000015B2200676F4B9B"
"45C5ADE9DE3C8CD01200CB12DFF8C792220DB088FE615BE5C2905C207064D8"
"1954F6A7225A8BAADF5A3042016BFB87355D1D0AFEDBAA8FB22F98355D745F"
"398EEE9E6B294BBE6A5681A31A6107243D19384E277B5A7B1F23B8C83DE78A"
"14AE123A8556F3CF91154711376AFB0F894F832B3DE1";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: inner txn lacked transaction result
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::meta] =
"201C00000006F8E5110061250000005655463E39A6AFDDA77DBF3591BF3C2A"
"4BE9BB8D9113BF6D0797EB403C3D0D894FEF5692FA6A9FC8EA6018D5D16532"
"D7795C91BFB0831355BDFDA177E86C8BF997985FE624000000026240000000"
"773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481"
"14AE123A8556F3CF91154711376AFB0F894F832B3DE1E1F1";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: inner txn did not have a tesSUCCESS or tec
// result
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::meta] =
"201C00000006F8E5110061250000005655463E39A6AFDDA77DBF3591BF3C2A"
"4BE9BB8D9113BF6D0797EB403C3D0D894FEF5692FA6A9FC8EA6018D5D16532"
"D7795C91BFB0831355BDFDA177E86C8BF997985FE624000000026240000000"
"773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481"
"14AE123A8556F3CF91154711376AFB0F894F832B3DE1E1F103103C";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: import and txn inside xpop must be signed by
// the same account
{
Json::Value const tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value const tx = import(bob, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: attempted to import xpop containing a txn with
// a sfNetworkID field.
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"120003210000535922000000002400000002201B00000069201D0000535968"
"400000003B9ACA0073210388935426E0D08083314842EDFBB2D517BD47699F"
"9A4527318A8E10468C97C0527446304402200E40CA821A7BFE347448FFA654"
"0F67C7FFBF0287756D324DFBADCEDE2B23782C02207BE1B1294F1A1D5AC219"
"90740B78F9C0693431237D6E07FE84228082986E50FF8114AE123A8556F3CF"
"91154711376AFB0F894F832B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: OperationLimit missing from inner xpop txn.
// outer txid:
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"12000322000000002400000002201B0000006C68400000003B9ACA007321ED"
"A8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C"
"747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F"
"232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C51"
"77C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: Wrong network ID for OperationLimit in inner
// txn. outer txid:
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"12000322000000002400000002201B0000006C201D0000535A68400000003B"
"9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62"
"ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593EC"
"C21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF"
"3EC206AD3C5177C519560F8114AE123A8556F3CF91154711376AFB0F894F83"
"2B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(telWRONG_NETWORK));
}
// temMALFORMED - Import: inner txn must be an AccountSet, SetRegularKey
// or SignerListSet transaction.
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"12006322000000002400000002201B0000006C201D0000535968400000003B"
"9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62"
"ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593EC"
"C21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF"
"3EC206AD3C5177C519560F8114AE123A8556F3CF91154711376AFB0F894F83"
"2B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: outer and inner txns were (multi) signed with
// different keys.
// temMALFORMED - Import: outer and inner txns were signed with
// different keys.
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"12000322000000002400000002201B0000006C201D0000535968400000003B"
"9ACA007321EBA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62"
"ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593EC"
"C21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF"
"3EC206AD3C5177C519560F8114AE123A8556F3CF91154711376AFB0F894F83"
"2B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: inner txn signature verify failed
// DA: TODO: ASK FOR HELP
// temMALFORMED - Import: failed to deserialize manifest on txid
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::manifest] = "YmFkSnNvbg==";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: manifest master key did not match top level
// master key in unl section of xpop
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::manifest] =
"JAAAAAFxIe2E1ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+"
"b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338"
"jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/"
"ikfgf9SZOlOGcBcBJAw44PLjH+"
"HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+"
"IILJIiIKU/+1Uxx0FRpQbMDA==";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: manifest signature invalid
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::manifest] =
"JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+"
"b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkA3UjfY5zOEkhq31tU4338"
"jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/"
"ikfgf9SZOlOGcBcBJAw34PLjH+"
"HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+"
"IILJIiIKU/+1Uxx0FRpQbMDA==";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: unl blob not signed correctly
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::signature] =
"949F6B8DA6E11C213B561659C16F13D35385E8EA9E775483ADC84578F6D578"
"943DE5EB681584B2C03EFFFDFD216F9E0B21576E482F941C7195893B72B5B1"
"F70D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: unl blob not signed correctly
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::signature] = "not a hex";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// DA: GOOD SIGNATURE NOT JSON
// temMALFORMED - Import: unl blob was not valid json (after base64
// decoding)
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// DA: GOOD SIGNATURE GOOD JSON MISSING FIELDS
// temMALFORMED - Import: unl blob json (after base64 decoding) lacked
// required fields and/or types
// temMALFORMED - Import: unl blob validUntil <= validFrom
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] =
"eyJzZXF1ZW5jZSI6MSwiZWZmZWN0aXZlIjowLCJleHBpcmF0aW9uIjowLCJ2YW"
"xpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVB"
"RkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMT"
"FDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlE"
"TUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S2"
"8raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2"
"TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWG"
"dTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lO"
"U0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcD"
"dWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlv"
"bl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGRE"
"U5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3Qi"
"OiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeH"
"RIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VG"
"clU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3Tj"
"Zpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQx"
"QVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RT"
"lTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9j"
"eWNyRi9DdFNBND0ifV19";
tmpXpop[jss::validation][jss::unl][jss::signature] =
"2B3C0ECB63C82454522188337354C480693A9BCD64E776B4DBAD4C61B9E72D"
"D4CC1DC237B06891E57C623C38506FE8E01B1914C9413471BCC160111E2829"
"7606";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: unl blob expired
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] =
"eyJzZXF1ZW5jZSI6MSwiZWZmZWN0aXZlIjowLCJleHBpcmF0aW9uIjoxLCJ2YW"
"xpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVB"
"RkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMT"
"FDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlE"
"TUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S2"
"8raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2"
"TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWG"
"dTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lO"
"U0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcD"
"dWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlv"
"bl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGRE"
"U5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3Qi"
"OiJKQUFBQUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeH"
"RIdENaQmprS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VG"
"clU0bnJFcVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3Tj"
"Zpa1ZFQkk2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQx"
"QVVnOVg4WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RT"
"lTanVMUjZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9j"
"eWNyRi9DdFNBND0ifV19";
tmpXpop[jss::validation][jss::unl][jss::signature] =
"FA82662A23EC78E9644C65F752B7A58F61F35AC36C260F9E9D5CAC7D53D16D"
"5D615A02A6462F2618C162D089AD2E3BA7D656728392180517A81B4C47F86A"
"640D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: unl blob not yet valid
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] =
"eyJzZXF1ZW5jZSI6MSwiZWZmZWN0aXZlIjozNjAwLCJleHBpcmF0aW9uIjo4Nj"
"QwMCwidmFsaWRhdG9ycyI6W3sidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRUQz"
"OEJENDQ1QUZENjIxNTk2MjBDQzE5NkMyNjY4QTI2QjZGQkIzNkIwOTlFQjU1Qj"
"M4QTU4QzExQzEyMDRERTVDIiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMDR2VVJh"
"L1dJVmxpRE1HV3dtYUtKcmI3czJzSm5yVmJPS1dNRWNFZ1RlWEhNaEFvR01OY3"
"NnVVVOd0tvK2g3emhhWEtGK0hHdzZYaEVqb0RLcmFsa1luTWpLZGtjd1JRSWhB"
"SjdjTjRKNk5kVmZwbnZFSS9aZXVXQ1R2bnBhSmlyS05GY0Mzek1Pamd3akFpQW"
"JLSTBmYlhnUzFSTGxvTmh4ZEhoVnE5b3pFV1ZFOWNJd1hETjNBeHF5WTNBU1FD"
"dDArdS9pTlNERDZiWHZVVHRkbXQ0TnJ0bGJ4NFZ6dW1UcGZqUllwNGxNb0kvaD"
"QzcFVUanA3VkZvWGJuS1dqVmhxTmFHbTU3N0s2SjY5N1haN1RRRT0ifSx7InZh"
"bGlkYXRpb25fcHVibGljX2tleSI6IkVEQkVFMzBGQUU5MkVFRTg4RTFDNDk4ME"
"QwOUVDRkRFOTlBMTE2RDA3OEVDMjE4NTdEQjFCNDdCNDI2NDE4RTQyOCIsIm1h"
"bmlmZXN0IjoiSkFBQUFBSnhJZTIrNHcrdWt1N29qaHhKZ05DZXo5NlpvUmJRZU"
"93aGhYMnh0SHRDWkJqa0tITWhBOVNtSER4T2lDTVRabDVuUnhycDJ5aldaNWdq"
"eDJEcm9VRnJVNG5yRXFVN2RrY3dSUUloQUxkRUZlalkrcFVuZ2xpN3NUdmliME"
"JtREhQN042aWtWRUJJNkg3SXdVMXpBaUJkc3lvU3FQY0MyTk1xZ0FuSFhIR2Rr"
"QUl3QlFEMUFVZzlYOFpKTHlmY3dIQVNRQ3QxYktWek9NeFJRbVIzd05LNGRLZG"
"9mSUdyeEU5U2p1TFI2UGE4QjVuMDhTWUo4SzYyZ2UrOWE2QnRaYWxFbS9IT2Rj"
"ejBOQUZPY3ljckYvQ3RTQTQ9In1dfQ";
tmpXpop[jss::validation][jss::unl][jss::signature] =
"9CCA07A3EDD1334D5ADCB3730D8F3F9BD1E0C338100384C7B15B6A910F96BE"
"4F46E3052B37E9FE2E7DC9918BD85B9E871923AE1BDD7144EE2A92F625064C"
"570C";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: depth > 32
// temMALFORMED - Import: !proof->isObject() && !proof->isArray()
// DA: Catchall Error
// temMALFORMED - Import: return false
// temMALFORMED - Import: xpop proof did not contain the specified txn
// hash
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::proof][jss::children]["D"]
[jss::children]["7"][jss::hash] =
"12D47E7D543E15F1EDBA91CDF335722727851BDDA8C2FF8924772AD"
"C6B522A29";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// // temMALFORMED - Import: depth > 32
// // temMALFORMED - Import: !proof.isObject() && !proof.isArray()
// // DA: CatchAll Error
// temMALFORMED - Import: computed txroot does not match xpop txroot,
// invalid xpop.
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::proof][jss::children]["3"]
[jss::hash] =
"22D47E7D543E15F1EDBA91CDF335722727851BDDA8C2FF8924772AD"
"C6B522A29";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: error parsing coins | phash | acroot in the
// ledger section of XPOP.
// temMALFORMED - Import: unl blob contained invalid validator entry,
// skipping
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] =
"eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
"JzIjpbeyJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21h"
"S0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aG"
"FYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBu"
"dkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG"
"9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJY"
"dlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm"
"5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsibWFuaWZlc3QiOiJKQUFB"
"QUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQm"
"prS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJF"
"cVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQk"
"k2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4"
"WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUj"
"ZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9D"
"dFNBND0ifV19=";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: unl blob contained an invalid validator key,
// skipping
// {
// }
// temMALFORMED - Import: unl blob contained an invalid manifest,
// skipping
// {
// }
// // temMALFORMED - Import: unl blob list entry manifest master key did
// // not match master key, skipping
// // temMALFORMED - Import: unl blob list entry manifest signature
// // invalid, skipping
// // temMALFORMED - Import: validator nodepub did not appear in
// validator
// // list but did appear
// // temMALFORMED - Import: validator nodepub key appears more than
// once
// // in data section
// temMALFORMED - Import: validation inside xpop was not valid hex
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value valData;
valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
"not a hex";
tmpXpop[jss::validation][jss::data] = valData;
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: validation message was not for computed ledger
// hash
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value valData;
valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
"22800000012600000056292C0D012051A0829745427488A59B6525231634DC"
"327F91589EB03F469ABF1C3CA32070625A501790AD68E9045001FDEED03F0A"
"C8277F11F6E46E0ABD2DC38B1F8240D56B22E4F2732103D4A61C3C4E882313"
"665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76463044022072"
"427336342AE80B7AAE407CEA611B0DA680426B3C5894E52EE2D23641D09A73"
"02203B131B68D3C5B6C5482312CC7D90D2CE131A9C46458967F2F98688B726"
"C16719";
tmpXpop[jss::validation][jss::data] = valData;
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: validation inside xpop was not signed with a
// signing key we recognise
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value valData;
valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
"22800000012600000056292C0D012051B0829745427488A59B6525231634DC"
"327F91589EB03F469ABF1C3CA32070625A501790BD68E9045001FDEED03F0A"
"C8277F11F6E46E0ABD2DC38B1F8240D56B22E4F2732103E4A61C3C4E882313"
"665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76463054022072"
"427336342AE80B7AAE407CEA611B0DA680426B3C5894E52EE2D23641D09A73"
"02203B131B68D3C5B6C5482312CC7D90D2CE131A9C46458967F2F98688B726"
"C16719";
tmpXpop[jss::validation][jss::data] = valData;
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: validation inside xpop was not correctly
// signed
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value valData;
valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
"22800000012600000056292C0D012051B0829745427488A59B6525231634DC"
"327F91589EB03F469ABF1C3CA32070625A501790BD68E9045001FDEED03F0A"
"C8277F11F6E46E0ABD2DC38B1F8240D56B22E4F2732103D4A61C3C4E882313"
"665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76463044022072"
"427336342AE80B7AAE407CEA611B0DA680426B3C5894E52EE2D23641D09A73"
"02203B131B68D3C5B6C5482312CC7D90D2CE131A9C46458967F2F98688B726"
"C16719";
tmpXpop[jss::validation][jss::data] = valData;
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: validation inside xpop was not able to be
// parsed DA: Unknown Catch All
// temMALFORMED - Import: xpop did not contain an 80% quorum for the txn
// it purports to prove.
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value valData;
valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
"";
valData["n9KXYzdZD8YpsNiChtMjP6yhvQAhkkh5XeSTbvYyV1waF8wkNnBT"] =
"";
tmpXpop[jss::validation][jss::data] = valData;
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// temMALFORMED - Import: xpop inner txn did not contain a sequence
// number or fee No Sequence
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"1200632200000000201B0000006C201D0000535968400000003B9ACA007321"
"EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A9"
"6C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF"
"0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C"
"5177C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// No Fee
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"12006322000000002400000002201B0000006C201D000053597321EDA8D46E"
"11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440"
"549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4"
"375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519"
"560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// Bad Fee - TODO
}
void
testInvalidPreclaim(FeatureBitset features)
{
testcase("import invalid preclaim");
using namespace test::jtx;
using namespace std::literals;
// ----------------------------------------------------------------------
// preclaim
// temDISABLED
{
test::jtx::Env env{
*this,
makeNetworkVLConfig(21337, keys),
features - featureImport};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
ter(temDISABLED));
}
// tefINTERNAL
// during preclaim could not parse xpop, bailing.
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation] = {};
Json::Value const tx = import(alice, tmpXpop);
// DA: Sanity Check - tefINTERNAL(preclaim)
env(tx, ter(temMALFORMED));
}
// tefINTERNAL
// during preclaim could not find importSequence, bailing.
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"1200632200000000201B0000006C201D0000535968400000003B9ACA007321"
"EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A9"
"6C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF"
"0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C"
"5177C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
Json::Value const tx = import(alice, tmpXpop);
// DA: Sanity Check - tefINTERNAL(preclaim)
env(tx, ter(temMALFORMED));
}
// tefPAST_IMPORT_SEQ
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
ter(tesSUCCESS));
env(import(alice, loadXpop(ImportTCAccountSet::min)),
fee(feeDrops * 10),
ter(tefPAST_IMPORT_SEQ));
}
// temBAD_FEE
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const alice = Account("alice");
env.memoize(alice);
Json::Value tx =
import(alice, loadXpop(ImportTCAccountSet::w_seed));
tx[jss::Sequence] = 0;
tx[jss::Fee] = 10;
env(tx, ter(temBAD_FEE));
}
// tefINTERNAL
// during preclaim could not parse vlInfo, bailing.
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
Json::Value const tx = import(alice, tmpXpop);
// DA: Sanity Check - tefINTERNAL(preclaim)
env(tx, ter(temMALFORMED));
}
// tefPAST_IMPORT_VL_SEQ
// import vl sequence already used, bailing.
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
std::string const pkString =
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC1";
auto const pkHex = strUnHex(pkString);
PublicKey const pk{makeSlice(*pkHex)};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
Json::Value const xpop1_1 =
loadXpop(ImportTCAccountSet::unl_seq_1_1);
Json::Value const tx_1_1 = import(alice, xpop1_1);
env(tx_1_1, fee(feeDrops * 10), ter(tesSUCCESS));
BEAST_EXPECT(importVLSequence(env, pk) == 1);
Json::Value const xpop2_1 =
loadXpop(ImportTCAccountSet::unl_seq_2_1);
Json::Value const tx_2_1 = import(bob, xpop2_1);
env(tx_2_1, fee(feeDrops * 10), ter(tesSUCCESS));
BEAST_EXPECT(importVLSequence(env, pk) == 2);
Json::Value const xpop1_2 =
loadXpop(ImportTCAccountSet::unl_seq_1_2);
Json::Value const tx_1_2 = import(alice, xpop1_2);
env(tx_1_2, fee(feeDrops * 10), ter(tefPAST_IMPORT_VL_SEQ));
}
// tesSUCCESS
// DA: testImportSequence
// tefINTERNAL
// DA: Impossible Test
// tefINTERNAL
// DA: Impossible Test
// tesSUCCESS
// DA: testImportSequence
// telIMPORT_VL_KEY_NOT_RECOGNISED
// import vl key not recognized, bailing.
{
test::jtx::Env env{*this, makeNetworkConfig(21337)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
Json::Value const tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value const tx = import(alice, tmpXpop);
env(tx, fee(feeDrops * 10), ter(telIMPORT_VL_KEY_NOT_RECOGNISED));
}
}
void
testInvalidDoApply(FeatureBitset features)
{
testcase("import invalid doApply");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// burn 1000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(1'000'000'000), ter(tesSUCCESS));
env.close();
//----------------------------------------------------------------------
// doApply
// temDISABLED
// tefINTERNAL/temMALFORMED - ctx_.tx.isFieldPresent(sfBlob)
{
Json::Value tx;
tx[jss::TransactionType] = jss::Import;
tx[jss::Account] = alice.human();
env(tx, ter(temMALFORMED));
}
// tefINTERNAL/temMALFORMED - !xpop
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation] = {}; // one of many ways to throw error
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// tefINTERNAL/temMALFORMED
// during apply could not find importSequence or fee, bailing.
// No Fee
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::transaction][jss::blob] =
"1200632200000000201B0000006C201D0000535968400000003B9ACA007321"
"EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A9"
"6C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF"
"0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C"
"5177C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// No Sequence
// tefINTERNAL
// initBal <= beast::zero
// Sanity Check
// tefINTERNAL
// ImportSequence passed
// Sanity Check (tefPAST_IMPORT_SEQ)
// tefINTERNAL/temMALFORMED
// !infoVL
{
Json::Value tmpXpop = loadXpop(ImportTCAccountSet::w_seed);
tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
Json::Value const tx = import(alice, tmpXpop);
env(tx, ter(temMALFORMED));
}
// tefINTERNAL
// current > infoVL->first
// Sanity Check (tefPAST_IMPORT_VL_SEQ)
}
void
testAccountSet(FeatureBitset features)
{
testcase("account set tx");
using namespace test::jtx;
using namespace std::literals;
// w/ seed -> dne (bad signer)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
env.memoize(alice);
env.memoize(bob);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(bob, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, bob, ter(temMALFORMED));
env.close();
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins);
// confirm account does not exist
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle == nullptr);
}
// w/ seed -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
env.memoize(alice);
env.memoize(bob);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
Json::Value tx =
import(alice, loadXpop(ImportTCAccountSet::w_seed));
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = XRP(1000) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ regular key other -> dne (bad signer)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.memoize(alice);
env.memoize(bob);
env.memoize(carol);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == 99'999'990'000'000'000);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx - wrong regular key
Json::Value txBad =
import(alice, loadXpop(ImportTCAccountSet::w_regular_key));
txBad[jss::Sequence] = 0;
txBad[jss::Fee] = 0;
env(txBad, alice, sig(carol), ter(temMALFORMED));
env.close();
// confirm fee was not minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice);
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins);
// confirm account does not exist
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle == nullptr);
}
// w/ regular key -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
env.memoize(alice);
env.memoize(bob);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == 99'999'990'000'000'000);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
Json::Value tx =
import(alice, loadXpop(ImportTCAccountSet::w_regular_key));
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, sig(bob), ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = XRP(1000) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle1] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle1 != nullptr);
// alice cannnot sign
env(noop(alice),
sig(alice),
fee(feeDrops),
ter(tefMASTER_DISABLED));
// bob cannot sign
env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH));
}
// w/ signers list -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.memoize(alice);
env.memoize(bob);
env.memoize(carol);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_signers);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, msig(bob, carol), ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = drops(48) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tefNOT_MULTI_SIGNING));
env.close();
// alice cannnot sign
env(noop(alice),
sig(alice),
fee(feeDrops),
ter(tefMASTER_DISABLED));
}
// w/ seed -> funded
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10,000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// import tx
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(10 * 10),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = XRP(1000) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ regular key -> funded
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins - (4 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_regular_key);
Json::Value const tx = import(alice, xpopJson);
env(tx, alice, sig(bob), fee(10 * 10), ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = XRP(1000) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists bob can sign
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
BEAST_EXPECT(!acctSle->isFieldPresent(sfRegularKey));
env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH));
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ signers -> funded
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins - (6 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_signers);
Json::Value const tx = import(alice, xpopJson);
env(tx,
alice,
msig(bob, carol),
fee((3 * feeDrops) * 10),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = drops(48) - ((3 * feeDrops) * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tefNOT_MULTI_SIGNING));
env.close();
// alice cannnot sign
env(noop(alice), sig(alice), fee(feeDrops), ter(tesSUCCESS));
}
}
void
testAccountSetFlags(FeatureBitset features)
{
testcase("account set flags");
using namespace test::jtx;
using namespace std::literals;
// account set flags not migrated
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// import tx
env(import(alice, loadXpop(ImportTCAccountSet::w_flags)),
fee(feeDrops * 10),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(10) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
}
void
testSetRegularKey(FeatureBitset features)
{
testcase("set regular key tx");
using namespace test::jtx;
using namespace std::literals;
// w/ seed -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm env
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
env.memoize(alice);
env.memoize(bob);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + reward amount
auto const totalBurn = drops(12) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == bob.id());
env(noop(alice), sig(bob), fee(feeDrops), ter(tesSUCCESS));
}
// w/ regular key -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm env
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.memoize(alice);
env.memoize(bob);
env.memoize(carol);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson =
loadXpop(ImportTCSetRegularKey::w_regular_key);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, sig(bob), ter(tesSUCCESS));
env.close();
// total burn = burn drops - initial value
auto const totalBurn = drops(12) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == carol.id());
env(noop(alice), sig(carol), fee(feeDrops), ter(tesSUCCESS));
}
// w/ signers -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm env
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const dave = Account("dave");
env.memoize(alice);
env.memoize(bob);
env.memoize(carol);
env.memoize(dave);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_signers);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, msig(bob, carol), ter(tesSUCCESS));
env.close();
// total burn = burn drops + reward amount
auto const totalBurn = drops(48) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(
acctSle && acctSle->isFieldPresent(sfRegularKey) &&
acctSle->getAccountID(sfRegularKey) == dave.id());
env(noop(alice), sig(dave), fee(feeDrops), ter(tesSUCCESS));
}
// w/ seed -> funded
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm env
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins - (4 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_seed);
env(import(alice, xpopJson), fee(feeDrops * 10), ter(tesSUCCESS));
env.close();
// total burn = burn drops - feeDrops
auto const totalBurn = drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == bob.id());
env(noop(alice), sig(bob), fee(feeDrops), ter(tesSUCCESS));
}
// w/ seed -> funded (update regular key)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (6 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, carol));
env(noop(alice), sig(carol), fee(feeDrops), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (2 * feeDrops));
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_seed);
env(import(alice, xpopJson),
fee(feeDrops * 10),
sig(alice),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key set
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == bob.id());
env(noop(alice), sig(bob), fee(feeDrops), ter(tesSUCCESS));
}
// w/ regular key -> funded (update regular key)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const dave = Account("dave");
env.fund(XRP(1000), alice, bob, carol, dave);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (8 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, dave));
env(noop(alice), sig(dave), fee(feeDrops), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (2 * feeDrops));
// import tx
auto const xpopJson =
loadXpop(ImportTCSetRegularKey::w_regular_key);
env(import(alice, xpopJson),
fee(feeDrops * 10),
sig(bob),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == carol.id());
env(noop(alice), sig(carol), fee(feeDrops), ter(tesSUCCESS));
}
// w/ signers list -> funded (update regular key)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const dave = Account("dave");
auto const elsa = Account("elsa");
env.fund(XRP(1000), alice, bob, carol, dave, elsa);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (10 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, elsa));
env(noop(alice), sig(elsa), fee(feeDrops), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (2 * feeDrops));
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_signers);
env(import(alice, xpopJson),
msig(bob, carol),
fee((3 * feeDrops) * 10),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(48) - ((3 * feeDrops) * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == dave.id());
env(noop(alice), sig(dave), fee(feeDrops), ter(tesSUCCESS));
// confirm signers list not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
}
// seed -> funded (empty regular key)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (6 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, carol));
env(noop(alice), sig(carol), fee(feeDrops), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (2 * feeDrops));
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_seed_empty);
env(import(alice, xpopJson),
fee(feeDrops * 10),
sig(alice),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(!acctSle->isFieldPresent(sfRegularKey));
}
// w/ regular key -> funded (empty regular key)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (6 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, carol));
env(noop(alice), sig(carol), fee(feeDrops), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (2 * feeDrops));
// import tx
auto const xpopJson =
loadXpop(ImportTCSetRegularKey::w_regular_key_empty);
env(import(alice, xpopJson),
fee(feeDrops * 10),
sig(bob),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(!acctSle->isFieldPresent(sfRegularKey));
env(noop(alice), sig(alice), fee(feeDrops), ter(tesSUCCESS));
env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH));
env(noop(alice), sig(carol), fee(feeDrops), ter(tefBAD_AUTH));
}
// w/ signers -> funded (empty regular key)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const dave = Account("dave");
env.fund(XRP(1000), alice, bob, carol, dave);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (8 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, dave));
env(noop(alice), sig(dave), fee(feeDrops), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (2 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (2 * feeDrops));
// import tx
auto const xpopJson =
loadXpop(ImportTCSetRegularKey::w_signers_empty);
env(import(alice, xpopJson),
msig(bob, carol),
fee((3 * feeDrops) * 10),
ter(tesSUCCESS));
env.close();
// total burn = burn drops - fee drops
auto const totalBurn = drops(48) - ((3 * feeDrops) * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(!acctSle->isFieldPresent(sfRegularKey));
env(noop(alice), sig(alice), fee(feeDrops), ter(tesSUCCESS));
env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH));
env(noop(alice), sig(carol), fee(feeDrops), ter(tefBAD_AUTH));
env(noop(alice), sig(dave), fee(feeDrops), ter(tefBAD_AUTH));
}
}
void
testSetRegularKeyFlags(FeatureBitset features)
{
testcase("set regular key flags");
using namespace test::jtx;
using namespace std::literals;
// dne -> dont set flag
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const alice = Account("alice");
auto const bob = Account("bob");
env.memoize(alice);
env.memoize(bob);
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// confirm lsfPasswordSpent is set
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(
(acctSle->getFieldU32(sfFlags) & lsfPasswordSpent) ==
lsfPasswordSpent);
}
// funded -> set flag
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// import tx
auto const xpopJson = loadXpop(ImportTCSetRegularKey::w_seed_zero);
env(import(alice, xpopJson), fee(feeDrops * 10), ter(tesSUCCESS));
env.close();
// confirm lsfPasswordSpent is not set
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(
(acctSle->getFieldU32(sfFlags) & lsfPasswordSpent) == 0);
}
}
void
testSignersListSet(FeatureBitset features)
{
testcase("signers list set tx");
using namespace test::jtx;
using namespace std::literals;
// w/ seed -> dne w/ seed (Bad Fee)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.memoize(alice);
env.memoize(bob);
env.memoize(carol);
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
// confirm total coins header
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson =
loadXpop(ImportTCSignersListSet::w_seed_bad_fee);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
// tx[jss::Fee] = 0;
env(tx, alice, ter(temBAD_FEE));
env.close();
}
// w/ seed -> dne w/ seed
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.memoize(alice);
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
// confirm alice balance
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCSignersListSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - reward
auto const totalBurn = XRP(2) + drops(12) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm fee was minted
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers set
auto const [signers, signersSle] =
signersKeyAndSle(*env.current(), alice);
auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == carol.id());
BEAST_EXPECT(signerEntries[1u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(signerEntries[1u].getAccountID(sfAccount) == bob.id());
// confirm multisign tx
env.close();
auto const aliceSeq = env.seq(alice);
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
// confirm noop master disabled
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ regular key -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const dave = Account("dave");
env.memoize(alice);
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
// confirm alice balance
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const burnAmt = XRP(2);
auto const xpopJson =
loadXpop(ImportTCSignersListSet::w_regular_key);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, sig(bob), ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - fee drops
auto const totalBurn = XRP(2) + drops(12) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm fee was minted
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers set
auto const [signers, signersSle] =
signersKeyAndSle(*env.current(), alice);
auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == dave.id());
BEAST_EXPECT(signerEntries[1u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[1u].getAccountID(sfAccount) == carol.id());
// confirm multisign tx
env.close();
auto const aliceSeq = env.seq(alice);
env(noop(alice),
msig(carol, dave),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
// confirm regular key bad auth
env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH));
// confirm noop master disabled
env(noop(alice), fee(feeDrops), ter(tefMASTER_DISABLED));
}
// w/ signers -> dne
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 100,000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const dave = Account("dave");
auto const elsa = Account("elsa");
env.memoize(alice);
env.memoize(dave);
env.memoize(elsa);
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins);
// confirm alice balance
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCSignersListSet::w_signers);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, msig(bob, carol), ter(tesSUCCESS));
env.close();
// confirm signers set
auto const [signers, signersSle] =
signersKeyAndSle(*env.current(), alice);
auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == dave.id());
BEAST_EXPECT(signerEntries[1u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[1u].getAccountID(sfAccount) == elsa.id());
// confirm multisign tx
env.close();
auto const aliceSeq = env.seq(alice);
env(noop(alice),
msig(dave, elsa),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
// confirm regular key bad auth
env(noop(alice), sig(bob), fee(feeDrops), ter(tefBAD_AUTH));
// confirm noop master disabled
env(noop(alice), fee(feeDrops), ter(tefMASTER_DISABLED));
}
// w/ seed -> funded
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == burnCoins - (6 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// import tx
auto const xpopJson = loadXpop(ImportTCSignersListSet::w_seed);
env(import(alice, xpopJson), fee(feeDrops * 10), ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - fee drops
auto const totalBurn = XRP(2) + drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers set
auto const [signers, signersSle] =
signersKeyAndSle(*env.current(), alice);
auto const signerEntries =
signersSle->getFieldArray(sfSignerEntries);
BEAST_EXPECT(signerEntries.size() == 2);
BEAST_EXPECT(signerEntries[0u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(
signerEntries[0u].getAccountID(sfAccount) == carol.id());
BEAST_EXPECT(signerEntries[1u].getFieldU16(sfSignerWeight) == 1);
BEAST_EXPECT(signerEntries[1u].getAccountID(sfAccount) == bob.id());
// confirm multisign tx
env.close();
auto const aliceSeq = env.seq(alice);
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
// confirm noop alice tx
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ seed (empty) -> funded (has entries)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (6 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the signers list
env(signers(alice, 2, {{bob, 1}, {carol, 1}}));
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (4 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (4 * feeDrops));
// import tx
auto const xpopJson =
loadXpop(ImportTCSignersListSet::w_seed_empty);
env(import(alice, xpopJson), fee(feeDrops * 10), ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - fee drops
auto const totalBurn = XRP(2) + drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
// confirm noop master success
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ regular key (empty) -> funded (has entries)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (6 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the regular key
env(regkey(alice, bob));
env(noop(alice), sig(bob), fee(feeDrops), ter(tesSUCCESS));
env.close();
// set the signers list
env(signers(alice, 2, {{bob, 1}, {carol, 1}}));
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (6 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (6 * feeDrops));
// import tx
auto const xpopJson =
loadXpop(ImportTCSignersListSet::w_regular_key_empty);
env(import(alice, xpopJson),
fee(feeDrops * 10),
sig(bob),
ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - fee drops
auto const totalBurn = XRP(2) + drops(12) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm regular key
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle->getAccountID(sfRegularKey) == bob.id());
env(noop(alice), sig(bob), fee(feeDrops), ter(tesSUCCESS));
// confirm signers not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
// confirm noop master success
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ signers (empty) -> funded (has entries)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
// confirm env
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == burnCoins - (6 * feeDrops));
auto const envAlice = env.balance(alice);
BEAST_EXPECT(envAlice == XRP(1000));
// set the signers list
env(signers(alice, 2, {{bob, 1}, {carol, 1}}));
env(noop(alice),
msig(bob, carol),
fee(3 * feeDrops),
ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - (4 * feeDrops));
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == envAlice - (4 * feeDrops));
// import tx
auto const xpopJson =
loadXpop(ImportTCSignersListSet::w_signers_empty);
env(import(alice, xpopJson),
msig(bob, carol),
fee((3 * feeDrops) * 10),
ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - fee drops
auto const totalBurn = XRP(2) + drops(48) - ((3 * feeDrops) * 10);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm signers not set
auto const k = keylet::signers(alice);
BEAST_EXPECT(env.current()->read(k) == nullptr);
// confirm noop master success
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
}
void
testAccountIndex(FeatureBitset features)
{
testcase("account index");
using namespace test::jtx;
using namespace std::literals;
// Account Index from Import
{
for (std::uint32_t const withFeature : {0, 1, 2})
{
auto const amend = withFeature == 0 ? features
: withFeature == 1 ? features - featureXahauGenesis
: features - featureDeletableAccounts;
test::jtx::Env env{
*this, makeNetworkVLConfig(21337, keys), amend};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
ter(tesSUCCESS));
env.close();
// confirm account index was set
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
std::cout << "withFeature: " << withFeature << "\n";
// confirm sequence
if (withFeature == 0)
{
BEAST_EXPECT((*acctSle)[sfAccountIndex] == 0);
}
std::uint64_t const seq = withFeature == 0 ? 12
: withFeature == 1 ? 6
: 12;
BEAST_EXPECT((*acctSle)[sfSequence] == seq);
// confirm account count was set
if (withFeature == 0)
{
auto const [fee, feeSle] = feesKeyAndSle(*env.current());
BEAST_EXPECT((*feeSle)[sfAccountCount] == 1);
}
}
}
// Account Index from Payment
{
for (std::uint32_t const withFeature : {0, 1, 2})
{
auto const amend = withFeature == 0 ? features
: withFeature == 1 ? features - featureXahauGenesis
: features - featureDeletableAccounts;
test::jtx::Env env{
*this, makeNetworkVLConfig(21337, keys), amend};
auto const feeDrops = env.current()->fees().base;
env.close();
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
env.fund(XRP(1000), alice, bob, carol);
env.close();
struct TestAccountData
{
Account acct;
std::uint32_t index;
std::uint64_t sequence;
};
std::uint64_t const seq = withFeature == 0 ? 11
: withFeature == 1 ? 5
: 11;
std::array<TestAccountData, 3> acctTests = {{
{alice, 0, seq},
{bob, 1, seq},
{carol, 2, seq},
}};
for (auto const& t : acctTests)
{
// confirm index was set
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), t.acct);
// confirm sequence
if (withFeature == 0)
{
BEAST_EXPECT((*acctSle)[sfAccountIndex] == t.index);
}
BEAST_EXPECT((*acctSle)[sfSequence] == t.sequence);
}
if (withFeature == 0)
{
// confirm count was updated
auto const [fee, feeSle] = feesKeyAndSle(*env.current());
BEAST_EXPECT((*feeSle)[sfAccountCount] == 3);
}
}
}
}
void
testHookIssuer(FeatureBitset features)
{
testcase("hook issuer tx");
using namespace test::jtx;
using namespace std::literals;
// Test that hook can reject and does NOT mint the funds.
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
auto const issuer = Account("issuer");
env.fund(XRP(10000), alice, issuer);
env.close();
std::string const createCodeHex =
"0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F"
"017F60017F017E02250303656E76057472616365000003656E7608726F6C6C"
"6261636B000103656E76025F670002030201030503010002062B077F0141C0"
"88040B7F004180080B7F0041BE080B7F004180080B7F0041C088040B7F0041"
"000B7F0041010B07080104686F6F6B00030AC3800001BF800001017F230041"
"106B220124002001200036020C41940841154180084114410010001A41AA08"
"4114420A10011A41012200200010021A200141106A240042000B0B44010041"
"80080B3D526F6C6C6261636B2E633A2043616C6C65642E0022526F6C6C6261"
"636B2E633A2043616C6C65642E2200526F6C6C6261636B3A20526F6C6C6261"
"636B21";
std::string ns_str =
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECA"
"FE";
Json::Value jv =
ripple::test::jtx::hook(issuer, {{hso(createCodeHex)}}, 0);
jv[jss::Hooks][0U][jss::Hook][jss::HookNamespace] = ns_str;
jv[jss::Hooks][0U][jss::Hook][jss::HookOn] =
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFFFFFBFFF"
"FF";
env(jv, fee(1'000'000));
env.close();
auto const preAlice = env.balance(alice);
auto const preCoins = env.current()->info().drops;
auto tx = import(alice, loadXpop(ImportTCAccountSet::w_seed));
tx[sfIssuer.jsonName] = issuer.human();
env(tx, fee(1'000'000), ter(tecHOOK_REJECTED));
env.close();
// confirm fee was burned but no mint
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice - XRP(1));
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins - XRP(1));
// resubmit import without issuer - no trigger hook
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
ter(tesSUCCESS));
env.close();
// total burn = (burn drops + burn fee drops) - fee drops
auto const totalBurn = XRP(1000) - (feeDrops * 10);
// confirm fee was minted
auto const postAlice2 = env.balance(alice);
BEAST_EXPECT(postAlice2 == postAlice + totalBurn);
// confirm total coins header
auto const postCoins2 = env.current()->info().drops;
BEAST_EXPECT(postCoins2 == postCoins + totalBurn);
env.close();
}
}
void
testAccountDelete(FeatureBitset features)
{
testcase("account delete");
using namespace test::jtx;
using namespace std::literals;
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
auto const bob = Account("bob");
env.fund(XRP(1000), alice, bob);
env.close();
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
Json::Value const xpop = loadXpop(ImportTCAccountSet::w_seed);
Json::Value const tx = import(alice, xpop);
env(tx, fee(feeDrops * 10), ter(tesSUCCESS));
// Close enough ledgers to be able to delete alices's account.
incLgrSeqForAccDel(env, alice);
// alice cannot delete account after import
auto const acctDelFee{drops(env.current()->fees().increment)};
env(acctdelete(alice, bob),
fee(acctDelFee),
ter(tecHAS_OBLIGATIONS));
}
}
void
testImportSequence(FeatureBitset features)
{
testcase("import sequence");
using namespace test::jtx;
using namespace std::literals;
// test bad IMPORT_VL_KEYS no UNLReport
{
std::vector<std::string> const badVLKeys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC2"};
test::jtx::Env env{*this, makeNetworkVLConfig(21337, badVLKeys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
auto preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
ter(telIMPORT_VL_KEY_NOT_RECOGNISED));
env.close();
}
// test good IMPORT_VL_KEYS no UNLReport
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
auto preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
ter(tesSUCCESS));
env.close();
}
// test no IMPORT_VL_KEYS has UNLReport
{
test::jtx::Env env{*this, makeNetworkConfig(21337)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const burnCoins = env.current()->info().drops;
BEAST_EXPECT(burnCoins == initCoins - 10'000'000'000);
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
auto preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(1000));
// ADD UNL REPORT
std::vector<std::string> const _ivlKeys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC1",
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC2",
};
std::vector<PublicKey> ivlKeys;
for (auto const& strPk : _ivlKeys)
{
auto pkHex = strUnHex(strPk);
ivlKeys.emplace_back(makeSlice(*pkHex));
}
std::vector<std::string> const _vlKeys = {
"ED8E43A943A174190BA2FAE91F44AC6E2D1D8202EFDCC2EA3DBB39814576D6"
"90F7",
"ED45D1840EE724BE327ABE9146503D5848EFD5F38B6D5FEDE71E80ACCE5E6E"
"738B"};
std::vector<PublicKey> vlKeys;
for (auto const& strPk : _vlKeys)
{
auto pkHex = strUnHex(strPk);
vlKeys.emplace_back(makeSlice(*pkHex));
}
// insert a ttUNL_REPORT pseudo into the open ledger
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j) -> bool {
STTx tx = createUNLReportTx(
env.current()->seq() + 1, ivlKeys[0], vlKeys[0]);
uint256 txID = tx.getTransactionID();
auto s = std::make_shared<ripple::Serializer>();
tx.add(*s);
env.app().getHashRouter().setFlags(txID, SF_PRIVATE2);
view.rawTxInsert(txID, std::move(s), nullptr);
return true;
});
// close the ledger
env.close();
BEAST_EXPECT(hasUNLReport(env) == true);
// Test Import
env(import(alice, loadXpop(ImportTCAccountSet::w_seed)),
fee(feeDrops * 10),
ter(tesSUCCESS));
env.close();
}
}
void
testMaxSupply(FeatureBitset features)
{
testcase("max supply");
using namespace test::jtx;
using namespace std::literals;
// burn 100'000 coins
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == 100'000'000'000'000'000);
// 100'000'000'000'000'000 - drops
// 100'000'000'000 - xrp
// burn 100,000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(100'000'000'000), ter(tesSUCCESS));
env.close();
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == envCoins - drops(100'000'000'000));
auto const alice = Account("alice");
env.memoize(alice);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
STAmount burnFee = XRP(1000) + XRP(2);
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + burnFee);
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + burnFee);
}
// burn all coins
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == 100'000'000'000'000'000);
// burn all but 1,000 xrp
auto const master = Account("masterpassphrase");
env(noop(master),
fee(envCoins - drops(1'000'000'000)),
ter(tesSUCCESS));
env.close();
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == XRP(1000));
auto const alice = Account("alice");
env.memoize(alice);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
STAmount burnFee = XRP(1000) + XRP(2);
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + burnFee);
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + burnFee);
}
// burn no coins
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const envCoins = env.current()->info().drops;
BEAST_EXPECT(envCoins == 100'000'000'000'000'000);
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == XRP(100'000'000'000));
auto const alice = Account("alice");
env.memoize(alice);
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
STAmount burnFee = XRP(1000) + XRP(2);
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tefINTERNAL));
env.close();
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice);
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins);
}
}
void
testMinMax(FeatureBitset features)
{
testcase("min max");
using namespace test::jtx;
using namespace std::literals;
// w/ seed -> dne (min)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::min);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + reward amount
auto const totalBurn = drops(10) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ seed -> dne (min) (Reserve/Reference/Fee)
// Owner Fee = 20 xrp
{
test::jtx::Env env{*this, makeMaxFeeConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == initCoins - 10'000'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::min);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + reward amount
auto const totalBurn = drops(10) + XRP(20);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
// w/ seed -> dne (max)
{
test::jtx::Env env{*this, makeNetworkVLConfig(21337, keys)};
auto const feeDrops = env.current()->fees().base;
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 100'000'000'000'000'000);
// burn 99'999'998'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(99'999'998'000'000'000), ter(tesSUCCESS));
env.close();
// confirm total coins header
auto const preCoins = env.current()->info().drops;
BEAST_EXPECT(preCoins == initCoins - 99'999'998'000'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::max);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + reward amount
auto const totalBurn = drops(99'999'939'799'000'000) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + totalBurn);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == preCoins + totalBurn);
// confirm account exists
auto const [acct, acctSle] =
accountKeyAndSle(*env.current(), alice);
BEAST_EXPECT(acctSle != nullptr);
env(noop(alice), fee(feeDrops), ter(tesSUCCESS));
}
}
std::unique_ptr<Config>
makeGenesisConfig(
FeatureBitset features,
uint32_t networkID,
std::string fee,
std::string a_res,
std::string o_res,
uint32_t ledgerID)
{
using namespace jtx;
// IMPORT VL KEY
std::vector<std::string> const keys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A"
"5DCE5F86D11FDA1874481CE9D5A1CDC1"};
Json::Value jsonValue;
Json::Reader reader;
reader.parse(ImportTCHalving::base_genesis, jsonValue);
foreachFeature(features, [&](uint256 const& feature) {
std::string featureName = featureToName(feature);
std::optional<uint256> featureHash =
getRegisteredFeature(featureName);
if (featureHash.has_value())
{
std::string hashString = to_string(featureHash.value());
jsonValue["ledger"]["accountState"][1]["Amendments"].append(
hashString);
}
});
jsonValue["ledger_current_index"] = ledgerID;
jsonValue["ledger"]["ledger_index"] = to_string(ledgerID);
jsonValue["ledger"]["seqNum"] = to_string(ledgerID);
return envconfig([&](std::unique_ptr<Config> cfg) {
cfg->NETWORK_ID = networkID;
cfg->START_LEDGER = jsonValue.toStyledString();
cfg->START_UP = Config::LOAD_JSON;
Section config;
config.append(
{"reference_fee = " + fee,
"account_reserve = " + a_res,
"owner_reserve = " + o_res});
auto setup = setup_FeeVote(config);
cfg->FEES = setup;
for (auto const& strPk : keys)
{
auto pkHex = strUnHex(strPk);
if (!pkHex)
Throw<std::runtime_error>(
"Import VL Key '" + strPk + "' was not valid hex.");
auto const pkType = publicKeyType(makeSlice(*pkHex));
if (!pkType)
Throw<std::runtime_error>(
"Import VL Key '" + strPk +
"' was not a valid key type.");
cfg->IMPORT_VL_KEYS.emplace(strPk, makeSlice(*pkHex));
}
return cfg;
});
}
void
testHalving(FeatureBitset features)
{
testcase("halving");
using namespace test::jtx;
using namespace std::literals;
// Halving @ ledger seq 1'999'999
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 1999998),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 1'999'999);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = XRP(1'000) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 2'000'000
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 1999998),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
env.close();
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 2'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = XRP(1'000) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 2'000'001
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 1999998),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
env.close();
env.close();
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 2'000'001);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = drops(999999964) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 5'000'000
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 4999999),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 5'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = drops(892857142) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 20'000'000
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 19999999),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 20'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = drops(357142857) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 29'999'998
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 29999998),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 29'999'999);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = drops(35) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 30'000'000
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 29999998),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
env.close();
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 30'000'000);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = drops(0) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
// Halving @ ledger seq 50'000'001
{
test::jtx::Env env{
*this,
makeGenesisConfig(
features, 21337, "10", "1000000", "200000", 50000000),
features};
// confirm total coins header
auto const initCoins = env.current()->info().drops;
BEAST_EXPECT(initCoins == 0);
auto const initSeq = env.current()->info().seq;
BEAST_EXPECT(initSeq == 50'000'001);
// init env
auto const alice = Account("alice");
env.memoize(alice);
// confirm env
auto const preAlice = env.balance(alice);
BEAST_EXPECT(preAlice == XRP(0));
// import tx
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
Json::Value tx = import(alice, xpopJson);
tx[jss::Sequence] = 0;
tx[jss::Fee] = 0;
env(tx, alice, ter(tesSUCCESS));
env.close();
// total burn = burn drops + Init Reward
auto const creditDrops = drops(0) + XRP(2);
// confirm fee was minted
auto const postAlice = env.balance(alice);
BEAST_EXPECT(postAlice == preAlice + creditDrops);
// confirm total coins header
auto const postCoins = env.current()->info().drops;
BEAST_EXPECT(postCoins == initCoins + creditDrops);
}
}
void
testRPCFee(FeatureBitset features)
{
testcase("rpc fee");
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, makeNetworkConfig(21337)};
auto const feeDrops = env.current()->fees().base;
auto const alice = Account("alice");
env.fund(XRP(1000), alice);
env.close();
// build tx_blob
Json::Value params;
auto const xpopJson = loadXpop(ImportTCAccountSet::w_seed);
auto tx = env.jt(import(alice, xpopJson));
params[jss::tx_blob] = strHex(tx.stx->getSerializer().slice());
// fee request
auto const jrr = env.rpc("json", "fee", to_string(params));
// verify hooks fee
auto const hooksFee = jrr[jss::result][jss::fee_hooks_feeunits];
BEAST_EXPECT(hooksFee == to_string(feeDrops * 10));
// verify open ledger fee
auto const dropsJV = jrr[jss::result][jss::drops];
auto const openLedgerFee = dropsJV[jss::open_ledger_fee];
BEAST_EXPECT(openLedgerFee == to_string((feeDrops * 10) + feeDrops));
}
public:
void
run() override
{
using namespace test::jtx;
FeatureBitset const all{supported_amendments()};
testWithFeats(all);
}
void
testWithFeats(FeatureBitset features)
{
// testComputeStartingBalance(features);
// testIsHex(features);
// testIsBase58(features);
// testIsBase64(features);
// testParseUint64(features);
// testSyntaxCheckProofObject(features);
// testSyntaxCheckXPOP(features);
// testGetVLInfo(features);
// testEnabled(features);
// testInvalidPreflight(features);
// testInvalidPreclaim(features);
// testInvalidDoApply(features);
// testAccountSet(features);
// testAccountSetFlags(features);
// testSetRegularKey(features);
// testSetRegularKeyFlags(features);
// testSignersListSet(features);
testAccountIndex(features);
// testHookIssuer(features);
// testImportSequence(features);
// testAccountDelete(features);
// testMaxSupply(features);
// testMinMax(features);
// testHalving(features - featureOwnerPaysFee);
// testRPCFee(features);
}
};
BEAST_DEFINE_TESTSUITE(Import, app, ripple);
} // namespace test
} // namespace ripple