mirror of
				https://github.com/Xahau/xahaud.git
				synced 2025-11-04 10:45:50 +00:00 
			
		
		
		
	Add successor information to clio ETL messages
* Allow clio to ask for object successors and predecessors from rippled * Add lower_bound and last_below to SHAMap
This commit is contained in:
		@@ -5,7 +5,9 @@ if (POLICY CMP0074)
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
project (rippled)
 | 
			
		||||
set (CMAKE_CXX_STANDARD 17)
 | 
			
		||||
set(CMAKE_CXX_EXTENSIONS OFF)
 | 
			
		||||
set(CMAKE_CXX_STANDARD 17)
 | 
			
		||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
 | 
			
		||||
# make GIT_COMMIT_HASH define available to all sources
 | 
			
		||||
find_package(Git)
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,10 @@ message GetLedgerRequest
 | 
			
		||||
    // coming from a secure_gateway host, then the client is not subject to
 | 
			
		||||
    // resource controls
 | 
			
		||||
    string user = 6;
 | 
			
		||||
 | 
			
		||||
    // For every object in the diff, get the object's predecessor and successor
 | 
			
		||||
    // in the state map. Only used if get_objects is also true.
 | 
			
		||||
    bool get_object_neighbors = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message GetLedgerResponse
 | 
			
		||||
@@ -58,6 +62,18 @@ message GetLedgerResponse
 | 
			
		||||
 | 
			
		||||
    // True if request was exempt from resource controls
 | 
			
		||||
    bool is_unlimited = 7;
 | 
			
		||||
 | 
			
		||||
    // True if the response contains the state map diff
 | 
			
		||||
    bool objects_included = 8;
 | 
			
		||||
 | 
			
		||||
    // True if the response contains key of objects adjacent to objects in state
 | 
			
		||||
    // map diff
 | 
			
		||||
    bool object_neighbors_included = 9;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Successor information for book directories modified as part of this
 | 
			
		||||
    // ledger
 | 
			
		||||
    repeated BookSuccessor book_successors = 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message TransactionHashList
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,14 @@ message RawLedgerObject
 | 
			
		||||
        DELETED = 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Whether the object was created, modified or deleted
 | 
			
		||||
    ModificationType mod_type = 3;
 | 
			
		||||
 | 
			
		||||
    // Key of the object preceding this object in the desired ledger
 | 
			
		||||
    bytes predecessor = 4;
 | 
			
		||||
 | 
			
		||||
    // Key of the object succeeding this object in the desired ledger
 | 
			
		||||
    bytes successor = 5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message RawLedgerObjects
 | 
			
		||||
@@ -62,3 +69,17 @@ message RawLedgerObjects
 | 
			
		||||
    repeated RawLedgerObject objects = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Successor information for book directories. The book base is (usually) not
 | 
			
		||||
// an actual object, yet we need to be able to ask for the successor to the
 | 
			
		||||
// book base.
 | 
			
		||||
message BookSuccessor {
 | 
			
		||||
 | 
			
		||||
    // Base of the book in question
 | 
			
		||||
    bytes book_base = 1;
 | 
			
		||||
 | 
			
		||||
    // First book directory in the book. An empty value here means the entire
 | 
			
		||||
    // book is deleted
 | 
			
		||||
    bytes first_book = 2;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -105,6 +105,7 @@ LedgerHandler::check()
 | 
			
		||||
std::pair<org::xrpl::rpc::v1::GetLedgerResponse, grpc::Status>
 | 
			
		||||
doLedgerGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest>& context)
 | 
			
		||||
{
 | 
			
		||||
    auto begin = std::chrono::system_clock::now();
 | 
			
		||||
    org::xrpl::rpc::v1::GetLedgerRequest& request = context.params;
 | 
			
		||||
    org::xrpl::rpc::v1::GetLedgerResponse response;
 | 
			
		||||
    grpc::Status status = grpc::Status::OK;
 | 
			
		||||
@@ -212,13 +213,101 @@ doLedgerGrpc(RPC::GRPCContext<org::xrpl::rpc::v1::GetLedgerRequest>& context)
 | 
			
		||||
                obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::DELETED);
 | 
			
		||||
            else
 | 
			
		||||
                obj->set_mod_type(org::xrpl::rpc::v1::RawLedgerObject::CREATED);
 | 
			
		||||
            auto const blob = inDesired ? inDesired->slice() : inBase->slice();
 | 
			
		||||
            auto const objectType =
 | 
			
		||||
                static_cast<LedgerEntryType>(blob[1] << 8 | blob[2]);
 | 
			
		||||
 | 
			
		||||
            if (request.get_object_neighbors())
 | 
			
		||||
            {
 | 
			
		||||
                if (!(inBase && inDesired))
 | 
			
		||||
                {
 | 
			
		||||
                    auto lb = desired->stateMap().lower_bound(k);
 | 
			
		||||
                    auto ub = desired->stateMap().upper_bound(k);
 | 
			
		||||
                    if (lb != desired->stateMap().end())
 | 
			
		||||
                        obj->set_predecessor(
 | 
			
		||||
                            lb->key().data(), lb->key().size());
 | 
			
		||||
                    if (ub != desired->stateMap().end())
 | 
			
		||||
                        obj->set_successor(ub->key().data(), ub->key().size());
 | 
			
		||||
                    if (objectType == ltDIR_NODE)
 | 
			
		||||
                    {
 | 
			
		||||
                        auto sle = std::make_shared<SLE>(SerialIter{blob}, k);
 | 
			
		||||
                        if (!sle->isFieldPresent(sfOwner))
 | 
			
		||||
                        {
 | 
			
		||||
                            auto bookBase = keylet::quality({ltDIR_NODE, k}, 0);
 | 
			
		||||
                            if (!inBase && inDesired)
 | 
			
		||||
                            {
 | 
			
		||||
                                auto firstBook =
 | 
			
		||||
                                    desired->stateMap().upper_bound(
 | 
			
		||||
                                        bookBase.key);
 | 
			
		||||
                                if (firstBook != desired->stateMap().end() &&
 | 
			
		||||
                                    firstBook->key() <
 | 
			
		||||
                                        getQualityNext(bookBase.key) &&
 | 
			
		||||
                                    firstBook->key() == k)
 | 
			
		||||
                                {
 | 
			
		||||
                                    auto succ = response.add_book_successors();
 | 
			
		||||
                                    succ->set_book_base(
 | 
			
		||||
                                        bookBase.key.data(),
 | 
			
		||||
                                        bookBase.key.size());
 | 
			
		||||
                                    succ->set_first_book(
 | 
			
		||||
                                        firstBook->key().data(),
 | 
			
		||||
                                        firstBook->key().size());
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            if (inBase && !inDesired)
 | 
			
		||||
                            {
 | 
			
		||||
                                auto oldFirstBook =
 | 
			
		||||
                                    base->stateMap().upper_bound(bookBase.key);
 | 
			
		||||
                                if (oldFirstBook != base->stateMap().end() &&
 | 
			
		||||
                                    oldFirstBook->key() <
 | 
			
		||||
                                        getQualityNext(bookBase.key) &&
 | 
			
		||||
                                    oldFirstBook->key() == k)
 | 
			
		||||
                                {
 | 
			
		||||
                                    auto succ = response.add_book_successors();
 | 
			
		||||
                                    succ->set_book_base(
 | 
			
		||||
                                        bookBase.key.data(),
 | 
			
		||||
                                        bookBase.key.size());
 | 
			
		||||
                                    auto newFirstBook =
 | 
			
		||||
                                        desired->stateMap().upper_bound(
 | 
			
		||||
                                            bookBase.key);
 | 
			
		||||
 | 
			
		||||
                                    if (newFirstBook !=
 | 
			
		||||
                                            desired->stateMap().end() &&
 | 
			
		||||
                                        newFirstBook->key() <
 | 
			
		||||
                                            getQualityNext(bookBase.key))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        succ->set_first_book(
 | 
			
		||||
                                            newFirstBook->key().data(),
 | 
			
		||||
                                            newFirstBook->key().size());
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        response.set_objects_included(true);
 | 
			
		||||
        response.set_object_neighbors_included(request.get_object_neighbors());
 | 
			
		||||
        response.set_skiplist_included(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    response.set_validated(
 | 
			
		||||
        RPC::isValidated(context.ledgerMaster, *ledger, context.app));
 | 
			
		||||
 | 
			
		||||
    auto end = std::chrono::system_clock::now();
 | 
			
		||||
    auto duration =
 | 
			
		||||
        std::chrono::duration_cast<std::chrono::milliseconds>(end - begin)
 | 
			
		||||
            .count() *
 | 
			
		||||
        1.0;
 | 
			
		||||
    JLOG(context.j.warn())
 | 
			
		||||
        << __func__ << " - Extract time = " << duration
 | 
			
		||||
        << " - num objects = " << response.ledger_objects().objects_size()
 | 
			
		||||
        << " - num txns = " << response.transactions_list().transactions_size()
 | 
			
		||||
        << " - ms per obj "
 | 
			
		||||
        << duration / response.ledger_objects().objects_size()
 | 
			
		||||
        << " - ms per txn "
 | 
			
		||||
        << duration / response.transactions_list().transactions_size();
 | 
			
		||||
 | 
			
		||||
    return {response, status};
 | 
			
		||||
}
 | 
			
		||||
}  // namespace ripple
 | 
			
		||||
 
 | 
			
		||||
@@ -210,9 +210,17 @@ public:
 | 
			
		||||
    peekItem(uint256 const& id, SHAMapHash& hash) const;
 | 
			
		||||
 | 
			
		||||
    // traverse functions
 | 
			
		||||
 | 
			
		||||
    // finds the object in the tree with the smallest object id greater than the
 | 
			
		||||
    // input id
 | 
			
		||||
    const_iterator
 | 
			
		||||
    upper_bound(uint256 const& id) const;
 | 
			
		||||
 | 
			
		||||
    // finds the object in the tree with the greatest object id smaller than the
 | 
			
		||||
    // input id
 | 
			
		||||
    const_iterator
 | 
			
		||||
    lower_bound(uint256 const& id) const;
 | 
			
		||||
 | 
			
		||||
    /**  Visit every node in this SHAMap
 | 
			
		||||
 | 
			
		||||
         @param function called with every node visited.
 | 
			
		||||
@@ -400,12 +408,31 @@ private:
 | 
			
		||||
    std::shared_ptr<SHAMapTreeNode>
 | 
			
		||||
    writeNode(NodeObjectType t, std::shared_ptr<SHAMapTreeNode> node) const;
 | 
			
		||||
 | 
			
		||||
    // returns the first item at or below this node
 | 
			
		||||
    SHAMapLeafNode*
 | 
			
		||||
    firstBelow(
 | 
			
		||||
        std::shared_ptr<SHAMapTreeNode>,
 | 
			
		||||
        SharedPtrNodeStack& stack,
 | 
			
		||||
        int branch = 0) const;
 | 
			
		||||
 | 
			
		||||
    // returns the last item at or below this node
 | 
			
		||||
    SHAMapLeafNode*
 | 
			
		||||
    lastBelow(
 | 
			
		||||
        std::shared_ptr<SHAMapTreeNode> node,
 | 
			
		||||
        SharedPtrNodeStack& stack,
 | 
			
		||||
        int branch = branchFactor) const;
 | 
			
		||||
 | 
			
		||||
    // helper function for firstBelow and lastBelow
 | 
			
		||||
    SHAMapLeafNode*
 | 
			
		||||
    belowHelper(
 | 
			
		||||
        std::shared_ptr<SHAMapTreeNode> node,
 | 
			
		||||
        SharedPtrNodeStack& stack,
 | 
			
		||||
        int branch,
 | 
			
		||||
        std::tuple<
 | 
			
		||||
            int,
 | 
			
		||||
            std::function<bool(int)>,
 | 
			
		||||
            std::function<void(int&)>> const& loopParams) const;
 | 
			
		||||
 | 
			
		||||
    // Simple descent
 | 
			
		||||
    // Get a child of the specified node
 | 
			
		||||
    SHAMapTreeNode*
 | 
			
		||||
 
 | 
			
		||||
@@ -437,12 +437,14 @@ SHAMap::unshareNode(std::shared_ptr<Node> node, SHAMapNodeID const& nodeID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SHAMapLeafNode*
 | 
			
		||||
SHAMap::firstBelow(
 | 
			
		||||
SHAMap::belowHelper(
 | 
			
		||||
    std::shared_ptr<SHAMapTreeNode> node,
 | 
			
		||||
    SharedPtrNodeStack& stack,
 | 
			
		||||
    int branch) const
 | 
			
		||||
    int branch,
 | 
			
		||||
    std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const&
 | 
			
		||||
        loopParams) const
 | 
			
		||||
{
 | 
			
		||||
    // Return the first item at or below this node
 | 
			
		||||
    auto& [init, cmp, incr] = loopParams;
 | 
			
		||||
    if (node->isLeaf())
 | 
			
		||||
    {
 | 
			
		||||
        auto n = std::static_pointer_cast<SHAMapLeafNode>(node);
 | 
			
		||||
@@ -454,7 +456,7 @@ SHAMap::firstBelow(
 | 
			
		||||
        stack.push({inner, SHAMapNodeID{}});
 | 
			
		||||
    else
 | 
			
		||||
        stack.push({inner, stack.top().second.getChildNodeID(branch)});
 | 
			
		||||
    for (int i = 0; i < branchFactor;)
 | 
			
		||||
    for (int i = init; cmp(i);)
 | 
			
		||||
    {
 | 
			
		||||
        if (!inner->isEmptyBranch(i))
 | 
			
		||||
        {
 | 
			
		||||
@@ -468,14 +470,37 @@ SHAMap::firstBelow(
 | 
			
		||||
            }
 | 
			
		||||
            inner = std::static_pointer_cast<SHAMapInnerNode>(node);
 | 
			
		||||
            stack.push({inner, stack.top().second.getChildNodeID(branch)});
 | 
			
		||||
            i = 0;  // scan all 16 branches of this new node
 | 
			
		||||
            i = init;  // descend and reset loop
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            ++i;  // scan next branch
 | 
			
		||||
            incr(i);  // scan next branch
 | 
			
		||||
    }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
SHAMapLeafNode*
 | 
			
		||||
SHAMap::lastBelow(
 | 
			
		||||
    std::shared_ptr<SHAMapTreeNode> node,
 | 
			
		||||
    SharedPtrNodeStack& stack,
 | 
			
		||||
    int branch) const
 | 
			
		||||
{
 | 
			
		||||
    auto init = branchFactor - 1;
 | 
			
		||||
    auto cmp = [](int i) { return i >= 0; };
 | 
			
		||||
    auto incr = [](int& i) { --i; };
 | 
			
		||||
 | 
			
		||||
    return belowHelper(node, stack, branch, {init, cmp, incr});
 | 
			
		||||
}
 | 
			
		||||
SHAMapLeafNode*
 | 
			
		||||
SHAMap::firstBelow(
 | 
			
		||||
    std::shared_ptr<SHAMapTreeNode> node,
 | 
			
		||||
    SharedPtrNodeStack& stack,
 | 
			
		||||
    int branch) const
 | 
			
		||||
{
 | 
			
		||||
    auto init = 0;
 | 
			
		||||
    auto cmp = [](int i) { return i <= branchFactor; };
 | 
			
		||||
    auto incr = [](int& i) { ++i; };
 | 
			
		||||
 | 
			
		||||
    return belowHelper(node, stack, branch, {init, cmp, incr});
 | 
			
		||||
}
 | 
			
		||||
static const std::shared_ptr<SHAMapItem const> no_item;
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<SHAMapItem const> const&
 | 
			
		||||
@@ -619,6 +644,43 @@ SHAMap::upper_bound(uint256 const& id) const
 | 
			
		||||
    }
 | 
			
		||||
    return end();
 | 
			
		||||
}
 | 
			
		||||
SHAMap::const_iterator
 | 
			
		||||
SHAMap::lower_bound(uint256 const& id) const
 | 
			
		||||
{
 | 
			
		||||
    SharedPtrNodeStack stack;
 | 
			
		||||
    walkTowardsKey(id, &stack);
 | 
			
		||||
    while (!stack.empty())
 | 
			
		||||
    {
 | 
			
		||||
        auto [node, nodeID] = stack.top();
 | 
			
		||||
        if (node->isLeaf())
 | 
			
		||||
        {
 | 
			
		||||
            auto leaf = static_cast<SHAMapLeafNode*>(node.get());
 | 
			
		||||
            if (leaf->peekItem()->key() < id)
 | 
			
		||||
                return const_iterator(
 | 
			
		||||
                    this, leaf->peekItem().get(), std::move(stack));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
 | 
			
		||||
            for (int branch = selectBranch(nodeID, id) - 1; branch >= 0;
 | 
			
		||||
                 --branch)
 | 
			
		||||
            {
 | 
			
		||||
                if (!inner->isEmptyBranch(branch))
 | 
			
		||||
                {
 | 
			
		||||
                    node = descendThrow(inner, branch);
 | 
			
		||||
                    auto leaf = lastBelow(node, stack, branch);
 | 
			
		||||
                    if (!leaf)
 | 
			
		||||
                        Throw<SHAMapMissingNode>(type_, id);
 | 
			
		||||
                    return const_iterator(
 | 
			
		||||
                        this, leaf->peekItem().get(), std::move(stack));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        stack.pop();
 | 
			
		||||
    }
 | 
			
		||||
    // TODO: what to return here?
 | 
			
		||||
    return end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
SHAMap::hasItem(uint256 const& id) const
 | 
			
		||||
 
 | 
			
		||||
@@ -384,6 +384,273 @@ class View_test : public beast::unit_test::suite
 | 
			
		||||
        return std::vector<uint256>({uint256(args)...});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testUpperAndLowerBound()
 | 
			
		||||
    {
 | 
			
		||||
        using namespace jtx;
 | 
			
		||||
        Env env(*this);
 | 
			
		||||
        Config config;
 | 
			
		||||
        std::shared_ptr<Ledger const> const genesis = std::make_shared<Ledger>(
 | 
			
		||||
            create_genesis,
 | 
			
		||||
            config,
 | 
			
		||||
            std::vector<uint256>{},
 | 
			
		||||
            env.app().getNodeFamily());
 | 
			
		||||
        auto const ledger = std::make_shared<Ledger>(
 | 
			
		||||
            *genesis, env.app().timeKeeper().closeTime());
 | 
			
		||||
 | 
			
		||||
        auto setup = [&ledger](std::vector<int> const& vec) {
 | 
			
		||||
            wipe(*ledger);
 | 
			
		||||
            for (auto x : vec)
 | 
			
		||||
            {
 | 
			
		||||
                ledger->rawInsert(sle(x));
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        {
 | 
			
		||||
            setup({1, 2, 3});
 | 
			
		||||
            BEAST_EXPECT(sles(*ledger) == list(1, 2, 3));
 | 
			
		||||
            auto e = ledger->stateMap().end();
 | 
			
		||||
            auto b1 = ledger->stateMap().begin();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(2)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(3)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(4)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(5)) == b1);
 | 
			
		||||
            b1 = ledger->stateMap().begin();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(0)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(1)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(2)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(3)) == e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            setup({2, 4, 6});
 | 
			
		||||
            BEAST_EXPECT(sles(*ledger) == list(2, 4, 6));
 | 
			
		||||
            auto e = ledger->stateMap().end();
 | 
			
		||||
            auto b1 = ledger->stateMap().begin();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(2)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(3)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(4)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(5)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(6)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(7)) == b1);
 | 
			
		||||
            b1 = ledger->stateMap().begin();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(1)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(2)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(3)) == b1);
 | 
			
		||||
            ++b1;
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(4)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(5)) == b1);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(6)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(7)) == e);
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            setup({2, 3, 5, 6, 10, 15});
 | 
			
		||||
            BEAST_EXPECT(sles(*ledger) == list(2, 3, 5, 6, 10, 15));
 | 
			
		||||
            auto e = ledger->stateMap().end();
 | 
			
		||||
            auto b = ledger->stateMap().begin();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(2)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(3)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(4)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(5)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(6)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(7)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(8)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(9)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(10)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(11)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(12)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(13)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(14)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(15)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(16)) == b);
 | 
			
		||||
            b = ledger->stateMap().begin();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(0)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(1)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(2)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(3)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(4)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(5)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(6)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(7)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(8)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(9)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(10)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(11)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(12)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(13)) == b);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(14)) == b);
 | 
			
		||||
            ++b;
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(15)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(16)) == e);
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            // some full trees, some empty trees, etc
 | 
			
		||||
            setup({0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12,
 | 
			
		||||
                   13, 14, 15, 16, 20, 25, 30, 32, 33, 34, 35, 36, 37,
 | 
			
		||||
                   38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 66, 100});
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                sles(*ledger) ==
 | 
			
		||||
                list(
 | 
			
		||||
                    0,
 | 
			
		||||
                    1,
 | 
			
		||||
                    2,
 | 
			
		||||
                    3,
 | 
			
		||||
                    4,
 | 
			
		||||
                    5,
 | 
			
		||||
                    6,
 | 
			
		||||
                    7,
 | 
			
		||||
                    8,
 | 
			
		||||
                    9,
 | 
			
		||||
                    10,
 | 
			
		||||
                    11,
 | 
			
		||||
                    12,
 | 
			
		||||
                    13,
 | 
			
		||||
                    14,
 | 
			
		||||
                    15,
 | 
			
		||||
                    16,
 | 
			
		||||
                    20,
 | 
			
		||||
                    25,
 | 
			
		||||
                    30,
 | 
			
		||||
                    32,
 | 
			
		||||
                    33,
 | 
			
		||||
                    34,
 | 
			
		||||
                    35,
 | 
			
		||||
                    36,
 | 
			
		||||
                    37,
 | 
			
		||||
                    38,
 | 
			
		||||
                    39,
 | 
			
		||||
                    40,
 | 
			
		||||
                    41,
 | 
			
		||||
                    42,
 | 
			
		||||
                    43,
 | 
			
		||||
                    44,
 | 
			
		||||
                    45,
 | 
			
		||||
                    46,
 | 
			
		||||
                    47,
 | 
			
		||||
                    48,
 | 
			
		||||
                    66,
 | 
			
		||||
                    100));
 | 
			
		||||
            auto b = ledger->stateMap().begin();
 | 
			
		||||
            auto e = ledger->stateMap().end();
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(0)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == b);
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(5))->key() ==
 | 
			
		||||
                uint256(4));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(15))->key() ==
 | 
			
		||||
                uint256(14));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(16))->key() ==
 | 
			
		||||
                uint256(15));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(19))->key() ==
 | 
			
		||||
                uint256(16));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(20))->key() ==
 | 
			
		||||
                uint256(16));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(24))->key() ==
 | 
			
		||||
                uint256(20));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(31))->key() ==
 | 
			
		||||
                uint256(30));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(32))->key() ==
 | 
			
		||||
                uint256(30));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(40))->key() ==
 | 
			
		||||
                uint256(39));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(47))->key() ==
 | 
			
		||||
                uint256(46));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(48))->key() ==
 | 
			
		||||
                uint256(47));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(64))->key() ==
 | 
			
		||||
                uint256(48));
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(90))->key() ==
 | 
			
		||||
                uint256(66));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(96))->key() ==
 | 
			
		||||
                uint256(66));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().lower_bound(uint256(100))->key() ==
 | 
			
		||||
                uint256(66));
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(0))->key() ==
 | 
			
		||||
                uint256(1));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(5))->key() ==
 | 
			
		||||
                uint256(6));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(15))->key() ==
 | 
			
		||||
                uint256(16));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(16))->key() ==
 | 
			
		||||
                uint256(20));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(18))->key() ==
 | 
			
		||||
                uint256(20));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(20))->key() ==
 | 
			
		||||
                uint256(25));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(31))->key() ==
 | 
			
		||||
                uint256(32));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(32))->key() ==
 | 
			
		||||
                uint256(33));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(47))->key() ==
 | 
			
		||||
                uint256(48));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(48))->key() ==
 | 
			
		||||
                uint256(66));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(53))->key() ==
 | 
			
		||||
                uint256(66));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(66))->key() ==
 | 
			
		||||
                uint256(100));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(70))->key() ==
 | 
			
		||||
                uint256(100));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(85))->key() ==
 | 
			
		||||
                uint256(100));
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                ledger->stateMap().upper_bound(uint256(98))->key() ==
 | 
			
		||||
                uint256(100));
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(100)) == e);
 | 
			
		||||
            BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(155)) == e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void
 | 
			
		||||
    testSles()
 | 
			
		||||
    {
 | 
			
		||||
@@ -811,6 +1078,7 @@ class View_test : public beast::unit_test::suite
 | 
			
		||||
        testStacked();
 | 
			
		||||
        testContext();
 | 
			
		||||
        testSles();
 | 
			
		||||
        testUpperAndLowerBound();
 | 
			
		||||
        testFlags();
 | 
			
		||||
        testTransferRate();
 | 
			
		||||
        testAreCompatible();
 | 
			
		||||
 
 | 
			
		||||
@@ -70,20 +70,22 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
                              auto sequence,
 | 
			
		||||
                              bool transactions,
 | 
			
		||||
                              bool expand,
 | 
			
		||||
                              bool get_objects) {
 | 
			
		||||
                              bool get_objects,
 | 
			
		||||
                              bool get_object_neighbors) {
 | 
			
		||||
            GrpcLedgerClient grpcClient{grpcPort};
 | 
			
		||||
 | 
			
		||||
            grpcClient.request.mutable_ledger()->set_sequence(sequence);
 | 
			
		||||
            grpcClient.request.set_transactions(transactions);
 | 
			
		||||
            grpcClient.request.set_expand(expand);
 | 
			
		||||
            grpcClient.request.set_get_objects(get_objects);
 | 
			
		||||
            grpcClient.request.set_get_object_neighbors(get_object_neighbors);
 | 
			
		||||
 | 
			
		||||
            grpcClient.GetLedger();
 | 
			
		||||
            return std::make_pair(grpcClient.status, grpcClient.reply);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto [status, reply] = grpcLedger(3, false, false, false);
 | 
			
		||||
            auto [status, reply] = grpcLedger(3, false, false, false, false);
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(status.ok());
 | 
			
		||||
            BEAST_EXPECT(reply.validated());
 | 
			
		||||
@@ -119,7 +121,7 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
        addRaw(ledger->info(), s, true);
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, false, false);
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, false, false, false);
 | 
			
		||||
            BEAST_EXPECT(status.ok());
 | 
			
		||||
            BEAST_EXPECT(reply.validated());
 | 
			
		||||
            BEAST_EXPECT(reply.has_hashes_list());
 | 
			
		||||
@@ -145,7 +147,7 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, true, false);
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, true, false, false);
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(status.ok());
 | 
			
		||||
            BEAST_EXPECT(reply.validated());
 | 
			
		||||
@@ -209,7 +211,7 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, true, true);
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, true, true, false);
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(status.ok());
 | 
			
		||||
            BEAST_EXPECT(reply.validated());
 | 
			
		||||
@@ -295,6 +297,112 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
                ++idx;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            auto [status, reply] = grpcLedger(4, true, true, true, true);
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(status.ok());
 | 
			
		||||
            BEAST_EXPECT(reply.validated());
 | 
			
		||||
            BEAST_EXPECT(!reply.has_hashes_list());
 | 
			
		||||
            BEAST_EXPECT(reply.object_neighbors_included());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(reply.has_transactions_list());
 | 
			
		||||
            BEAST_EXPECT(reply.transactions_list().transactions_size() == 4);
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(0)
 | 
			
		||||
                              .transaction_blob()) ==
 | 
			
		||||
                transactions[0]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(0)
 | 
			
		||||
                              .metadata_blob()) ==
 | 
			
		||||
                metas[0]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(1)
 | 
			
		||||
                              .transaction_blob()) ==
 | 
			
		||||
                transactions[1]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(1)
 | 
			
		||||
                              .metadata_blob()) ==
 | 
			
		||||
                metas[1]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(2)
 | 
			
		||||
                              .transaction_blob()) ==
 | 
			
		||||
                transactions[2]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(2)
 | 
			
		||||
                              .metadata_blob()) ==
 | 
			
		||||
                metas[2]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(3)
 | 
			
		||||
                              .transaction_blob()) ==
 | 
			
		||||
                transactions[3]->getSerializer().slice());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(
 | 
			
		||||
                makeSlice(reply.transactions_list()
 | 
			
		||||
                              .transactions(3)
 | 
			
		||||
                              .metadata_blob()) ==
 | 
			
		||||
                metas[3]->getSerializer().slice());
 | 
			
		||||
            BEAST_EXPECT(reply.skiplist_included());
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(s.slice() == makeSlice(reply.ledger_header()));
 | 
			
		||||
 | 
			
		||||
            auto parent = env.app().getLedgerMaster().getLedgerBySeq(3);
 | 
			
		||||
 | 
			
		||||
            SHAMap::Delta differences;
 | 
			
		||||
 | 
			
		||||
            int maxDifferences = std::numeric_limits<int>::max();
 | 
			
		||||
 | 
			
		||||
            bool res = parent->stateMap().compare(
 | 
			
		||||
                ledger->stateMap(), differences, maxDifferences);
 | 
			
		||||
            BEAST_EXPECT(res);
 | 
			
		||||
 | 
			
		||||
            size_t idx = 0;
 | 
			
		||||
 | 
			
		||||
            for (auto& [k, v] : differences)
 | 
			
		||||
            {
 | 
			
		||||
                auto obj = reply.ledger_objects().objects(idx);
 | 
			
		||||
                BEAST_EXPECT(k == uint256::fromVoid(obj.key().data()));
 | 
			
		||||
                if (v.second)
 | 
			
		||||
                {
 | 
			
		||||
                    BEAST_EXPECT(v.second->slice() == makeSlice(obj.data()));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    BEAST_EXPECT(obj.data().size() == 0);
 | 
			
		||||
 | 
			
		||||
                if (!(v.first && v.second))
 | 
			
		||||
                {
 | 
			
		||||
                    auto succ = ledger->stateMap().upper_bound(k);
 | 
			
		||||
                    auto pred = ledger->stateMap().lower_bound(k);
 | 
			
		||||
 | 
			
		||||
                    if (succ != ledger->stateMap().end())
 | 
			
		||||
                        BEAST_EXPECT(
 | 
			
		||||
                            succ->key() ==
 | 
			
		||||
                            uint256::fromVoid(obj.successor().data()));
 | 
			
		||||
                    else
 | 
			
		||||
                        BEAST_EXPECT(obj.successor().size() == 0);
 | 
			
		||||
                    if (pred != ledger->stateMap().end())
 | 
			
		||||
                        BEAST_EXPECT(
 | 
			
		||||
                            pred->key() ==
 | 
			
		||||
                            uint256::fromVoid(obj.predecessor().data()));
 | 
			
		||||
                    else
 | 
			
		||||
                        BEAST_EXPECT(obj.predecessor().size() == 0);
 | 
			
		||||
                }
 | 
			
		||||
                ++idx;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Delete an account
 | 
			
		||||
 | 
			
		||||
@@ -312,7 +420,7 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto [status, reply] =
 | 
			
		||||
                grpcLedger(env.closed()->seq(), true, true, true);
 | 
			
		||||
                grpcLedger(env.closed()->seq(), true, true, true, true);
 | 
			
		||||
 | 
			
		||||
            BEAST_EXPECT(status.ok());
 | 
			
		||||
            BEAST_EXPECT(reply.validated());
 | 
			
		||||
@@ -333,16 +441,34 @@ class ReportingETL_test : public beast::unit_test::suite
 | 
			
		||||
            size_t idx = 0;
 | 
			
		||||
            for (auto& [k, v] : differences)
 | 
			
		||||
            {
 | 
			
		||||
                BEAST_EXPECT(
 | 
			
		||||
                    k ==
 | 
			
		||||
                    uint256::fromVoid(
 | 
			
		||||
                        reply.ledger_objects().objects(idx).key().data()));
 | 
			
		||||
                auto obj = reply.ledger_objects().objects(idx);
 | 
			
		||||
                BEAST_EXPECT(k == uint256::fromVoid(obj.key().data()));
 | 
			
		||||
                if (v.second)
 | 
			
		||||
                {
 | 
			
		||||
                    BEAST_EXPECT(
 | 
			
		||||
                        v.second->slice() ==
 | 
			
		||||
                        makeSlice(reply.ledger_objects().objects(idx).data()));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    BEAST_EXPECT(obj.data().size() == 0);
 | 
			
		||||
                if (!(v.first && v.second))
 | 
			
		||||
                {
 | 
			
		||||
                    auto succ = base->stateMap().upper_bound(k);
 | 
			
		||||
                    auto pred = base->stateMap().lower_bound(k);
 | 
			
		||||
 | 
			
		||||
                    if (succ != base->stateMap().end())
 | 
			
		||||
                        BEAST_EXPECT(
 | 
			
		||||
                            succ->key() ==
 | 
			
		||||
                            uint256::fromVoid(obj.successor().data()));
 | 
			
		||||
                    else
 | 
			
		||||
                        BEAST_EXPECT(obj.successor().size() == 0);
 | 
			
		||||
                    if (pred != base->stateMap().end())
 | 
			
		||||
                        BEAST_EXPECT(
 | 
			
		||||
                            pred->key() ==
 | 
			
		||||
                            uint256::fromVoid(obj.predecessor().data()));
 | 
			
		||||
                    else
 | 
			
		||||
                        BEAST_EXPECT(obj.predecessor().size() == 0);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ++idx;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user