//------------------------------------------------------------------------------
/*
    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 <test/app/Import_json.h>
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/AmendmentTable.h>
#include <xrpld/app/misc/HashRouter.h>
#include <xrpld/app/tx/detail/Import.h>
#include <xrpld/core/ConfigSections.h>
#include <xrpld/ledger/Dir.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Import.h>
#include <xrpl/protocol/jss.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"};

    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 std::uint32_t
    importVLSequence(jtx::Env const& env, PublicKey const& pk)
    {
        auto const sle = env.le(keylet::import_vlseq(pk));
        if (sle && sle->isFieldPresent(sfImportSequence))
            return (*sle)[sfImportSequence];
        return 0;
    }

    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, network::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");
        // 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)
    {
        testcase("import utils - syntaxCheckProof: is array");

        using namespace test::jtx;
        using namespace std::literals;

        test::jtx::Env env{*this, network::makeNetworkVLConfig(21337, keys)};

        auto const alice = Account("alice");
        env.fund(XRP(1000), alice);
        env.close();

        // XPOP.transaction.proof list xpop is disabled
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::proof] = Json::arrayValue;
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // // XPOP.transaction.proof list should be exactly 16 entries
        //  {
        //     Json::Value tmpXpop =
        //     import::loadXpop(ImportTCAccountSet::w_seed);
        //     tmpXpop[jss::transaction][jss::proof] = Json::arrayValue;
        //     for(int i = 0; i < 16; i++) {
        //         tmpXpop[jss::transaction][jss::proof].append("E000CC0736630D66DAB573A2642E1BD0646DFB13A871B2CCFA6E4226F477D88C");
        //     }
        //     Json::Value const tx = import::import(alice, tmpXpop);
        //     env(tx, ter(temMALFORMED));
        // }
        //  // XPOP.transaction.proof list entry missing or wrong format (should
        //  be hex string with 64 characters)
        //  {
        //     Json::Value tmpXpop =
        //     import::loadXpop(ImportTCAccountSet::w_seed);
        //     tmpXpop[jss::transaction][jss::proof] = Json::arrayValue;
        //     for(int i = 0; i < 16; i++) {
        //         tmpXpop[jss::transaction][jss::proof].append("wrong format");
        //     }
        //     Json::Value const tx = import::import(alice, tmpXpop);
        //     env(tx, ter(temMALFORMED));
        // }
        //  // XPOP.transaction.proof list entry has wrong format
        //  {
        //     Json::Value tmpXpop =
        //     import::loadXpop(ImportTCAccountSet::w_seed);
        //     tmpXpop[jss::transaction][jss::proof] = Json::arrayValue;
        //     Json::Value child1;
        //     child1["children"] = Json::arrayValue;
        //     child1["hash"] =
        //     "31C2D27644F0E6D81AF332F466F71D43C25946A45FE43F121A500E22003F39A4";
        //     child1["key"] =
        //     "60748F98318DB6A39737D0C1BE5614AEBD7F1ACEE5FB16E82D49F16BB13BA87F";
        //     tmpXpop[jss::transaction][jss::proof].append(child1);
        //     Json::Value const tx = import::import(alice, tmpXpop);
        //     env(tx, ter(temMALFORMED));
        //  }
    }

    void
    testSyntaxCheckProofObject(FeatureBitset features)
    {
        testcase("import utils - syntaxCheckProof: is object");

        using namespace test::jtx;
        using namespace std::literals;

        test::jtx::Env env{*this, network::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, network::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.index missing or wrong format
        // invalid xpop.ledger.index (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",
                    "index": "1"
                },
                "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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,
                    "index": 1,
                    "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, network::makeNetworkVLConfig(21337, keys)};

        std::string strJson = R"json({
            "ledger": {
                "acroot": "DCE36DCACDCFCB3441866E09A183B7B8064B3F5E06593CD6AA8ACCC1B284B477",
                "txroot": "409C8D073DDB5AB07FD2CD4F14467A8F3BC8FFBA16A0032D12D823D8511C12F4",
                "phash": "BAB19E13B25251A83493073F424FD986EA7BA49F9F4C83A061700131460D747D",
                "close": 738786851,
                "coins": "99999998999999868",
                "cres": 10,
                "flags": 10,
                "index": 1,
                "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, network::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::import(
                    alice, import::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,
                network::makeNetworkVLConfig(21337, keys),
                features - featureImport};

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                ter(temDISABLED));
        }

        // temDISABLED - disabled
        // !ctx.rules.enabled(featureHooksUpdate1) &&
        // ctx.tx.isFieldPresent(sfIssuer)
        {
            test::jtx::Env env{
                *this,
                network::makeNetworkVLConfig(21337, keys),
                features - featureHooksUpdate1};

            auto const alice = Account("alice");
            auto const issuer = Account("issuer");
            env.fund(XRP(1000), alice, issuer);
            env.close();

            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                import::issuer(issuer),
                ter(temDISABLED));
        }

        // temMALFORMED - Import: xpop did not contain an 80% quorum for the txn
        // it purports to prove.
        for (bool const withXahauV1 : {true, false})
        {
            auto const amend = withXahauV1 ? features : features - fixXahauV1;
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys), amend};

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value valData = tmpXpop[jss::validation][jss::data];
            valData["n9MNStvsHZxrCYnShytCfKMCXBcq8wJjR6fLTS7L2Q8qoFVNioQ8"] =
                "";
            valData["n9MTTavZe6EPqqyQ27pJbNWpfHw8ZNspVgznrwx5HWm7cdTjKQie"] =
                "";
            if (withXahauV1)
            {
                valData
                    ["n94J5LCRu9bBWrJiwRWujvuVECVPWbXFcQ2VN38qLD378F5pDSDM"] =
                        "";
            }
            tmpXpop[jss::validation][jss::data] = valData;
            auto const txResult =
                withXahauV1 ? ter(temMALFORMED) : ter(temMALFORMED);
            env(import::import(alice, tmpXpop), txResult);
        }

        test::jtx::Env env{*this, network::makeNetworkVLConfig(21337, keys)};

        auto const feeDrops = env.current()->fees().base;

        // 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");
        auto const carol = Account("carol");
        auto const dave = Account("dave");

        env.fund(XRP(1000), alice, bob, carol, dave);
        env.close();

        // temMALFORMED
        // Issuer cannot be the source account.
        {
            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                import::issuer(alice),
                ter(temMALFORMED));
        }

        // telINSUF_FEE_P - sfFee cannot be 0
        {
            Json::Value tx = import::import(
                alice, import::loadXpop(ImportTCAccountSet::w_seed));
            STAmount const& fee = XRP(0);
            tx[jss::Fee] = fee.getJson(JsonOptions::none);
            env(tx, ter(telINSUF_FEE_P));
        }

        // temMALFORMED - blob was more than 512kib
        // DA: blob.resize(513 * 1024); fails with Boost
        // {
        //     ripple::Blob blob;
        //     blob.resize(513 * 1024);
        //     Json::Value tx = import::import(alice,
        //     import::loadXpop(ImportTCAccountSet::w_seed));
        //     tx[sfBlob.jsonName] = strHex(blob); env(tx, ter(temMALFORMED));
        // }

        // temMALFORMED - sfAmount field must be in drops
        {
            Json::Value tx = import::import(
                alice, import::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 = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation] = {};  // one of many ways to throw error
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validation.unl.public_key was not valid hex
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::public_key] = "not a hex";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validation.unl.public_key was not a recognised
        // public key type
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::public_key] =
                "0084D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
                "CDC1";
            env(import::import(alice, tmpXpop), 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 = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] = "DEADBEEF";
            tmpXpop[jss::transaction][jss::meta] = "DEADBEEF";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - !stpTrans
        // DA: Duplicate - getInnerTxn (Any Failure)

        // temMALFORMED - Import: attempted to import xpop containing an emitted
        // or pseudo txn.
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "12000322000000002400000002201B00000069201D0000535968400000003B"
                "9ACA0073210388935426E0D08083314842EDFBB2D517BD47699F9A4527318A"
                "8E10468C97C0527446304402200E40CA821A7BFE347448FFA6540F67C7FFBF"
                "0287756D324DFBADCEDE2B23782C02207BE1B1294F1A1D5AC21990740B78F9"
                "C0693431237D6E07FE84228082986E50FF8114AE123A8556F3CF9115471137"
                "6AFB0F894F832B3DED202E000000013D00000000000000015B2200676F4B9B"
                "45C5ADE9DE3C8CD01200CB12DFF8C792220DB088FE615BE5C2905C207064D8"
                "1954F6A7225A8BAADF5A3042016BFB87355D1D0AFEDBAA8FB22F98355D745F"
                "398EEE9E6B294BBE6A5681A31A6107243D19384E277B5A7B1F23B8C83DE78A"
                "14AE123A8556F3CF91154711376AFB0F894F832B3DE1";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: inner txn lacked transaction result
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::meta] =
                "201C00000006F8E5110061250000005655463E39A6AFDDA77DBF3591BF3C2A"
                "4BE9BB8D9113BF6D0797EB403C3D0D894FEF5692FA6A9FC8EA6018D5D16532"
                "D7795C91BFB0831355BDFDA177E86C8BF997985FE624000000026240000000"
                "773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481"
                "14AE123A8556F3CF91154711376AFB0F894F832B3DE1E1F1";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: inner txn did not have a tesSUCCESS or tec
        // result
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::meta] =
                "201C00000006F8E5110061250000005655463E39A6AFDDA77DBF3591BF3C2A"
                "4BE9BB8D9113BF6D0797EB403C3D0D894FEF5692FA6A9FC8EA6018D5D16532"
                "D7795C91BFB0831355BDFDA177E86C8BF997985FE624000000026240000000"
                "773593F4E1E7220000000024000000032D0000000162400000003B9AC9F481"
                "14AE123A8556F3CF91154711376AFB0F894F832B3DE1E1F103103C";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: import and txn inside xpop must be signed by
        // the same account
        {
            Json::Value const tmpXpop =
                import::loadXpop(ImportTCAccountSet::w_seed);
            env(import::import(bob, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: attempted to import xpop containing a txn with
        // a sfNetworkID field.
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "120003210000535922000000002400000002201B00000069201D0000535968"
                "400000003B9ACA0073210388935426E0D08083314842EDFBB2D517BD47699F"
                "9A4527318A8E10468C97C0527446304402200E40CA821A7BFE347448FFA654"
                "0F67C7FFBF0287756D324DFBADCEDE2B23782C02207BE1B1294F1A1D5AC219"
                "90740B78F9C0693431237D6E07FE84228082986E50FF8114AE123A8556F3CF"
                "91154711376AFB0F894F832B3D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: OperationLimit missing from inner xpop txn.
        // outer txid:
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "12000322000000002400000002201B0000006C68400000003B9ACA007321ED"
                "A8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C"
                "747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F"
                "232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C51"
                "77C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: Wrong network ID for OperationLimit in inner
        // txn. outer txid:
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "12000322000000002400000002201B0000006C201D0000535A68400000003B"
                "9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62"
                "ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593EC"
                "C21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF"
                "3EC206AD3C5177C519560F8114AE123A8556F3CF91154711376AFB0F894F83"
                "2B3D";
            env(import::import(alice, tmpXpop), ter(telWRONG_NETWORK));
        }

        // temMALFORMED - Import: inner txn must be an AccountSet, SetRegularKey
        // or SignerListSet transaction.
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "12006322000000002400000002201B0000006C201D0000535968400000003B"
                "9ACA007321EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62"
                "ECAD0AE4A96C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593EC"
                "C21B3C79EF0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF"
                "3EC206AD3C5177C519560F8114AE123A8556F3CF91154711376AFB0F894F83"
                "2B3D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: outer and inner txns were (multi) signed with
        // different keys.
        {
            auto const xpopJson =
                import::loadXpop(ImportTCSignersListSet::w_signers);
            env(import::import(alice, xpopJson),
                msig(bob, dave),
                fee((3 * feeDrops) * 10),
                ter(temMALFORMED));
            env.close();
        }

        // temMALFORMED - Import: outer and inner txns were (multi) signed with
        // different keys. - empty innerSigners
        {
            Json::Value xpopJson =
                import::loadXpop(ImportTCSignersListSet::w_signers);
            xpopJson[jss::transaction][jss::blob] =
                "12000C22000000002400000014201B0000002B201D00005359202300000002"
                "6840000000001E84B073008114AE123A8556F3CF91154711376AFB0F894F83"
                "2B3DF3F1F4EB1300018114AA266540F7DACC27E264B75ED0A5ED7330BFB614"
                "E1EB1300018114D91B8EE5C7ABF632469D4C0907C5E40C8B8F79B3E1F1";
            env(import::import(alice, xpopJson),
                msig(bob, carol),
                fee((3 * feeDrops) * 10),
                ter(temMALFORMED));
            env.close();
        }

        // temMALFORMED - Import: outer or inner txn was missing signers.
        // different keys.
        {
            Json::Value xpopJson =
                import::loadXpop(ImportTCSignersListSet::w_signers);
            xpopJson[jss::transaction][jss::blob] =
                "12000C22000000002400000014201B0000002B201D00005359202300000002"
                "6840000000001E84B073008114AE123A8556F3CF91154711376AFB0F894F83"
                "2B3DF4EB1300018114AA266540F7DACC27E264B75ED0A5ED7330BFB614E1EB"
                "1300018114D91B8EE5C7ABF632469D4C0907C5E40C8B8F79B3E1F1";
            env(import::import(alice, xpopJson),
                fee((3 * feeDrops) * 10),
                ter(temMALFORMED));
            env.close();
        }

        // temMALFORMED - Import: outer and inner txns were signed with
        // different keys.
        {
            auto const xpopJson =
                import::loadXpop(ImportTCSetRegularKey::w_regular_key);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                sig(carol),
                ter(temMALFORMED));
        }

        // temMALFORMED - Import: inner txn signature verify failed
        {
            Json::Value xpopJson =
                import::loadXpop(ImportTCSignersListSet::w_signers);
            xpopJson[jss::transaction][jss::blob] =
                "12000C2200000008240000001A201B000003B9201D00005359202300000000"
                "6840000000001E84B073008114AE123A8556F3CF91154711376AFB0F894F83"
                "2B3DF3E0107321028949021029D5CC87E78BCF053AFEC0CAFD15108EC119EA"
                "AFEC466F5C095407BF74473045022100BE132424E6E6304574F4BB5F6E287A"
                "63429482CE7E0437B443FC1457F4A3830002204B909A44FF9DFFD99AF751B0"
                "40830E53830FE3CA591449600372BED85FDBC33C8114B389FBCED0AF9DCDFF"
                "62900BFAEFA3EB872D8A96E1E010732102691AC5AE1C4C333AE5DF8A93BDC4"
                "95F0EEBFC6DB0DA7EB6EF808F3AFC006E3FE74473045022100D235706A0AB2"
                "407EC5D62F9130E9A98A70D90CE0D69D749C37272DFC975BD79002207D8AED"
                "0BF31A5E3290920B72EAFE22AB6B09814466372E09B067779C4E103FBE8114"
                "F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B90FE1F1";
            env(import::import(alice, xpopJson),
                msig(bob, carol),
                fee((3 * feeDrops) * 10),
                ter(temMALFORMED));
            env.close();
        }

        // temMALFORMED - Import: failed to deserialize manifest on txid
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::manifest] = "YmFkSnNvbg==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: manifest master key did not match top level
        // master key in unl section of xpop
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::manifest] =
                "JAAAAAFxIe2E1ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+"
                "b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338"
                "jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/"
                "ikfgf9SZOlOGcBcBJAw44PLjH+"
                "HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+"
                "IILJIiIKU/+1Uxx0FRpQbMDA==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: manifest signature invalid
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::manifest] =
                "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+"
                "b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkA3UjfY5zOEkhq31tU4338"
                "jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/"
                "ikfgf9SZOlOGcBcBJAw34PLjH+"
                "HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+"
                "IILJIiIKU/+1Uxx0FRpQbMDA==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob not signed correctly
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::signature] =
                "949F6B8DA6E11C213B561659C16F13D35385E8EA9E775483ADC84578F6D578"
                "943DE5EB681584B2C03EFFFDFD216F9E0B21576E482F941C7195893B72B5B1"
                "F70D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob not signed correctly
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::signature] = "not a hex";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob was not valid json (after base64
        // decoding)
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob json (after base64 decoding) lacked
        // required field (sequence) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJleHBpcmF0aW9uIjo3NDEzOTg0MDAsInZhbGlkYXRvcnMiOlt7InZhbGlkYX"
                "Rpb25fcHVibGljX2tleSI6IkVEMzhCRDQ0NUFGRDYyMTU5NjIwQ0MxOTZDMjY2"
                "OEEyNkI2RkJCMzZCMDk5RUI1NUIzOEE1OEMxMUMxMjA0REU1QyIsIm1hbmlmZX"
                "N0IjoiSkFBQUFBSnhJZTA0dlVSYS9XSVZsaURNR1d3bWFLSnJiN3Myc0puclZi"
                "T0tXTUVjRWdUZVhITWhBb0dNTmNzZ1VVTndLbytoN3poYVhLRitIR3c2WGhFam"
                "9ES3JhbGtZbk1qS2RrY3dSUUloQUo3Y040SjZOZFZmcG52RUkvWmV1V0NUdm5w"
                "YUppcktORmNDM3pNT2pnd2pBaUFiS0kwZmJYZ1MxUkxsb05oeGRIaFZxOW96RV"
                "dWRTljSXdYRE4zQXhxeVkzQVNRQ3QwK3UvaU5TREQ2Ylh2VVR0ZG10NE5ydGxi"
                "eDRWenVtVHBmalJZcDRsTW9JL2g0M3BVVGpwN1ZGb1hibktXalZocU5hR201Nz"
                "dLNko2OTdYWjdUUUU9In0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFREJF"
                "RTMwRkFFOTJFRUU4OEUxQzQ5ODBEMDlFQ0ZERTk5QTExNkQwNzhFQzIxODU3RE"
                "IxQjQ3QjQyNjQxOEU0MjgiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUwNHZVUmEv"
                "V0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2"
                "dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFK"
                "N2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYk"
                "tJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0"
                "MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oND"
                "NwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9XX0=";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // temMALFORMED - Import: unl blob json (after base64 decoding) wrong
        // required field (sequence) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6IjIiLCJleHBpcmF0aW9uIjo3NDEzOTg0MDAsInZhbGlkYX"
                "RvcnMiOlt7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEMzhCRDQ0NUFGRDYy"
                "MTU5NjIwQ0MxOTZDMjY2OEEyNkI2RkJCMzZCMDk5RUI1NUIzOEE1OEMxMUMxMj"
                "A0REU1QyIsIm1hbmlmZXN0IjoiSkFBQUFBSnhJZTA0dlVSYS9XSVZsaURNR1d3"
                "bWFLSnJiN3Myc0puclZiT0tXTUVjRWdUZVhITWhBb0dNTmNzZ1VVTndLbytoN3"
                "poYVhLRitIR3c2WGhFam9ES3JhbGtZbk1qS2RrY3dSUUloQUo3Y040SjZOZFZm"
                "cG52RUkvWmV1V0NUdm5wYUppcktORmNDM3pNT2pnd2pBaUFiS0kwZmJYZ1MxUk"
                "xsb05oeGRIaFZxOW96RVdWRTljSXdYRE4zQXhxeVkzQVNRQ3QwK3UvaU5TREQ2"
                "Ylh2VVR0ZG10NE5ydGxieDRWenVtVHBmalJZcDRsTW9JL2g0M3BVVGpwN1ZGb1"
                "hibktXalZocU5hR201NzdLNko2OTdYWjdUUUU9In0seyJ2YWxpZGF0aW9uX3B1"
                "YmxpY19rZXkiOiJFREJFRTMwRkFFOTJFRUU4OEUxQzQ5ODBEMDlFQ0ZERTk5QT"
                "ExNkQwNzhFQzIxODU3REIxQjQ3QjQyNjQxOEU0MjgiLCJtYW5pZmVzdCI6IkpB"
                "QUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0"
                "VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxr"
                "WW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTk"
                "ZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3"
                "WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bV"
                "RwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3"
                "WFo3VFFFPSJ9XX0=";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // temMALFORMED - Import: unl blob json (after base64 decoding)
        // lacked required field (expiration) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwidmFsaWRhdG9ycyI6W3sidmFsaWRhdGlvbl9wdWJsaW"
                "Nfa2V5IjoiRUQzOEJENDQ1QUZENjIxNTk2MjBDQzE5NkMyNjY4QTI2QjZGQkIz"
                "NkIwOTlFQjU1QjM4QTU4QzExQzEyMDRERTVDIiwibWFuaWZlc3QiOiJKQUFBQU"
                "FKeEllMDR2VVJhL1dJVmxpRE1HV3dtYUtKcmI3czJzSm5yVmJPS1dNRWNFZ1Rl"
                "WEhNaEFvR01OY3NnVVVOd0tvK2g3emhhWEtGK0hHdzZYaEVqb0RLcmFsa1luTW"
                "pLZGtjd1JRSWhBSjdjTjRKNk5kVmZwbnZFSS9aZXVXQ1R2bnBhSmlyS05GY0Mz"
                "ek1Pamd3akFpQWJLSTBmYlhnUzFSTGxvTmh4ZEhoVnE5b3pFV1ZFOWNJd1hETj"
                "NBeHF5WTNBU1FDdDArdS9pTlNERDZiWHZVVHRkbXQ0TnJ0bGJ4NFZ6dW1UcGZq"
                "UllwNGxNb0kvaDQzcFVUanA3VkZvWGJuS1dqVmhxTmFHbTU3N0s2SjY5N1haN1"
                "RRRT0ifSx7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEQkVFMzBGQUU5MkVF"
                "RTg4RTFDNDk4MEQwOUVDRkRFOTlBMTE2RDA3OEVDMjE4NTdEQjFCNDdCNDI2ND"
                "E4RTQyOCIsIm1hbmlmZXN0IjoiSkFBQUFBSnhJZTA0dlVSYS9XSVZsaURNR1d3"
                "bWFLSnJiN3Myc0puclZiT0tXTUVjRWdUZVhITWhBb0dNTmNzZ1VVTndLbytoN3"
                "poYVhLRitIR3c2WGhFam9ES3JhbGtZbk1qS2RrY3dSUUloQUo3Y040SjZOZFZm"
                "cG52RUkvWmV1V0NUdm5wYUppcktORmNDM3pNT2pnd2pBaUFiS0kwZmJYZ1MxUk"
                "xsb05oeGRIaFZxOW96RVdWRTljSXdYRE4zQXhxeVkzQVNRQ3QwK3UvaU5TREQ2"
                "Ylh2VVR0ZG10NE5ydGxieDRWenVtVHBmalJZcDRsTW9JL2g0M3BVVGpwN1ZGb1"
                "hibktXalZocU5hR201NzdLNko2OTdYWjdUUUU9In1dfQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // temMALFORMED - Import: unl blob json (after base64 decoding) wrong
        // required field (expiration) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6Ijc0MTM5ODQwMCIsInZhbGlkYX"
                "RvcnMiOlt7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEMzhCRDQ0NUFGRDYy"
                "MTU5NjIwQ0MxOTZDMjY2OEEyNkI2RkJCMzZCMDk5RUI1NUIzOEE1OEMxMUMxMj"
                "A0REU1QyIsIm1hbmlmZXN0IjoiSkFBQUFBSnhJZTA0dlVSYS9XSVZsaURNR1d3"
                "bWFLSnJiN3Myc0puclZiT0tXTUVjRWdUZVhITWhBb0dNTmNzZ1VVTndLbytoN3"
                "poYVhLRitIR3c2WGhFam9ES3JhbGtZbk1qS2RrY3dSUUloQUo3Y040SjZOZFZm"
                "cG52RUkvWmV1V0NUdm5wYUppcktORmNDM3pNT2pnd2pBaUFiS0kwZmJYZ1MxUk"
                "xsb05oeGRIaFZxOW96RVdWRTljSXdYRE4zQXhxeVkzQVNRQ3QwK3UvaU5TREQ2"
                "Ylh2VVR0ZG10NE5ydGxieDRWenVtVHBmalJZcDRsTW9JL2g0M3BVVGpwN1ZGb1"
                "hibktXalZocU5hR201NzdLNko2OTdYWjdUUUU9In0seyJ2YWxpZGF0aW9uX3B1"
                "YmxpY19rZXkiOiJFREJFRTMwRkFFOTJFRUU4OEUxQzQ5ODBEMDlFQ0ZERTk5QT"
                "ExNkQwNzhFQzIxODU3REIxQjQ3QjQyNjQxOEU0MjgiLCJtYW5pZmVzdCI6IkpB"
                "QUFBQUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0"
                "VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxr"
                "WW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTk"
                "ZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3"
                "WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bV"
                "RwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3"
                "WFo3VFFFPSJ9XX0=";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // temMALFORMED - Import: unl blob json (after base64 decoding)
        // lacked required field (effective) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJlZmZlY3Rpdm"
                "UiOiI3NDEzOTg0MDAiLCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1Ymxp"
                "Y19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQj"
                "M2QjA5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6IkpBQUFB"
                "QUp4SWUwNHZVUmEvV0lWbGlETUdXd21hS0pyYjdzMnNKbnJWYk9LV01FY0VnVG"
                "VYSE1oQW9HTU5jc2dVVU53S28raDd6aGFYS0YrSEd3NlhoRWpvREtyYWxrWW5N"
                "aktka2N3UlFJaEFKN2NONEo2TmRWZnBudkVJL1pldVdDVHZucGFKaXJLTkZjQz"
                "N6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG9OaHhkSGhWcTlvekVXVkU5Y0l3WERO"
                "M0F4cXlZM0FTUUN0MCt1L2lOU0RENmJYdlVUdGRtdDROcnRsYng0Vnp1bVRwZm"
                "pSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3"
                "VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURCRUUzMEZBRTkyRU"
                "VFODhFMUM0OTgwRDA5RUNGREU5OUExMTZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0"
                "MThFNDI4IiwibWFuaWZlc3QiOiJKQUFBQUFKeEllMDR2VVJhL1dJVmxpRE1HV3"
                "dtYUtKcmI3czJzSm5yVmJPS1dNRWNFZ1RlWEhNaEFvR01OY3NnVVVOd0tvK2g3"
                "emhhWEtGK0hHdzZYaEVqb0RLcmFsa1luTWpLZGtjd1JRSWhBSjdjTjRKNk5kVm"
                "ZwbnZFSS9aZXVXQ1R2bnBhSmlyS05GY0Mzek1Pamd3akFpQWJLSTBmYlhnUzFS"
                "TGxvTmh4ZEhoVnE5b3pFV1ZFOWNJd1hETjNBeHF5WTNBU1FDdDArdS9pTlNERD"
                "ZiWHZVVHRkbXQ0TnJ0bGJ4NFZ6dW1UcGZqUllwNGxNb0kvaDQzcFVUanA3VkZv"
                "WGJuS1dqVmhxTmFHbTU3N0s2SjY5N1haN1RRRT0ifV19";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // temMALFORMED - Import: unl blob json (after base64 decoding)
        // lacked required field (validators) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwfQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // temMALFORMED - Import: unl blob json (after base64 decoding) wrong
        // required field (validators) and/or types
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjoid3JvbmcifQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob validUntil <= validFrom
        {
            Json::Value tmpXpop = import::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";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob expired
        {
            Json::Value tmpXpop = import::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";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob not yet valid
        {
            Json::Value tmpXpop = import::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";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: depth > 32
        // DA: Impossible test

        // temMALFORMED - Import: !proof->isObject() && !proof->isArray()
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::proof] = "not object";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: return false

        // temMALFORMED - Import : xpop proof did not contain the specified txn
        // hash
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::proof][jss::children]["D"]
                   [jss::children]["7"][jss::hash] =
                       "12D47E7D543E15F1EDBA91CDF335722727851BDDA8C2FF8924772AD"
                       "C6B522A29";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: depth > 32
        // DA: Impossible test

        // temMALFORMED - Import: !proof.isObject() && !proof.isArray()
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::proof] = "not a object";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: computed txroot does not match xpop txroot,
        // invalid xpop.
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::proof][jss::children]["3"]
                   [jss::hash] =
                       "22D47E7D543E15F1EDBA91CDF335722727851BDDA8C2FF8924772AD"
                       "C6B522A29";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: error parsing coins | phash | acroot in the
        // ledger section of XPOP.
        // DA: Duplicate - syntaxCheckXPOP

        // temMALFORMED - Import: unl blob contained invalid validator entry,
        // skipping - not object
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbIndyb25nIiwid3JvbmciXX0=";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob contained invalid validator entry,
        // skipping - no manifest
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1"
                "OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNE"
                "RFNUMifSx7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEQkVFMzBGQUU5MkVF"
                "RTg4RTFDNDk4MEQwOUVDRkRFOTlBMTE2RDA3OEVDMjE4NTdEQjFCNDdCNDI2ND"
                "E4RTQyOCJ9XX0=";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob contained invalid validator entry,
        // skipping - wrong type validation_public_key
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOjEsIm1hbmlmZXN0IjoiSkFB"
                "QUFBSnhJZTA0dlVSYS9XSVZsaURNR1d3bWFLSnJiN3Myc0puclZiT0tXTUVjRW"
                "dUZVhITWhBb0dNTmNzZ1VVTndLbytoN3poYVhLRitIR3c2WGhFam9ES3JhbGtZ"
                "bk1qS2RrY3dSUUloQUo3Y040SjZOZFZmcG52RUkvWmV1V0NUdm5wYUppcktORm"
                "NDM3pNT2pnd2pBaUFiS0kwZmJYZ1MxUkxsb05oeGRIaFZxOW96RVdWRTljSXdY"
                "RE4zQXhxeVkzQVNRQ3QwK3UvaU5TREQ2Ylh2VVR0ZG10NE5ydGxieDRWenVtVH"
                "BmalJZcDRsTW9JL2g0M3BVVGpwN1ZGb1hibktXalZocU5hR201NzdLNko2OTdY"
                "WjdUUUU9In0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOjEsIm1hbmlmZXN0Ij"
                "oiSkFBQUFBSnhJZTIrNHcrdWt1N29qaHhKZ05DZXo5NlpvUmJRZU93aGhYMnh0"
                "SHRDWkJqa0tITWhBOVNtSER4T2lDTVRabDVuUnhycDJ5aldaNWdqeDJEcm9VRn"
                "JVNG5yRXFVN2RrY3dSUUloQUxkRUZlalkrcFVuZ2xpN3NUdmliMEJtREhQN042"
                "aWtWRUJJNkg3SXdVMXpBaUJkc3lvU3FQY0MyTk1xZ0FuSFhIR2RrQUl3QlFEMU"
                "FVZzlYOFpKTHlmY3dIQVNRQ3QxYktWek9NeFJRbVIzd05LNGRLZG9mSUdyeEU5"
                "U2p1TFI2UGE4QjVuMDhTWUo4SzYyZ2UrOWE2QnRaYWxFbS9IT2RjejBOQUZPY3"
                "ljckYvQ3RTQTQ9In1dfQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob contained invalid validator entry,
        // skipping - wrong type
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1"
                "OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNE"
                "RFNUMiLCJtYW5pZmVzdCI6MX0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJF"
                "REJFRTMwRkFFOTJFRUU4OEUxQzQ5ODBEMDlFQ0ZERTk5QTExNkQwNzhFQzIxOD"
                "U3REIxQjQ3QjQyNjQxOEU0MjgiLCJtYW5pZmVzdCI6MX1dfQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob contained an invalid validator key,
        // skipping - invalid format
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRCIsIm1hbmlmZXN0Ijoi"
                "SkFBQUFBSnhJZTA0dlVSYS9XSVZsaURNR1d3bWFLSnJiN3Myc0puclZiT0tXTU"
                "VjRWdUZVhITWhBb0dNTmNzZ1VVTndLbytoN3poYVhLRitIR3c2WGhFam9ES3Jh"
                "bGtZbk1qS2RrY3dSUUloQUo3Y040SjZOZFZmcG52RUkvWmV1V0NUdm5wYUppck"
                "tORmNDM3pNT2pnd2pBaUFiS0kwZmJYZ1MxUkxsb05oeGRIaFZxOW96RVdWRTlj"
                "SXdYRE4zQXhxeVkzQVNRQ3QwK3UvaU5TREQ2Ylh2VVR0ZG10NE5ydGxieDRWen"
                "VtVHBmalJZcDRsTW9JL2g0M3BVVGpwN1ZGb1hibktXalZocU5hR201NzdLNko2"
                "OTdYWjdUUUU9In0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRCIsIm1hbm"
                "lmZXN0IjoiSkFBQUFBSnhJZTIrNHcrdWt1N29qaHhKZ05DZXo5NlpvUmJRZU93"
                "aGhYMnh0SHRDWkJqa0tITWhBOVNtSER4T2lDTVRabDVuUnhycDJ5aldaNWdqeD"
                "JEcm9VRnJVNG5yRXFVN2RrY3dSUUloQUxkRUZlalkrcFVuZ2xpN3NUdmliMEJt"
                "REhQN042aWtWRUJJNkg3SXdVMXpBaUJkc3lvU3FQY0MyTk1xZ0FuSFhIR2RrQU"
                "l3QlFEMUFVZzlYOFpKTHlmY3dIQVNRQ3QxYktWek9NeFJRbVIzd05LNGRLZG9m"
                "SUdyeEU5U2p1TFI2UGE4QjVuMDhTWUo4SzYyZ2UrOWE2QnRaYWxFbS9IT2Rjej"
                "BOQUZPY3ljckYvQ3RTQTQ9In1dfQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob contained an invalid validator key,
        // skipping - missing
        {
            Json::Value tmpXpop = import::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";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob contained an invalid manifest,
        // skipping
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1"
                "OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNE"
                "RFNUMiLCJtYW5pZmVzdCI6Indyb25nIn0seyJ2YWxpZGF0aW9uX3B1YmxpY19r"
                "ZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2Qj"
                "A5OUVCNTVCMzhBNThDMTFDMTIwNERFNUMiLCJtYW5pZmVzdCI6Indyb25nIn1d"
                "fQ==";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob list entry manifest master key did
        // not match master key, skipping
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1"
                "OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNE"
                "RFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQUp4SWUyKzR3K3VrdTdvamh4SmdOQ2V6"
                "OTZab1JiUWVPd2hoWDJ4dEh0Q1pCamtLSE1oQTlTbUhEeE9pQ01UWmw1blJ4cn"
                "AyeWpXWjVnangyRHJvVUZyVTRuckVxVTdka2N3UlFJaEFMZEVGZWpZK3BVbmds"
                "aTdzVHZpYjBCbURIUDdONmlrVkVCSTZIN0l3VTF6QWlCZHN5b1NxUGNDMk5NcW"
                "dBbkhYSEdka0FJd0JRRDFBVWc5WDhaSkx5ZmN3SEFTUUN0MWJLVnpPTXhSUW1S"
                "M3dOSzRkS2RvZklHcnhFOVNqdUxSNlBhOEI1bjA4U1lKOEs2MmdlKzlhNkJ0Wm"
                "FsRW0vSE9kY3owTkFGT2N5Y3JGL0N0U0E0PSJ9LHsidmFsaWRhdGlvbl9wdWJs"
                "aWNfa2V5IjoiRURBRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMT"
                "ZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFB"
                "QUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQm"
                "prS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJF"
                "cVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQk"
                "k2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4"
                "WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUj"
                "ZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9D"
                "dFNBND0ifV19";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: unl blob list entry manifest signature
        // invalid, skipping
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] =
                "eyJzZXF1ZW5jZSI6MiwiZXhwaXJhdGlvbiI6NzQxMzk4NDAwLCJ2YWxpZGF0b3"
                "JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDM4QkQ0NDVBRkQ2MjE1"
                "OTYyMENDMTk2QzI2NjhBMjZCNkZCQjM2QjA5OUVCNTVCMzhBNThDMTFDMTIwNE"
                "RFNUMiLCJtYW5pZmVzdCI6IkpBQUFBQVZ4SWUwNHZVUmEvV0lWbGlETUdXd21h"
                "S0pyYjdzMnNKbnJWYk9LV01FY0VnVGVYSE1oQW9HTU5jc2dVVU53S28raDd6aG"
                "FYS0YrSEd3NlhoRWpvREtyYWxrWW5Naktka2N3UlFJaEFKN2NONEo2TmRWZnBu"
                "dkVJL1pldVdDVHZucGFKaXJLTkZjQzN6TU9qZ3dqQWlBYktJMGZiWGdTMVJMbG"
                "9OaHhkSGhWcTlvekVXVkU5Y0l3WEROM0F4cXlZM0FTUUN0MCt1L2lOU0RENmJY"
                "dlVUdGRtdDROcnRsYng0Vnp1bVRwZmpSWXA0bE1vSS9oNDNwVVRqcDdWRm9YYm"
                "5LV2pWaHFOYUdtNTc3SzZKNjk3WFo3VFFFPSJ9LHsidmFsaWRhdGlvbl9wdWJs"
                "aWNfa2V5IjoiRURCRUUzMEZBRTkyRUVFODhFMUM0OTgwRDA5RUNGREU5OUExMT"
                "ZEMDc4RUMyMTg1N0RCMUI0N0I0MjY0MThFNDI4IiwibWFuaWZlc3QiOiJKQUFB"
                "QUFKeEllMis0dyt1a3U3b2poeEpnTkNlejk2Wm9SYlFlT3doaFgyeHRIdENaQm"
                "prS0hNaEE5U21IRHhPaUNNVFpsNW5SeHJwMnlqV1o1Z2p4MkRyb1VGclU0bnJF"
                "cVU3ZGtjd1JRSWhBTGRFRmVqWStwVW5nbGk3c1R2aWIwQm1ESFA3TjZpa1ZFQk"
                "k2SDdJd1UxekFpQmRzeW9TcVBjQzJOTXFnQW5IWEhHZGtBSXdCUUQxQVVnOVg4"
                "WkpMeWZjd0hBU1FDdDFiS1Z6T014UlFtUjN3Tks0ZEtkb2ZJR3J4RTlTanVMUj"
                "ZQYThCNW4wOFNZSjhLNjJnZSs5YTZCdFphbEVtL0hPZGN6ME5BRk9jeWNyRi9D"
                "dFNBND0ifV19";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validator nodepub did not appear in
        // validator list but did appear in data section
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value valData;
            valData["n84QWAYxKUHacmyFTnzK4bvqVcUfr6RwtaNxCM2cJRY59UHmz1Fr"] =
                "22800000012600000015292C4C84F53A29EC0A36EDAB6C61510AD4F33846A1"
                "23D86FDAD30CBF175E217BA7B5394A5A761DA5C6B7A45D678DA85017208060"
                "24F4F741C65B2E44B005CA293120889A6BC5F1E179335E20384AB3C6D15019"
                "B94C4657B7529C72DDCE970A87E2EB9EE8EE12580ADFE6CF93B8672E4B289B"
                "BC732103FCA947A7F08B146457BEF95AF0CF7C3ABF0D09CD1DC02099F7185C"
                "37BB32807576473045022100BBBE6EDE0B2B61CD369E2188C8FBFACCB35CA2"
                "D166FD29D5E3D7B2195083E74302201FAA160136301A43E518B9424A0DA5DC"
                "1E7EF8B90DAE2FA87310047498514EB4";
            tmpXpop[jss::validation][jss::data] = valData;
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validator nodepub key appears more than
        // once in data section
        // DA: Impossible - Cannot serialize duplicate json fields

        // temMALFORMED - Import: validation inside xpop was not valid hex
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value valData;
            valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
                "not a hex";
            tmpXpop[jss::validation][jss::data] = valData;
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validation message was not for computed ledger
        // hash
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value valData;
            valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
                "22800000012600000056292C0D012051A0829745427488A59B6525231634DC"
                "327F91589EB03F469ABF1C3CA32070625A501790AD68E9045001FDEED03F0A"
                "C8277F11F6E46E0ABD2DC38B1F8240D56B22E4F2732103D4A61C3C4E882313"
                "665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76463044022072"
                "427336342AE80B7AAE407CEA611B0DA680426B3C5894E52EE2D23641D09A73"
                "02203B131B68D3C5B6C5482312CC7D90D2CE131A9C46458967F2F98688B726"
                "C16719";
            tmpXpop[jss::validation][jss::data] = valData;
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validation inside xpop was not signed with a
        // signing key we recognise
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value valData;
            valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
                "22800000012600000056292C0D012051B0829745427488A59B6525231634DC"
                "327F91589EB03F469ABF1C3CA32070625A501790BD68E9045001FDEED03F0A"
                "C8277F11F6E46E0ABD2DC38B1F8240D56B22E4F2732103E4A61C3C4E882313"
                "665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76463054022072"
                "427336342AE80B7AAE407CEA611B0DA680426B3C5894E52EE2D23641D09A73"
                "02203B131B68D3C5B6C5482312CC7D90D2CE131A9C46458967F2F98688B726"
                "C16719";
            tmpXpop[jss::validation][jss::data] = valData;
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validation inside xpop was not correctly
        // signed
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value valData;
            valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] =
                "22800000012600000056292C0D012051B0829745427488A59B6525231634DC"
                "327F91589EB03F469ABF1C3CA32070625A501790BD68E9045001FDEED03F0A"
                "C8277F11F6E46E0ABD2DC38B1F8240D56B22E4F2732103D4A61C3C4E882313"
                "665E67471AE9DB28D6679823C760EBA1416B5389EB12A53B76463044022072"
                "427336342AE80B7AAE407CEA611B0DA680426B3C5894E52EE2D23641D09A73"
                "02203B131B68D3C5B6C5482312CC7D90D2CE131A9C46458967F2F98688B726"
                "C16719";
            tmpXpop[jss::validation][jss::data] = valData;
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // temMALFORMED - Import: validation inside xpop was not able to be
        // parsed
        // DA: Catch All

        // temMALFORMED - Import: xpop inner txn did not contain a sequence
        // number or fee No Sequence
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "1200632200000000201B0000006C201D0000535968400000003B9ACA007321"
                "EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A9"
                "6C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF"
                "0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C"
                "5177C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // No Fee
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "12006322000000002400000002201B0000006C201D000053597321EDA8D46E"
                "11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A96C747440"
                "549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF0F232EB4"
                "375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C5177C519"
                "560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // Bad Fee
        // DA: Impossible - Cannot serialize negative number
    }

    void
    testInvalidPreclaim(FeatureBitset features)
    {
        testcase("import invalid preclaim");

        using namespace test::jtx;
        using namespace std::literals;

        // ----------------------------------------------------------------------
        // preclaim

        // temDISABLED
        {
            test::jtx::Env env{
                *this,
                network::makeNetworkVLConfig(21337, keys),
                features - featureImport};

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                ter(temDISABLED));
        }

        // tecNO_ISSUER, tecNO_PERMISSION
        // issuer not found, issuer is an AMM account
        {
            auto const alice = Account("alice");
            auto const bob = Account("bob");
            auto const issuer = Account("issuer");
            auto const USD = issuer["USD"];

            for (bool const withFixImportIssuer : {true, false})
            {
                auto const amend =
                    withFixImportIssuer ? features : features - fixImportIssuer;
                test::jtx::Env env{
                    *this, network::makeNetworkVLConfig(21337, keys), amend};
                env.fund(XRP(1000), alice, issuer);
                env.close();

                // burn 10'000 xrp
                auto const master = Account("masterpassphrase");
                env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
                env.close();

                env(import::import(
                        alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                    import::issuer(bob),
                    fee(100'000'000),
                    withFixImportIssuer ? ter(tecNO_ISSUER) : ter(tesSUCCESS));
                env.close();
            }
            for (bool const withFixImportIssuer : {true, false})
            {
                auto const amend =
                    withFixImportIssuer ? features : features - fixImportIssuer;
                test::jtx::Env env{
                    *this, network::makeNetworkVLConfig(21337, keys), amend};
                env.fund(XRP(1000), alice, issuer);
                env.close();

                // burn 10'000 xrp
                auto const master = Account("masterpassphrase");
                env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
                env.close();

                AMM amm(env, issuer, XRP(100), USD(100));
                BEAST_EXPECT(amm.ammExists());

                env(import::import(
                        alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                    import::issuer(amm.ammAccount()),
                    fee(100'000'000),
                    withFixImportIssuer ? ter(tecNO_PERMISSION)
                                        : ter(tesSUCCESS));
                env.close();
            }

            //     env.enableFeature(fixImportIssuer);
            //     env.close();

            //     env(import::import(
            //             carol, import::loadXpop(ImportTCAccountSet::w_seed)),
            //         import::issuer(bob),
            //         ter(tecNO_ISSUER));
            //     env.close();
            //     env(import::import(
            //             dave, import::loadXpop(ImportTCAccountSet::w_seed)),
            //         import::issuer(amm.ammAccount()),
            //         // fee(100'000'000),
            //         ter(tecNO_PERMISSION));
            //     env.close();
        }

        // tefINTERNAL
        // during preclaim could not parse xpop, bailing.
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation] = {};
            // DA: Sanity Check - tefINTERNAL(preclaim)
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // tefINTERNAL
        // during preclaim could not find importSequence, bailing.
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "1200632200000000201B0000006C201D0000535968400000003B9ACA007321"
                "EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A9"
                "6C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF"
                "0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C"
                "5177C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
            // DA: Sanity Check - tefINTERNAL(preclaim)
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // tefIMPORT_BLACKHOLED - SetRegularKey (w/seed) AccountZero
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};
            auto const feeDrops = env.current()->fees().base;

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            // Set Regular Key
            Json::Value jv;
            jv[jss::Account] = alice.human();
            const AccountID ACCOUNT_ZERO(0);
            jv["RegularKey"] = to_string(ACCOUNT_ZERO);
            jv[jss::TransactionType] = jss::SetRegularKey;
            env(jv, alice);

            // Disable Master Key
            env(fset(alice, asfDisableMaster), sig(alice));
            env.close();

            // Import with Master Key
            Json::Value tmpXpop =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            env(import::import(alice, tmpXpop),
                ter(tefIMPORT_BLACKHOLED),
                fee(feeDrops * 10),
                sig(alice));
            env.close();
        }

        // tefIMPORT_BLACKHOLED - SetRegularKey (w/seed) AccountOne
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};
            auto const feeDrops = env.current()->fees().base;

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            // Set Regular Key
            Json::Value jv;
            jv[jss::Account] = alice.human();
            const AccountID ACCOUNT_ONE(1);
            jv["RegularKey"] = to_string(ACCOUNT_ONE);
            jv[jss::TransactionType] = jss::SetRegularKey;
            env(jv, alice);

            // Disable Master Key
            env(fset(alice, asfDisableMaster), sig(alice));
            env.close();

            // Import with Master Key
            Json::Value tmpXpop =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            env(import::import(alice, tmpXpop),
                ter(tefIMPORT_BLACKHOLED),
                fee(feeDrops * 10),
                sig(alice));
            env.close();
        }

        // tefIMPORT_BLACKHOLED - SetRegularKey (w/seed) AccountTwo
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};
            auto const feeDrops = env.current()->fees().base;

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            // Set Regular Key
            Json::Value jv;
            jv[jss::Account] = alice.human();
            const AccountID ACCOUNT_TWO(2);
            jv["RegularKey"] = to_string(ACCOUNT_TWO);
            jv[jss::TransactionType] = jss::SetRegularKey;
            env(jv, alice);

            // Disable Master Key
            env(fset(alice, asfDisableMaster), sig(alice));
            env.close();

            // Import with Master Key
            Json::Value tmpXpop =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            env(import::import(alice, tmpXpop),
                ter(tefIMPORT_BLACKHOLED),
                fee(feeDrops * 10),
                sig(alice));
            env.close();
        }

        // tefIMPORT_BLACKHOLED - SignersListSet (w/seed)
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};
            auto const feeDrops = env.current()->fees().base;

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            // Set Regular Key
            Json::Value jv;
            jv[jss::Account] = alice.human();
            const AccountID ACCOUNT_ZERO(0);
            jv["RegularKey"] = to_string(ACCOUNT_ZERO);
            jv[jss::TransactionType] = jss::SetRegularKey;
            env(jv, alice);

            // Disable Master Key
            env(fset(alice, asfDisableMaster), sig(alice));
            env.close();

            // Import with Master Key
            Json::Value tmpXpop =
                import::loadXpop(ImportTCSignersListSet::w_seed);
            env(import::import(alice, tmpXpop),
                ter(tefIMPORT_BLACKHOLED),
                fee(feeDrops * 10),
                sig(alice));
            env.close();
        }

        // tefPAST_IMPORT_SEQ
        {
            test::jtx::Env env{
                *this, network::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::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                fee(feeDrops * 10),
                ter(tesSUCCESS));
            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::min)),
                fee(feeDrops * 10),
                ter(tefPAST_IMPORT_SEQ));
        }

        // temBAD_FEE
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};

            auto const alice = Account("alice");
            env.memoize(alice);
            Json::Value tx = import::import(
                alice, import::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, network::makeNetworkVLConfig(21337, keys)};

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
            // DA: Sanity Check - tefINTERNAL(preclaim)
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // tefPAST_IMPORT_VL_SEQ
        // import vl sequence already used, bailing.
        {
            test::jtx::Env env{
                *this, network::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 =
                import::loadXpop(ImportTCAccountSet::unl_seq_1_1);
            Json::Value const tx_1_1 = import::import(alice, xpop1_1);
            env(tx_1_1, fee(feeDrops * 10), ter(tesSUCCESS));

            BEAST_EXPECT(importVLSequence(env, pk) == 1);

            Json::Value const xpop2_1 =
                import::loadXpop(ImportTCAccountSet::unl_seq_2_1);
            Json::Value const tx_2_1 = import::import(bob, xpop2_1);
            env(tx_2_1, fee(feeDrops * 10), ter(tesSUCCESS));

            BEAST_EXPECT(importVLSequence(env, pk) == 2);

            Json::Value const xpop1_2 =
                import::loadXpop(ImportTCAccountSet::unl_seq_1_2);
            Json::Value const tx_1_2 = import::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, network::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 =
                import::loadXpop(ImportTCAccountSet::w_seed);
            env(import::import(alice, tmpXpop),
                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, network::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 = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation] = {};  // one of many ways to throw error
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }

        // tefINTERNAL/temMALFORMED
        // during apply could not find importSequence or fee, bailing.
        // No Fee
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::transaction][jss::blob] =
                "1200632200000000201B0000006C201D0000535968400000003B9ACA007321"
                "EDA8D46E11FD5D2082A4E6FF3039EB6259FBC2334983D015FC62ECAD0AE4A9"
                "6C747440549A370E68DBB1947419D4CCDF90CAE0BCA9121593ECC21B3C79EF"
                "0F232EB4375F95F1EBCED78B94D09838B5E769D43F041019ADEF3EC206AD3C"
                "5177C519560F8114AE123A8556F3CF91154711376AFB0F894F832B3D";
            env(import::import(alice, tmpXpop), ter(temMALFORMED));
        }
        // No Sequence

        // tefINTERNAL
        // initBal <= beast::zero
        // Sanity Check

        // tefINTERNAL
        // ImportSequence passed
        // Sanity Check (tefPAST_IMPORT_SEQ)

        // tefINTERNAL/temMALFORMED
        // !infoVL
        {
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            tmpXpop[jss::validation][jss::unl][jss::blob] = "YmFkSnNvbg==";
            env(import::import(alice, tmpXpop), 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, network::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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::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, network::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::import(
                alice, import::loadXpop(ImportTCAccountSet::w_seed));
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - feeDrops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 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, network::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::import(
                alice, import::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, network::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::import(
                alice, import::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
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCAccountSet::w_signers);
            Json::Value tx = import::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
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                fee(10 * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - feeDrops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCAccountSet::w_regular_key);
            Json::Value const tx = import::import(alice, xpopJson);
            env(tx, alice, sig(bob), fee(10 * 10), ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - feeDrops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCAccountSet::w_signers);
            Json::Value const tx = import::import(alice, xpopJson);
            env(tx,
                alice,
                msig(bob, carol),
                fee((3 * feeDrops) * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - feeDrops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn =
                drops(zeroBurn ? 0 : 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, network::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::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_flags)),
                fee(feeDrops * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + reward amount
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_regular_key);
            Json::Value tx = import::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
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_signers);
            Json::Value tx = import::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
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - feeDrops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                sig(alice),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_regular_key);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                sig(bob),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_signers);
            env(import::import(alice, xpopJson),
                msig(bob, carol),
                fee((3 * feeDrops) * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn =
                drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_seed_empty);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                sig(alice),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_regular_key_empty);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                sig(bob),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_signers_empty);
            env(import::import(alice, xpopJson),
                msig(bob, carol),
                fee((3 * feeDrops) * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = burn drops - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn =
                drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_seed);
            Json::Value tx = import::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, network::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 =
                import::loadXpop(ImportTCSetRegularKey::w_seed_zero);
            env(import::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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_seed_bad_fee);
            Json::Value tx = import::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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_seed);
            Json::Value tx = import::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
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn =
                XRP(zeroBurn ? 0 : 2) + drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_regular_key);
            Json::Value tx = import::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
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn =
                XRP(zeroBurn ? 0 : 2) + drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_signers);
            Json::Value tx = import::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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_seed);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = (burn drops + burn fee drops) - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 2) +
                drops(zeroBurn ? 0 : 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);
            BEAST_EXPECT(
                signersSle && signersSle->isFieldPresent(sfSignerEntries));
            if (signersSle && signersSle->isFieldPresent(sfSignerEntries))
            {
                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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_seed_empty);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = (burn drops + burn fee drops) - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 2) +
                drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_regular_key_empty);
            env(import::import(alice, xpopJson),
                fee(feeDrops * 10),
                sig(bob),
                ter(tesSUCCESS));
            env.close();

            // total burn = (burn drops + burn fee drops) - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 2) +
                drops(zeroBurn ? 0 : 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, network::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 =
                import::loadXpop(ImportTCSignersListSet::w_signers_empty);
            env(import::import(alice, xpopJson),
                msig(bob, carol),
                fee((3 * feeDrops) * 10),
                ter(tesSUCCESS));
            env.close();

            // total burn = (burn drops + burn fee drops) - fee drops
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = XRP(zeroBurn ? 0 : 2) +
                drops(zeroBurn ? 0 : 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
    testUsingTickets(FeatureBitset features)
    {
        testcase("using tickets");

        using namespace test::jtx;
        using namespace std::literals;

        {
            test::jtx::Env env{
                *this, network::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();

            std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
            env(ticket::create(alice, 10));
            std::uint32_t const aliceSeq{env.seq(alice)};
            env.require(owners(alice, 10));

            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                fee(feeDrops * 10),
                ticket::use(aliceTicketSeq++),
                ter(tesSUCCESS));
            env.close();

            env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
            BEAST_EXPECT(env.seq(alice) == aliceSeq);
            env.require(owners(alice, 9));
        }
    }

    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, network::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::import(
                        alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                    fee(feeDrops * 10),
                    ter(tesSUCCESS));
                env.close();

                // confirm account index was set
                auto const [acct, acctSle] =
                    accountKeyAndSle(*env.current(), alice);

                // 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, network::makeNetworkVLConfig(21337, keys), amend};
                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, network::makeNetworkVLConfig(21337, keys)};

            auto const feeDrops = env.current()->fees().base;
            bool const fixV2 = env.current()->rules().enabled(fixXahauV2);

            // 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();

            // fixXahauV2 - tshWEAK
            env(fset(issuer, asfTshCollect));
            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 const result = fixV2 ? ter(tesSUCCESS) : ter(tecHOOK_REJECTED);
            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                import::issuer(issuer),
                fee(1'000'000),
                result);
            env.close();

            // fixXahauV2
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const mintXAH = fixV2 ? XRP(zeroBurn ? 0 : 1000) : XRP(0);
            // confirm fee was burned mint / no mint
            auto const postAlice = env.balance(alice);
            BEAST_EXPECT(postAlice == preAlice - XRP(1) + mintXAH);

            // confirm total coins header
            auto const postCoins = env.current()->info().drops;
            BEAST_EXPECT(postCoins == preCoins - XRP(1) + mintXAH);

            // resubmit import without issuer
            auto const result2 =
                fixV2 ? ter(tefPAST_IMPORT_SEQ) : ter(tesSUCCESS);
            env(import::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                fee(feeDrops * 10),
                result2);
            env.close();

            // total burn
            auto const totalBurn =
                fixV2 ? XRP(0) : XRP(zeroBurn ? 0 : 1000) - (feeDrops * 10);

            // confirm fee was minted / not 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, network::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 =
                import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value const tx = import::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, network::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::import(
                    alice, import::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, network::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::import(
                    alice, import::loadXpop(ImportTCAccountSet::w_seed)),
                fee(feeDrops * 10),
                ter(tesSUCCESS));
            env.close();
        }

        // test no IMPORT_VL_KEYS has UNLReport
        {
            test::jtx::Env env{*this, network::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 = unl::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::import(
                    alice, import::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,
                network::makeNetworkVLConfig(21337, keys),
                nullptr,
                beast::severities::kDisabled,
            };

            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));

            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            STAmount burnFee = XRP(zeroBurn ? 0 : 1000) + XRP(2);
            auto const xpopJson = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::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,
                network::makeNetworkVLConfig(21337, keys),
                nullptr,
                beast::severities::kDisabled,
            };

            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));

            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            STAmount burnFee = XRP(zeroBurn ? 0 : 1000) + XRP(2);
            auto const xpopJson = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::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,
                network::makeNetworkVLConfig(21337, keys),
                nullptr,
                beast::severities::kDisabled,
            };

            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));

            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            STAmount burnFee = XRP(zeroBurn ? 0 : 1000) + XRP(2);
            auto const xpopJson = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::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, network::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 = import::loadXpop(ImportTCAccountSet::min);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + reward amount
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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,
                network::makeNetworkVLConfig(
                    21337, keys, "50", "10000000", "2000000")};

            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 = import::loadXpop(ImportTCAccountSet::min);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + reward amount
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn = drops(zeroBurn ? 0 : 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, network::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 = import::loadXpop(ImportTCAccountSet::max);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + reward amount
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const totalBurn =
                drops(zeroBurn ? 0 : 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));
        }
    }

    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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + Init Reward
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const creditDrops = XRP(zeroBurn ? 0 : 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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + Init Reward
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const creditDrops = XRP(zeroBurn ? 0 : 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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + Init Reward
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const creditDrops = drops(zeroBurn ? 0 : 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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + Init Reward
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const creditDrops = drops(zeroBurn ? 0 : 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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + Init Reward
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const creditDrops = drops(zeroBurn ? 0 : 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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::import(alice, xpopJson);
            tx[jss::Sequence] = 0;
            tx[jss::Fee] = 0;
            env(tx, alice, ter(tesSUCCESS));
            env.close();

            // total burn = burn drops + Init Reward
            bool const zeroBurn =
                env.current()->rules().enabled(featureZeroB2M);
            auto const creditDrops = drops(zeroBurn ? 0 : 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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::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,
                network::makeGenesisConfig(
                    features, 21337, keys, "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 = import::loadXpop(ImportTCAccountSet::w_seed);
            Json::Value tx = import::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
    testBlackhole(FeatureBitset features)
    {
        testcase("blackhole");

        using namespace test::jtx;
        using namespace std::literals;

        auto blackholeAccount = [&](Env& env, Account const& acct) {
            // Set Regular Key
            Json::Value jv;
            jv[jss::Account] = acct.human();
            const AccountID ACCOUNT_ZERO(0);
            jv["RegularKey"] = to_string(ACCOUNT_ZERO);
            jv[jss::TransactionType] = jss::SetRegularKey;
            env(jv, acct);

            // Disable Master Key
            env(fset(acct, asfDisableMaster), sig(acct));
            env.close();
        };

        auto burnHeader = [&](Env& env) {
            // 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(100'000'000'000'000), ter(tesSUCCESS));
            env.close();

            // confirm total coins header
            auto const burnCoins = env.current()->info().drops;
            BEAST_EXPECT(burnCoins == initCoins - 100'000'000'000'000);
        };

        // AccountSet (w/seed)
        {
            test::jtx::Env env{
                *this, network::makeNetworkVLConfig(21337, keys)};
            auto const feeDrops = env.current()->fees().base;

            // Burn Header
            burnHeader(env);

            auto const alice = Account("alice");
            env.fund(XRP(1000), alice);
            env.close();

            // Blackhole Account
            blackholeAccount(env, alice);

            // Import with Master Key
            Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed);
            env(import::import(alice, tmpXpop),
                ter(tesSUCCESS),
                fee(feeDrops * 10),
                sig(alice));
            env.close();
        }
    }

public:
    void
    run() override
    {
        using namespace test::jtx;
        FeatureBitset const all{supported_amendments()};
        testWithFeats(all - fixXahauV2);
        testWithFeats(all - featureZeroB2M);
        testWithFeats(all);
    }

    void
    testWithFeats(FeatureBitset features)
    {
        testComputeStartingBalance(features);
        testIsHex(features);
        testIsBase58(features);
        testIsBase64(features);
        testParseUint64(features);
        testSyntaxCheckProofArray(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);
        testUsingTickets(features);
        testAccountIndex(features);
        testHookIssuer(features);
        testImportSequence(features);
        testAccountDelete(features);
        testMaxSupply(features);
        testMinMax(features);
        testHalving(features - featureOwnerPaysFee);
        testBlackhole(features);
    }
};

BEAST_DEFINE_TESTSUITE(Import, app, ripple);

}  // namespace test
}  // namespace ripple
