Iterative amount/balance visitor (#1257)
* Iterative amount/balance visitor * Factor out epilogue
This commit is contained in:
parent
376a80d3b4
commit
35844371ab
4 changed files with 267 additions and 165 deletions
|
@ -1076,9 +1076,8 @@ void rai::mdb_store::clear (MDB_dbi db_a)
|
|||
|
||||
rai::uint128_t rai::mdb_store::block_balance (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
|
||||
{
|
||||
balance_visitor visitor (transaction_a, *this);
|
||||
visitor.compute (hash_a);
|
||||
return visitor.balance;
|
||||
summation_visitor visitor (transaction_a, *this);
|
||||
return visitor.compute_balance (hash_a);
|
||||
}
|
||||
|
||||
rai::epoch rai::mdb_store::block_version (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
|
||||
|
|
|
@ -1,171 +1,246 @@
|
|||
#include <queue>
|
||||
#include <rai/node/common.hpp>
|
||||
#include <rai/node/wallet.hpp>
|
||||
#include <rai/secure/blockstore.hpp>
|
||||
|
||||
#include <boost/polymorphic_cast.hpp>
|
||||
|
||||
rai::amount_visitor::amount_visitor (rai::transaction const & transaction_a, rai::block_store & store_a) :
|
||||
rai::summation_visitor::summation_visitor (rai::transaction const & transaction_a, rai::block_store & store_a) :
|
||||
transaction (transaction_a),
|
||||
store (store_a),
|
||||
current_amount (0),
|
||||
current_balance (0),
|
||||
amount (0)
|
||||
store (store_a)
|
||||
{
|
||||
}
|
||||
|
||||
void rai::amount_visitor::send_block (rai::send_block const & block_a)
|
||||
void rai::summation_visitor::send_block (rai::send_block const & block_a)
|
||||
{
|
||||
current_balance = block_a.hashables.previous;
|
||||
amount = block_a.hashables.balance.number ();
|
||||
current_amount = 0;
|
||||
}
|
||||
|
||||
void rai::amount_visitor::receive_block (rai::receive_block const & block_a)
|
||||
{
|
||||
current_amount = block_a.hashables.source;
|
||||
}
|
||||
|
||||
void rai::amount_visitor::open_block (rai::open_block const & block_a)
|
||||
{
|
||||
if (block_a.hashables.source != rai::genesis_account)
|
||||
assert (current->type != summation_type::invalid && current != nullptr);
|
||||
if (current->type == summation_type::amount)
|
||||
{
|
||||
current_amount = block_a.hashables.source;
|
||||
sum_set (block_a.hashables.balance.number ());
|
||||
current->balance_hash = block_a.hashables.previous;
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = rai::genesis_amount;
|
||||
current_amount = 0;
|
||||
sum_add (block_a.hashables.balance.number ());
|
||||
current->balance_hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void rai::amount_visitor::state_block (rai::state_block const & block_a)
|
||||
void rai::summation_visitor::state_block (rai::state_block const & block_a)
|
||||
{
|
||||
current_balance = block_a.hashables.previous;
|
||||
amount = block_a.hashables.balance.number ();
|
||||
current_amount = 0;
|
||||
}
|
||||
|
||||
void rai::amount_visitor::change_block (rai::change_block const & block_a)
|
||||
{
|
||||
amount = 0;
|
||||
current_amount = 0;
|
||||
}
|
||||
|
||||
void rai::amount_visitor::compute (rai::block_hash const & block_hash)
|
||||
{
|
||||
current_amount = block_hash;
|
||||
while (!current_amount.is_zero () || !current_balance.is_zero ())
|
||||
assert (current->type != summation_type::invalid && current != nullptr);
|
||||
sum_set (block_a.hashables.balance.number ());
|
||||
if (current->type == summation_type::amount)
|
||||
{
|
||||
if (!current_amount.is_zero ())
|
||||
current->balance_hash = block_a.hashables.previous;
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->balance_hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void rai::summation_visitor::receive_block (rai::receive_block const & block_a)
|
||||
{
|
||||
assert (current->type != summation_type::invalid && current != nullptr);
|
||||
if (current->type == summation_type::amount)
|
||||
{
|
||||
current->amount_hash = block_a.hashables.source;
|
||||
}
|
||||
else
|
||||
{
|
||||
rai::block_info block_info;
|
||||
if (!store.block_info_get (transaction, block_a.hash (), block_info))
|
||||
{
|
||||
auto block (store.block_get (transaction, current_amount));
|
||||
if (block != nullptr)
|
||||
sum_add (block_info.balance.number ());
|
||||
current->balance_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->amount_hash = block_a.hashables.source;
|
||||
current->balance_hash = block_a.hashables.previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rai::summation_visitor::open_block (rai::open_block const & block_a)
|
||||
{
|
||||
assert (current->type != summation_type::invalid && current != nullptr);
|
||||
if (current->type == summation_type::amount)
|
||||
{
|
||||
if (block_a.hashables.source != rai::genesis_account)
|
||||
{
|
||||
current->amount_hash = block_a.hashables.source;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum_set (rai::genesis_amount);
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current->amount_hash = block_a.hashables.source;
|
||||
current->balance_hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void rai::summation_visitor::change_block (rai::change_block const & block_a)
|
||||
{
|
||||
assert (current->type != summation_type::invalid && current != nullptr);
|
||||
if (current->type == summation_type::amount)
|
||||
{
|
||||
sum_set (0);
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rai::block_info block_info;
|
||||
if (!store.block_info_get (transaction, block_a.hash (), block_info))
|
||||
{
|
||||
sum_add (block_info.balance.number ());
|
||||
current->balance_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->balance_hash = block_a.hashables.previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rai::summation_visitor::frame rai::summation_visitor::push (rai::summation_visitor::summation_type type_a, rai::block_hash const & hash_a)
|
||||
{
|
||||
frames.emplace (type_a, type_a == summation_type::balance ? hash_a : 0, type_a == summation_type::amount ? hash_a : 0);
|
||||
return frames.top ();
|
||||
}
|
||||
|
||||
void rai::summation_visitor::sum_add (rai::uint128_t addend_a)
|
||||
{
|
||||
current->sum += addend_a;
|
||||
result = current->sum;
|
||||
}
|
||||
|
||||
void rai::summation_visitor::sum_set (rai::uint128_t value_a)
|
||||
{
|
||||
current->sum = value_a;
|
||||
result = current->sum;
|
||||
}
|
||||
|
||||
rai::uint128_t rai::summation_visitor::compute_internal (rai::summation_visitor::summation_type type_a, rai::block_hash const & hash_a)
|
||||
{
|
||||
push (type_a, hash_a);
|
||||
|
||||
/*
|
||||
Invocation loop representing balance and amount computations calling each other.
|
||||
This is usually better done by recursion or something like boost::coroutine2, but
|
||||
segmented stacks are not supported on all platforms so we do it manually to avoid
|
||||
stack overflow (the mutual calls are not tail-recursive so we cannot rely on the
|
||||
compiler optimizing that into a loop, though a future alternative is to do a
|
||||
CPS-style implementation to enforce tail calls.)
|
||||
*/
|
||||
while (frames.size () > 0)
|
||||
{
|
||||
current = &frames.top ();
|
||||
assert (current->type != summation_type::invalid && current != nullptr);
|
||||
|
||||
if (current->type == summation_type::balance)
|
||||
{
|
||||
if (current->awaiting_result)
|
||||
{
|
||||
block->visit (*this);
|
||||
sum_add (current->incoming_result);
|
||||
current->awaiting_result = false;
|
||||
}
|
||||
else
|
||||
|
||||
while (!current->awaiting_result && (!current->balance_hash.is_zero () || !current->amount_hash.is_zero ()))
|
||||
{
|
||||
if (block_hash == rai::genesis_account)
|
||||
if (!current->amount_hash.is_zero ())
|
||||
{
|
||||
amount = std::numeric_limits<rai::uint128_t>::max ();
|
||||
current_amount = 0;
|
||||
// Compute amount
|
||||
current->awaiting_result = true;
|
||||
push (summation_type::amount, current->amount_hash);
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (false);
|
||||
amount = 0;
|
||||
current_amount = 0;
|
||||
auto block (store.block_get (transaction, current->balance_hash));
|
||||
assert (block != nullptr);
|
||||
block->visit (*this);
|
||||
}
|
||||
}
|
||||
|
||||
epilogue ();
|
||||
}
|
||||
else
|
||||
else if (current->type == summation_type::amount)
|
||||
{
|
||||
balance_visitor prev (transaction, store);
|
||||
prev.compute (current_balance);
|
||||
amount = amount < prev.balance ? prev.balance - amount : amount - prev.balance;
|
||||
current_balance = 0;
|
||||
if (current->awaiting_result)
|
||||
{
|
||||
sum_set (current->sum < current->incoming_result ? current->incoming_result - current->sum : current->sum - current->incoming_result);
|
||||
current->awaiting_result = false;
|
||||
}
|
||||
|
||||
while (!current->awaiting_result && (!current->amount_hash.is_zero () || !current->balance_hash.is_zero ()))
|
||||
{
|
||||
if (!current->amount_hash.is_zero ())
|
||||
{
|
||||
auto block (store.block_get (transaction, current->amount_hash));
|
||||
if (block != nullptr)
|
||||
{
|
||||
block->visit (*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (current->amount_hash == rai::genesis_account)
|
||||
{
|
||||
sum_set (std::numeric_limits<rai::uint128_t>::max ());
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (false);
|
||||
sum_set (0);
|
||||
current->amount_hash = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute balance
|
||||
current->awaiting_result = true;
|
||||
push (summation_type::balance, current->balance_hash);
|
||||
current->balance_hash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
epilogue ();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
rai::balance_visitor::balance_visitor (rai::transaction const & transaction_a, rai::block_store & store_a) :
|
||||
transaction (transaction_a),
|
||||
store (store_a),
|
||||
current_balance (0),
|
||||
current_amount (0),
|
||||
balance (0)
|
||||
void rai::summation_visitor::epilogue ()
|
||||
{
|
||||
}
|
||||
|
||||
void rai::balance_visitor::send_block (rai::send_block const & block_a)
|
||||
{
|
||||
balance += block_a.hashables.balance.number ();
|
||||
current_balance = 0;
|
||||
}
|
||||
|
||||
void rai::balance_visitor::receive_block (rai::receive_block const & block_a)
|
||||
{
|
||||
rai::block_info block_info;
|
||||
if (!store.block_info_get (transaction, block_a.hash (), block_info))
|
||||
if (!current->awaiting_result)
|
||||
{
|
||||
balance += block_info.balance.number ();
|
||||
current_balance = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_amount = block_a.hashables.source;
|
||||
current_balance = block_a.hashables.previous;
|
||||
}
|
||||
}
|
||||
|
||||
void rai::balance_visitor::open_block (rai::open_block const & block_a)
|
||||
{
|
||||
current_amount = block_a.hashables.source;
|
||||
current_balance = 0;
|
||||
}
|
||||
|
||||
void rai::balance_visitor::change_block (rai::change_block const & block_a)
|
||||
{
|
||||
rai::block_info block_info;
|
||||
if (!store.block_info_get (transaction, block_a.hash (), block_info))
|
||||
{
|
||||
balance += block_info.balance.number ();
|
||||
current_balance = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_balance = block_a.hashables.previous;
|
||||
}
|
||||
}
|
||||
|
||||
void rai::balance_visitor::state_block (rai::state_block const & block_a)
|
||||
{
|
||||
balance = block_a.hashables.balance.number ();
|
||||
current_balance = 0;
|
||||
}
|
||||
|
||||
void rai::balance_visitor::compute (rai::block_hash const & block_hash)
|
||||
{
|
||||
current_balance = block_hash;
|
||||
while (!current_balance.is_zero () || !current_amount.is_zero ())
|
||||
{
|
||||
if (!current_amount.is_zero ())
|
||||
frames.pop ();
|
||||
if (frames.size () > 0)
|
||||
{
|
||||
amount_visitor source (transaction, store);
|
||||
source.compute (current_amount);
|
||||
balance += source.amount;
|
||||
current_amount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto block (store.block_get (transaction, current_balance));
|
||||
assert (block != nullptr);
|
||||
block->visit (*this);
|
||||
frames.top ().incoming_result = current->sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rai::uint128_t rai::summation_visitor::compute_amount (rai::block_hash const & block_hash)
|
||||
{
|
||||
return compute_internal (summation_type::amount, block_hash);
|
||||
}
|
||||
|
||||
rai::uint128_t rai::summation_visitor::compute_balance (rai::block_hash const & block_hash)
|
||||
{
|
||||
return compute_internal (summation_type::balance, block_hash);
|
||||
}
|
||||
|
||||
rai::representative_visitor::representative_visitor (rai::transaction const & transaction_a, rai::block_store & store_a) :
|
||||
transaction (transaction_a),
|
||||
store (store_a),
|
||||
|
|
|
@ -1,52 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <rai/secure/common.hpp>
|
||||
#include <stack>
|
||||
|
||||
namespace rai
|
||||
{
|
||||
class transaction;
|
||||
class block_store;
|
||||
/**
|
||||
* Determine the balance as of this block
|
||||
*/
|
||||
class balance_visitor : public rai::block_visitor
|
||||
{
|
||||
public:
|
||||
balance_visitor (rai::transaction const &, rai::block_store &);
|
||||
virtual ~balance_visitor () = default;
|
||||
void compute (rai::block_hash const &);
|
||||
void send_block (rai::send_block const &) override;
|
||||
void receive_block (rai::receive_block const &) override;
|
||||
void open_block (rai::open_block const &) override;
|
||||
void change_block (rai::change_block const &) override;
|
||||
void state_block (rai::state_block const &) override;
|
||||
rai::transaction const & transaction;
|
||||
rai::block_store & store;
|
||||
rai::block_hash current_balance;
|
||||
rai::block_hash current_amount;
|
||||
rai::uint128_t balance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine the amount delta resultant from this block
|
||||
* Summation visitor for blocks, supporting amount and balance computations. These
|
||||
* computations are mutually dependant. The natural solution is to use mutual recursion
|
||||
* between balance and amount visitors, but this leads to very deep stacks. Hence, the
|
||||
* summation visitor uses an iterative approach.
|
||||
*/
|
||||
class amount_visitor : public rai::block_visitor
|
||||
class summation_visitor : public rai::block_visitor
|
||||
{
|
||||
enum summation_type
|
||||
{
|
||||
invalid = 0,
|
||||
balance = 1,
|
||||
amount = 2
|
||||
};
|
||||
|
||||
/** Represents an invocation frame */
|
||||
class frame
|
||||
{
|
||||
public:
|
||||
frame (summation_type type_a, rai::block_hash balance_hash_a, rai::block_hash amount_hash_a) :
|
||||
type (type_a), balance_hash (balance_hash_a), amount_hash (amount_hash_a)
|
||||
{
|
||||
}
|
||||
|
||||
/** The summation type guides the block visitor handlers */
|
||||
summation_type type{ invalid };
|
||||
/** Accumulated balance or amount */
|
||||
rai::uint128_t sum{ 0 };
|
||||
/** The current balance hash */
|
||||
rai::block_hash balance_hash{ 0 };
|
||||
/** The current amount hash */
|
||||
rai::block_hash amount_hash{ 0 };
|
||||
/** If true, this frame is awaiting an invocation result */
|
||||
bool awaiting_result{ false };
|
||||
/** Set by the invoked frame, representing the return value */
|
||||
rai::uint128_t incoming_result{ 0 };
|
||||
};
|
||||
|
||||
public:
|
||||
amount_visitor (rai::transaction const &, rai::block_store &);
|
||||
virtual ~amount_visitor () = default;
|
||||
void compute (rai::block_hash const &);
|
||||
summation_visitor (rai::transaction const &, rai::block_store &);
|
||||
virtual ~summation_visitor () = default;
|
||||
/** Computes the balance as of \p block_hash */
|
||||
rai::uint128_t compute_balance (rai::block_hash const & block_hash);
|
||||
/** Computes the amount delta between \p block_hash and its predecessor */
|
||||
rai::uint128_t compute_amount (rai::block_hash const & block_hash);
|
||||
|
||||
protected:
|
||||
rai::transaction const & transaction;
|
||||
rai::block_store & store;
|
||||
|
||||
/** The final result */
|
||||
rai::uint128_t result{ 0 };
|
||||
/** The current invocation frame */
|
||||
frame * current{ nullptr };
|
||||
/** Invocation frames */
|
||||
std::stack<frame> frames;
|
||||
/** Push a copy of \p hash of the given summation \p type */
|
||||
rai::summation_visitor::frame push (rai::summation_visitor::summation_type type, rai::block_hash const & hash);
|
||||
void sum_add (rai::uint128_t addend_a);
|
||||
void sum_set (rai::uint128_t value_a);
|
||||
/** The epilogue yields the result to previous frame, if any */
|
||||
void epilogue ();
|
||||
|
||||
rai::uint128_t compute_internal (rai::summation_visitor::summation_type type, rai::block_hash const &);
|
||||
void send_block (rai::send_block const &) override;
|
||||
void receive_block (rai::receive_block const &) override;
|
||||
void open_block (rai::open_block const &) override;
|
||||
void change_block (rai::change_block const &) override;
|
||||
void state_block (rai::state_block const &) override;
|
||||
void from_send (rai::block_hash const &);
|
||||
rai::transaction const & transaction;
|
||||
rai::block_store & store;
|
||||
rai::block_hash current_amount;
|
||||
rai::block_hash current_balance;
|
||||
rai::uint128_t amount;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -623,9 +623,8 @@ epoch_signer (epoch_signer_a)
|
|||
// Balance for account containing hash
|
||||
rai::uint128_t rai::ledger::balance (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
|
||||
{
|
||||
rai::balance_visitor visitor (transaction_a, store);
|
||||
visitor.compute (hash_a);
|
||||
return visitor.balance;
|
||||
rai::summation_visitor visitor (transaction_a, store);
|
||||
return visitor.compute_balance (hash_a);
|
||||
}
|
||||
|
||||
// Balance for an account by account number
|
||||
|
@ -828,9 +827,8 @@ rai::account rai::ledger::account (rai::transaction const & transaction_a, rai::
|
|||
// Return amount decrease or increase for block
|
||||
rai::uint128_t rai::ledger::amount (rai::transaction const & transaction_a, rai::block_hash const & hash_a)
|
||||
{
|
||||
amount_visitor amount (transaction_a, store);
|
||||
amount.compute (hash_a);
|
||||
return amount.amount;
|
||||
summation_visitor amount (transaction_a, store);
|
||||
return amount.compute_amount (hash_a);
|
||||
}
|
||||
|
||||
// Return latest block for account
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue