Refactoring: Split secure.*pp into common, ledger and blockstore, and move working_path to utility, where it was declared anyway (#662)

This commit is contained in:
cryptocode 2018-02-22 22:03:21 +01:00 committed by Russel Waters
commit 702da39639
18 changed files with 3227 additions and 3141 deletions

View file

@ -266,8 +266,12 @@ add_library (secure
${PLATFORM_SECURE_SOURCE}
${CMAKE_BINARY_DIR}/bootstrap_weights.cpp
rai/config.hpp
rai/secure.cpp
rai/secure.hpp
rai/common.cpp
rai/common.hpp
rai/blockstore.cpp
rai/blockstore.hpp
rai/ledger.cpp
rai/ledger.hpp
rai/node/utility.cpp
rai/node/utility.hpp
rai/versioning.hpp

1280
rai/blockstore.cpp Normal file

File diff suppressed because it is too large Load diff

183
rai/blockstore.hpp Normal file
View file

@ -0,0 +1,183 @@
#pragma once
#include <rai/common.hpp>
namespace rai
{
/**
* The value produced when iterating with \ref store_iterator
*/
class store_entry
{
public:
store_entry ();
void clear ();
store_entry * operator-> ();
rai::mdb_val first;
rai::mdb_val second;
};
/**
* Iterates the key/value pairs of a transaction
*/
class store_iterator
{
public:
store_iterator (MDB_txn *, MDB_dbi);
store_iterator (std::nullptr_t);
store_iterator (MDB_txn *, MDB_dbi, MDB_val const &);
store_iterator (rai::store_iterator &&);
store_iterator (rai::store_iterator const &) = delete;
~store_iterator ();
rai::store_iterator & operator++ ();
void next_dup ();
rai::store_iterator & operator= (rai::store_iterator &&);
rai::store_iterator & operator= (rai::store_iterator const &) = delete;
rai::store_entry & operator-> ();
bool operator== (rai::store_iterator const &) const;
bool operator!= (rai::store_iterator const &) const;
MDB_cursor * cursor;
rai::store_entry current;
};
/**
* Manages block storage and iteration
*/
class block_store
{
public:
block_store (bool &, boost::filesystem::path const &, int lmdb_max_dbs = 128);
MDB_dbi block_database (rai::block_type);
void block_put_raw (MDB_txn *, MDB_dbi, rai::block_hash const &, MDB_val);
void block_put (MDB_txn *, rai::block_hash const &, rai::block const &, rai::block_hash const & = rai::block_hash (0));
MDB_val block_get_raw (MDB_txn *, rai::block_hash const &, rai::block_type &);
rai::block_hash block_successor (MDB_txn *, rai::block_hash const &);
void block_successor_clear (MDB_txn *, rai::block_hash const &);
std::unique_ptr<rai::block> block_get (MDB_txn *, rai::block_hash const &);
std::unique_ptr<rai::block> block_random (MDB_txn *);
std::unique_ptr<rai::block> block_random (MDB_txn *, MDB_dbi);
void block_del (MDB_txn *, rai::block_hash const &);
bool block_exists (MDB_txn *, rai::block_hash const &);
rai::block_counts block_count (MDB_txn *);
std::unordered_multimap<rai::block_hash, rai::block_hash> block_dependencies (MDB_txn *);
void frontier_put (MDB_txn *, rai::block_hash const &, rai::account const &);
rai::account frontier_get (MDB_txn *, rai::block_hash const &);
void frontier_del (MDB_txn *, rai::block_hash const &);
size_t frontier_count (MDB_txn *);
void account_put (MDB_txn *, rai::account const &, rai::account_info const &);
bool account_get (MDB_txn *, rai::account const &, rai::account_info &);
void account_del (MDB_txn *, rai::account const &);
bool account_exists (MDB_txn *, rai::account const &);
rai::store_iterator latest_begin (MDB_txn *, rai::account const &);
rai::store_iterator latest_begin (MDB_txn *);
rai::store_iterator latest_end ();
void pending_put (MDB_txn *, rai::pending_key const &, rai::pending_info const &);
void pending_del (MDB_txn *, rai::pending_key const &);
bool pending_get (MDB_txn *, rai::pending_key const &, rai::pending_info &);
bool pending_exists (MDB_txn *, rai::pending_key const &);
rai::store_iterator pending_begin (MDB_txn *, rai::pending_key const &);
rai::store_iterator pending_begin (MDB_txn *);
rai::store_iterator pending_end ();
void block_info_put (MDB_txn *, rai::block_hash const &, rai::block_info const &);
void block_info_del (MDB_txn *, rai::block_hash const &);
bool block_info_get (MDB_txn *, rai::block_hash const &, rai::block_info &);
bool block_info_exists (MDB_txn *, rai::block_hash const &);
rai::store_iterator block_info_begin (MDB_txn *, rai::block_hash const &);
rai::store_iterator block_info_begin (MDB_txn *);
rai::store_iterator block_info_end ();
rai::uint128_t block_balance (MDB_txn *, rai::block_hash const &);
static size_t const block_info_max = 32;
rai::uint128_t representation_get (MDB_txn *, rai::account const &);
void representation_put (MDB_txn *, rai::account const &, rai::uint128_t const &);
void representation_add (MDB_txn *, rai::account const &, rai::uint128_t const &);
rai::store_iterator representation_begin (MDB_txn *);
rai::store_iterator representation_end ();
void unchecked_clear (MDB_txn *);
void unchecked_put (MDB_txn *, rai::block_hash const &, std::shared_ptr<rai::block> const &);
std::vector<std::shared_ptr<rai::block>> unchecked_get (MDB_txn *, rai::block_hash const &);
void unchecked_del (MDB_txn *, rai::block_hash const &, rai::block const &);
rai::store_iterator unchecked_begin (MDB_txn *);
rai::store_iterator unchecked_begin (MDB_txn *, rai::block_hash const &);
rai::store_iterator unchecked_end ();
size_t unchecked_count (MDB_txn *);
std::unordered_multimap<rai::block_hash, std::shared_ptr<rai::block>> unchecked_cache;
void unsynced_put (MDB_txn *, rai::block_hash const &);
void unsynced_del (MDB_txn *, rai::block_hash const &);
bool unsynced_exists (MDB_txn *, rai::block_hash const &);
rai::store_iterator unsynced_begin (MDB_txn *, rai::block_hash const &);
rai::store_iterator unsynced_begin (MDB_txn *);
rai::store_iterator unsynced_end ();
void checksum_put (MDB_txn *, uint64_t, uint8_t, rai::checksum const &);
bool checksum_get (MDB_txn *, uint64_t, uint8_t, rai::checksum &);
void checksum_del (MDB_txn *, uint64_t, uint8_t);
rai::vote_result vote_validate (MDB_txn *, std::shared_ptr<rai::vote>);
// Return latest vote for an account from store
std::shared_ptr<rai::vote> vote_get (MDB_txn *, rai::account const &);
// Populate vote with the next sequence number
std::shared_ptr<rai::vote> vote_generate (MDB_txn *, rai::account const &, rai::raw_key const &, std::shared_ptr<rai::block>);
// Return either vote or the stored vote with a higher sequence number
std::shared_ptr<rai::vote> vote_max (MDB_txn *, std::shared_ptr<rai::vote>);
// Return latest vote for an account considering the vote cache
std::shared_ptr<rai::vote> vote_current (MDB_txn *, rai::account const &);
void flush (MDB_txn *);
rai::store_iterator vote_begin (MDB_txn *);
rai::store_iterator vote_end ();
std::mutex cache_mutex;
std::unordered_map<rai::account, std::shared_ptr<rai::vote>> vote_cache;
void version_put (MDB_txn *, int);
int version_get (MDB_txn *);
void do_upgrades (MDB_txn *);
void upgrade_v1_to_v2 (MDB_txn *);
void upgrade_v2_to_v3 (MDB_txn *);
void upgrade_v3_to_v4 (MDB_txn *);
void upgrade_v4_to_v5 (MDB_txn *);
void upgrade_v5_to_v6 (MDB_txn *);
void upgrade_v6_to_v7 (MDB_txn *);
void upgrade_v7_to_v8 (MDB_txn *);
void upgrade_v8_to_v9 (MDB_txn *);
void upgrade_v9_to_v10 (MDB_txn *);
void clear (MDB_dbi);
rai::mdb_env environment;
// block_hash -> account // Maps head blocks to owning account
MDB_dbi frontiers;
// account -> block_hash, representative, balance, timestamp // Account to head block, representative, balance, last_change
MDB_dbi accounts;
// block_hash -> send_block
MDB_dbi send_blocks;
// block_hash -> receive_block
MDB_dbi receive_blocks;
// block_hash -> open_block
MDB_dbi open_blocks;
// block_hash -> change_block
MDB_dbi change_blocks;
// block_hash -> sender, amount, destination // Pending blocks to sender account, amount, destination account
MDB_dbi pending;
// block_hash -> account, balance // Blocks info
MDB_dbi blocks_info;
// account -> weight // Representation
MDB_dbi representation;
// block_hash -> block // Unchecked bootstrap blocks
MDB_dbi unchecked;
// block_hash -> // Blocks that haven't been broadcast
MDB_dbi unsynced;
// (uint56_t, uint8_t) -> block_hash // Mapping of region to checksum
MDB_dbi checksum;
// account -> uint64_t // Highest vote observed for account
MDB_dbi vote;
// uint256_union -> ? // Meta information about block store
MDB_dbi meta;
};
}

755
rai/common.cpp Normal file
View file

@ -0,0 +1,755 @@
#include <rai/common.hpp>
#include <rai/blockstore.hpp>
#include <rai/lib/interface.h>
#include <rai/node/common.hpp>
#include <rai/versioning.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <queue>
#include <ed25519-donna/ed25519.h>
// Genesis keys for network variants
namespace
{
char const * test_private_key_data = "34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4";
char const * test_public_key_data = "B0311EA55708D6A53C75CDBF88300259C6D018522FE3D4D0A242E431F9E8B6D0"; // xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo
char const * beta_public_key_data = "9D3A5B66B478670455B241D6BAC3D3FE1CBB7E7B7EAA429FA036C2704C3DC0A4"; // xrb_39btdfmday591jcu6igpqd3x9ziwqfz9pzocacht1fp4g385ui76a87x6phk
char const * live_public_key_data = "E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA"; // xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3
char const * test_genesis_data = R"%%%({
"type": "open",
"source": "B0311EA55708D6A53C75CDBF88300259C6D018522FE3D4D0A242E431F9E8B6D0",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo",
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo",
"work": "9680625b39d3363d",
"signature": "ECDA914373A2F0CA1296475BAEE40500A7F0A7AD72A5A80C81D7FAB7F6C802B2CC7DB50F5DD0FB25B2EF11761FA7344A158DD5A700B21BD47DE5BD0F63153A02"
})%%%";
char const * beta_genesis_data = R"%%%({
"type": "open",
"source": "9D3A5B66B478670455B241D6BAC3D3FE1CBB7E7B7EAA429FA036C2704C3DC0A4",
"representative": "xrb_39btdfmday591jcu6igpqd3x9ziwqfz9pzocacht1fp4g385ui76a87x6phk",
"account": "xrb_39btdfmday591jcu6igpqd3x9ziwqfz9pzocacht1fp4g385ui76a87x6phk",
"work": "6eb12d4c42dba31e",
"signature": "BD0D374FCEB33EAABDF728E9B4DCDBF3B226DA97EEAB8EA5B7EDE286B1282C24D6EB544644FE871235E4F58CD94DF66D9C555309895F67A7D1F922AAC12CE907"
})%%%";
char const * live_genesis_data = R"%%%({
"type": "open",
"source": "E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA",
"representative": "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",
"account": "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",
"work": "62f05417dd3fb691",
"signature": "9F0C933C8ADE004D808EA1985FA746A7E95BA2A38F867640F53EC8F180BDFE9E2C1268DEAD7C2664F356E37ABA362BC58E46DBA03E523A7B5A19E4B6EB12BB02"
})%%%";
class ledger_constants
{
public:
ledger_constants () :
zero_key ("0"),
test_genesis_key (test_private_key_data),
rai_test_account (test_public_key_data),
rai_beta_account (beta_public_key_data),
rai_live_account (live_public_key_data),
rai_test_genesis (test_genesis_data),
rai_beta_genesis (beta_genesis_data),
rai_live_genesis (live_genesis_data),
genesis_account (rai::rai_network == rai::rai_networks::rai_test_network ? rai_test_account : rai::rai_network == rai::rai_networks::rai_beta_network ? rai_beta_account : rai_live_account),
genesis_block (rai::rai_network == rai::rai_networks::rai_test_network ? rai_test_genesis : rai::rai_network == rai::rai_networks::rai_beta_network ? rai_beta_genesis : rai_live_genesis),
genesis_amount (std::numeric_limits<rai::uint128_t>::max ()),
burn_account (0)
{
CryptoPP::AutoSeededRandomPool random_pool;
// Randomly generating these mean no two nodes will ever have the same sentinal values which protects against some insecure algorithms
random_pool.GenerateBlock (not_a_block.bytes.data (), not_a_block.bytes.size ());
random_pool.GenerateBlock (not_an_account.bytes.data (), not_an_account.bytes.size ());
}
rai::keypair zero_key;
rai::keypair test_genesis_key;
rai::account rai_test_account;
rai::account rai_beta_account;
rai::account rai_live_account;
std::string rai_test_genesis;
std::string rai_beta_genesis;
std::string rai_live_genesis;
rai::account genesis_account;
std::string genesis_block;
rai::uint128_t genesis_amount;
rai::block_hash not_a_block;
rai::account not_an_account;
rai::account burn_account;
};
ledger_constants globals;
}
size_t constexpr rai::send_block::size;
size_t constexpr rai::receive_block::size;
size_t constexpr rai::open_block::size;
size_t constexpr rai::change_block::size;
rai::keypair const & rai::zero_key (globals.zero_key);
rai::keypair const & rai::test_genesis_key (globals.test_genesis_key);
rai::account const & rai::rai_test_account (globals.rai_test_account);
rai::account const & rai::rai_beta_account (globals.rai_beta_account);
rai::account const & rai::rai_live_account (globals.rai_live_account);
std::string const & rai::rai_test_genesis (globals.rai_test_genesis);
std::string const & rai::rai_beta_genesis (globals.rai_beta_genesis);
std::string const & rai::rai_live_genesis (globals.rai_live_genesis);
rai::account const & rai::genesis_account (globals.genesis_account);
std::string const & rai::genesis_block (globals.genesis_block);
rai::uint128_t const & rai::genesis_amount (globals.genesis_amount);
rai::block_hash const & rai::not_a_block (globals.not_a_block);
rai::block_hash const & rai::not_an_account (globals.not_an_account);
rai::account const & rai::burn_account (globals.burn_account);
rai::votes::votes (std::shared_ptr<rai::block> block_a) :
id (block_a->root ())
{
rep_votes.insert (std::make_pair (rai::not_an_account, block_a));
}
rai::tally_result rai::votes::vote (std::shared_ptr<rai::vote> vote_a)
{
rai::tally_result result;
auto existing (rep_votes.find (vote_a->account));
if (existing == rep_votes.end ())
{
// Vote on this block hasn't been seen from rep before
result = rai::tally_result::vote;
rep_votes.insert (std::make_pair (vote_a->account, vote_a->block));
}
else
{
if (!(*existing->second == *vote_a->block))
{
// Rep changed their vote
result = rai::tally_result::changed;
existing->second = vote_a->block;
}
else
{
// Rep vote remained the same
result = rai::tally_result::confirm;
}
}
return result;
}
// Create a new random keypair
rai::keypair::keypair ()
{
random_pool.GenerateBlock (prv.data.bytes.data (), prv.data.bytes.size ());
ed25519_publickey (prv.data.bytes.data (), pub.bytes.data ());
}
// Create a keypair given a hex string of the private key
rai::keypair::keypair (std::string const & prv_a)
{
auto error (prv.data.decode_hex (prv_a));
assert (!error);
ed25519_publickey (prv.data.bytes.data (), pub.bytes.data ());
}
// Serialize a block prefixed with an 8-bit typecode
void rai::serialize_block (rai::stream & stream_a, rai::block const & block_a)
{
write (stream_a, block_a.type ());
block_a.serialize (stream_a);
}
std::unique_ptr<rai::block> rai::deserialize_block (MDB_val const & val_a)
{
rai::bufferstream stream (reinterpret_cast<uint8_t const *> (val_a.mv_data), val_a.mv_size);
return deserialize_block (stream);
}
rai::account_info::account_info () :
head (0),
rep_block (0),
open_block (0),
balance (0),
modified (0),
block_count (0)
{
}
rai::account_info::account_info (MDB_val const & val_a)
{
assert (val_a.mv_size == sizeof (*this));
static_assert (sizeof (head) + sizeof (rep_block) + sizeof (open_block) + sizeof (balance) + sizeof (modified) + sizeof (block_count) == sizeof (*this), "Class not packed");
std::copy (reinterpret_cast<uint8_t const *> (val_a.mv_data), reinterpret_cast<uint8_t const *> (val_a.mv_data) + sizeof (*this), reinterpret_cast<uint8_t *> (this));
}
rai::account_info::account_info (rai::block_hash const & head_a, rai::block_hash const & rep_block_a, rai::block_hash const & open_block_a, rai::amount const & balance_a, uint64_t modified_a, uint64_t block_count_a) :
head (head_a),
rep_block (rep_block_a),
open_block (open_block_a),
balance (balance_a),
modified (modified_a),
block_count (block_count_a)
{
}
void rai::account_info::serialize (rai::stream & stream_a) const
{
write (stream_a, head.bytes);
write (stream_a, rep_block.bytes);
write (stream_a, open_block.bytes);
write (stream_a, balance.bytes);
write (stream_a, modified);
write (stream_a, block_count);
}
bool rai::account_info::deserialize (rai::stream & stream_a)
{
auto error (read (stream_a, head.bytes));
if (!error)
{
error = read (stream_a, rep_block.bytes);
if (!error)
{
error = read (stream_a, open_block.bytes);
if (!error)
{
error = read (stream_a, balance.bytes);
if (!error)
{
error = read (stream_a, modified);
if (!error)
{
error = read (stream_a, block_count);
}
}
}
}
}
return error;
}
bool rai::account_info::operator== (rai::account_info const & other_a) const
{
return head == other_a.head && rep_block == other_a.rep_block && open_block == other_a.open_block && balance == other_a.balance && modified == other_a.modified && block_count == other_a.block_count;
}
bool rai::account_info::operator!= (rai::account_info const & other_a) const
{
return !(*this == other_a);
}
rai::mdb_val rai::account_info::val () const
{
return rai::mdb_val (sizeof (*this), const_cast<rai::account_info *> (this));
}
rai::block_counts::block_counts () :
send (0),
receive (0),
open (0),
change (0)
{
}
size_t rai::block_counts::sum ()
{
return send + receive + open + change;
}
rai::pending_info::pending_info () :
source (0),
amount (0)
{
}
rai::pending_info::pending_info (MDB_val const & val_a)
{
assert (val_a.mv_size == sizeof (*this));
static_assert (sizeof (source) + sizeof (amount) == sizeof (*this), "Packed class");
std::copy (reinterpret_cast<uint8_t const *> (val_a.mv_data), reinterpret_cast<uint8_t const *> (val_a.mv_data) + sizeof (*this), reinterpret_cast<uint8_t *> (this));
}
rai::pending_info::pending_info (rai::account const & source_a, rai::amount const & amount_a) :
source (source_a),
amount (amount_a)
{
}
void rai::pending_info::serialize (rai::stream & stream_a) const
{
rai::write (stream_a, source.bytes);
rai::write (stream_a, amount.bytes);
}
bool rai::pending_info::deserialize (rai::stream & stream_a)
{
auto result (rai::read (stream_a, source.bytes));
if (!result)
{
result = rai::read (stream_a, amount.bytes);
}
return result;
}
bool rai::pending_info::operator== (rai::pending_info const & other_a) const
{
return source == other_a.source && amount == other_a.amount;
}
rai::mdb_val rai::pending_info::val () const
{
return rai::mdb_val (sizeof (*this), const_cast<rai::pending_info *> (this));
}
rai::pending_key::pending_key (rai::account const & account_a, rai::block_hash const & hash_a) :
account (account_a),
hash (hash_a)
{
}
rai::pending_key::pending_key (MDB_val const & val_a)
{
assert (val_a.mv_size == sizeof (*this));
static_assert (sizeof (account) + sizeof (hash) == sizeof (*this), "Packed class");
std::copy (reinterpret_cast<uint8_t const *> (val_a.mv_data), reinterpret_cast<uint8_t const *> (val_a.mv_data) + sizeof (*this), reinterpret_cast<uint8_t *> (this));
}
void rai::pending_key::serialize (rai::stream & stream_a) const
{
rai::write (stream_a, account.bytes);
rai::write (stream_a, hash.bytes);
}
bool rai::pending_key::deserialize (rai::stream & stream_a)
{
auto error (rai::read (stream_a, account.bytes));
if (!error)
{
error = rai::read (stream_a, hash.bytes);
}
return error;
}
bool rai::pending_key::operator== (rai::pending_key const & other_a) const
{
return account == other_a.account && hash == other_a.hash;
}
rai::mdb_val rai::pending_key::val () const
{
return rai::mdb_val (sizeof (*this), const_cast<rai::pending_key *> (this));
}
rai::block_info::block_info () :
account (0),
balance (0)
{
}
rai::block_info::block_info (MDB_val const & val_a)
{
assert (val_a.mv_size == sizeof (*this));
static_assert (sizeof (account) + sizeof (balance) == sizeof (*this), "Packed class");
std::copy (reinterpret_cast<uint8_t const *> (val_a.mv_data), reinterpret_cast<uint8_t const *> (val_a.mv_data) + sizeof (*this), reinterpret_cast<uint8_t *> (this));
}
rai::block_info::block_info (rai::account const & account_a, rai::amount const & balance_a) :
account (account_a),
balance (balance_a)
{
}
void rai::block_info::serialize (rai::stream & stream_a) const
{
rai::write (stream_a, account.bytes);
rai::write (stream_a, balance.bytes);
}
bool rai::block_info::deserialize (rai::stream & stream_a)
{
auto error (rai::read (stream_a, account.bytes));
if (!error)
{
error = rai::read (stream_a, balance.bytes);
}
return error;
}
bool rai::block_info::operator== (rai::block_info const & other_a) const
{
return account == other_a.account && balance == other_a.balance;
}
rai::mdb_val rai::block_info::val () const
{
return rai::mdb_val (sizeof (*this), const_cast<rai::block_info *> (this));
}
bool rai::vote::operator== (rai::vote const & other_a) const
{
return sequence == other_a.sequence && *block == *other_a.block && account == other_a.account && signature == other_a.signature;
}
bool rai::vote::operator!= (rai::vote const & other_a) const
{
return !(*this == other_a);
}
std::string rai::vote::to_json () const
{
std::stringstream stream;
boost::property_tree::ptree tree;
tree.put ("account", account.to_account ());
tree.put ("signature", signature.number ());
tree.put ("sequence", std::to_string (sequence));
tree.put ("block", block->to_json ());
boost::property_tree::write_json (stream, tree);
return stream.str ();
}
namespace
{
class root_visitor : public rai::block_visitor
{
public:
root_visitor (rai::block_store & store_a) :
store (store_a)
{
}
virtual ~root_visitor () = default;
void send_block (rai::send_block const & block_a) override
{
result = block_a.previous ();
}
void receive_block (rai::receive_block const & block_a) override
{
result = block_a.previous ();
}
// Open blocks have no previous () so we use the account number
void open_block (rai::open_block const & block_a) override
{
rai::transaction transaction (store.environment, nullptr, false);
auto hash (block_a.source ());
auto source (store.block_get (transaction, hash));
if (source != nullptr)
{
auto send (dynamic_cast<rai::send_block *> (source.get ()));
if (send != nullptr)
{
result = send->hashables.destination;
}
else
{
result.clear ();
}
}
else
{
result.clear ();
}
}
void change_block (rai::change_block const & block_a) override
{
result = block_a.previous ();
}
rai::block_store & store;
rai::block_hash result;
};
} // namespace
rai::amount_visitor::amount_visitor (MDB_txn * transaction_a, rai::block_store & store_a) :
transaction (transaction_a),
store (store_a)
{
}
void rai::amount_visitor::send_block (rai::send_block const & block_a)
{
balance_visitor prev (transaction, store);
prev.compute (block_a.hashables.previous);
result = prev.result - block_a.hashables.balance.number ();
}
void rai::amount_visitor::receive_block (rai::receive_block const & block_a)
{
from_send (block_a.hashables.source);
}
void rai::amount_visitor::open_block (rai::open_block const & block_a)
{
if (block_a.hashables.source != rai::genesis_account)
{
from_send (block_a.hashables.source);
}
else
{
result = rai::genesis_amount;
}
}
void rai::amount_visitor::change_block (rai::change_block const & block_a)
{
result = 0;
}
void rai::amount_visitor::from_send (rai::block_hash const & hash_a)
{
auto source_block (store.block_get (transaction, hash_a));
assert (source_block != nullptr);
source_block->visit (*this);
}
void rai::amount_visitor::compute (rai::block_hash const & block_hash)
{
auto block (store.block_get (transaction, block_hash));
if (block != nullptr)
{
block->visit (*this);
}
else
{
if (block_hash == rai::genesis_account)
{
result = std::numeric_limits<rai::uint128_t>::max ();
}
else
{
assert (false);
result = 0;
}
}
}
rai::balance_visitor::balance_visitor (MDB_txn * transaction_a, rai::block_store & store_a) :
transaction (transaction_a),
store (store_a),
current (0),
result (0)
{
}
void rai::balance_visitor::send_block (rai::send_block const & block_a)
{
result += block_a.hashables.balance.number ();
current = 0;
}
void rai::balance_visitor::receive_block (rai::receive_block const & block_a)
{
amount_visitor source (transaction, store);
source.compute (block_a.hashables.source);
rai::block_info block_info;
if (!store.block_info_get (transaction, block_a.hash (), block_info))
{
result += block_info.balance.number ();
current = 0;
}
else
{
result += source.result;
current = block_a.hashables.previous;
}
}
void rai::balance_visitor::open_block (rai::open_block const & block_a)
{
amount_visitor source (transaction, store);
source.compute (block_a.hashables.source);
result += source.result;
current = 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))
{
result += block_info.balance.number ();
current = 0;
}
else
{
current = block_a.hashables.previous;
}
}
void rai::balance_visitor::compute (rai::block_hash const & block_hash)
{
current = block_hash;
while (!current.is_zero ())
{
auto block (store.block_get (transaction, current));
assert (block != nullptr);
block->visit (*this);
}
}
rai::representative_visitor::representative_visitor (MDB_txn * transaction_a, rai::block_store & store_a) :
transaction (transaction_a),
store (store_a),
result (0)
{
}
void rai::representative_visitor::compute (rai::block_hash const & hash_a)
{
current = hash_a;
while (result.is_zero ())
{
auto block (store.block_get (transaction, current));
assert (block != nullptr);
block->visit (*this);
}
}
void rai::representative_visitor::send_block (rai::send_block const & block_a)
{
current = block_a.previous ();
}
void rai::representative_visitor::receive_block (rai::receive_block const & block_a)
{
current = block_a.previous ();
}
void rai::representative_visitor::open_block (rai::open_block const & block_a)
{
result = block_a.hash ();
}
void rai::representative_visitor::change_block (rai::change_block const & block_a)
{
result = block_a.hash ();
}
rai::vote::vote (rai::vote const & other_a) :
sequence (other_a.sequence),
block (other_a.block),
account (other_a.account),
signature (other_a.signature)
{
}
rai::vote::vote (bool & error_a, rai::stream & stream_a)
{
if (!error_a)
{
error_a = rai::read (stream_a, account.bytes);
if (!error_a)
{
error_a = rai::read (stream_a, signature.bytes);
if (!error_a)
{
error_a = rai::read (stream_a, sequence);
if (!error_a)
{
block = rai::deserialize_block (stream_a);
error_a = block == nullptr;
}
}
}
}
}
rai::vote::vote (bool & error_a, rai::stream & stream_a, rai::block_type type_a)
{
if (!error_a)
{
error_a = rai::read (stream_a, account.bytes);
if (!error_a)
{
error_a = rai::read (stream_a, signature.bytes);
if (!error_a)
{
error_a = rai::read (stream_a, sequence);
if (!error_a)
{
block = rai::deserialize_block (stream_a, type_a);
error_a = block == nullptr;
}
}
}
}
}
rai::vote::vote (rai::account const & account_a, rai::raw_key const & prv_a, uint64_t sequence_a, std::shared_ptr<rai::block> block_a) :
sequence (sequence_a),
block (block_a),
account (account_a),
signature (rai::sign_message (prv_a, account_a, hash ()))
{
}
rai::vote::vote (MDB_val const & value_a)
{
rai::bufferstream stream (reinterpret_cast<uint8_t const *> (value_a.mv_data), value_a.mv_size);
auto error (rai::read (stream, account.bytes));
assert (!error);
error = rai::read (stream, signature.bytes);
assert (!error);
error = rai::read (stream, sequence);
assert (!error);
block = rai::deserialize_block (stream);
assert (block != nullptr);
}
rai::uint256_union rai::vote::hash () const
{
rai::uint256_union result;
blake2b_state hash;
blake2b_init (&hash, sizeof (result.bytes));
blake2b_update (&hash, block->hash ().bytes.data (), sizeof (result.bytes));
union
{
uint64_t qword;
std::array<uint8_t, 8> bytes;
};
qword = sequence;
blake2b_update (&hash, bytes.data (), sizeof (bytes));
blake2b_final (&hash, result.bytes.data (), sizeof (result.bytes));
return result;
}
void rai::vote::serialize (rai::stream & stream_a, rai::block_type)
{
write (stream_a, account);
write (stream_a, signature);
write (stream_a, sequence);
block->serialize (stream_a);
}
void rai::vote::serialize (rai::stream & stream_a)
{
write (stream_a, account);
write (stream_a, signature);
write (stream_a, sequence);
rai::serialize_block (stream_a, *block);
}
rai::genesis::genesis ()
{
boost::property_tree::ptree tree;
std::stringstream istream (rai::genesis_block);
boost::property_tree::read_json (istream, tree);
auto block (rai::deserialize_block_json (tree));
assert (dynamic_cast<rai::open_block *> (block.get ()) != nullptr);
open.reset (static_cast<rai::open_block *> (block.release ()));
}
void rai::genesis::initialize (MDB_txn * transaction_a, rai::block_store & store_a) const
{
auto hash_l (hash ());
assert (store_a.latest_begin (transaction_a) == store_a.latest_end ());
store_a.block_put (transaction_a, hash_l, *open);
store_a.account_put (transaction_a, genesis_account, { hash_l, open->hash (), open->hash (), std::numeric_limits<rai::uint128_t>::max (), rai::seconds_since_epoch (), 1 });
store_a.representation_put (transaction_a, genesis_account, std::numeric_limits<rai::uint128_t>::max ());
store_a.checksum_put (transaction_a, 0, 0, hash_l);
store_a.frontier_put (transaction_a, hash_l, genesis_account);
}
rai::block_hash rai::genesis::hash () const
{
return open->hash ();
}

272
rai/common.hpp Normal file
View file

@ -0,0 +1,272 @@
#pragma once
#include <rai/lib/blocks.hpp>
#include <rai/node/utility.hpp>
#include <boost/property_tree/ptree.hpp>
#include <unordered_map>
#include <blake2/blake2.h>
namespace boost
{
template <>
struct hash<rai::uint256_union>
{
size_t operator() (rai::uint256_union const & value_a) const
{
std::hash<rai::uint256_union> hash;
return hash (value_a);
}
};
}
namespace rai
{
class block_store;
/**
* Determine the balance as of this block
*/
class balance_visitor : public rai::block_visitor
{
public:
balance_visitor (MDB_txn *, 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;
MDB_txn * transaction;
rai::block_store & store;
rai::block_hash current;
rai::uint128_t result;
};
/**
* Determine the amount delta resultant from this block
*/
class amount_visitor : public rai::block_visitor
{
public:
amount_visitor (MDB_txn *, rai::block_store &);
virtual ~amount_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 from_send (rai::block_hash const &);
MDB_txn * transaction;
rai::block_store & store;
rai::uint128_t result;
};
/**
* Determine the representative for this block
*/
class representative_visitor : public rai::block_visitor
{
public:
representative_visitor (MDB_txn * transaction_a, rai::block_store & store_a);
virtual ~representative_visitor () = default;
void compute (rai::block_hash const & hash_a);
void send_block (rai::send_block const & block_a) override;
void receive_block (rai::receive_block const & block_a) override;
void open_block (rai::open_block const & block_a) override;
void change_block (rai::change_block const & block_a) override;
MDB_txn * transaction;
rai::block_store & store;
rai::block_hash current;
rai::block_hash result;
};
/**
* A key pair. The private key is generated from the random pool, or passed in
* as a hex string. The public key is derived using ed25519.
*/
class keypair
{
public:
keypair ();
keypair (std::string const &);
rai::public_key pub;
rai::raw_key prv;
};
std::unique_ptr<rai::block> deserialize_block (MDB_val const &);
/**
* Latest information about an account
*/
class account_info
{
public:
account_info ();
account_info (MDB_val const &);
account_info (rai::account_info const &) = default;
account_info (rai::block_hash const &, rai::block_hash const &, rai::block_hash const &, rai::amount const &, uint64_t, uint64_t);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::account_info const &) const;
bool operator!= (rai::account_info const &) const;
rai::mdb_val val () const;
rai::block_hash head;
rai::block_hash rep_block;
rai::block_hash open_block;
rai::amount balance;
/** Seconds since posix epoch */
uint64_t modified;
uint64_t block_count;
};
/**
* Information on an uncollected send, source account, amount, target account.
*/
class pending_info
{
public:
pending_info ();
pending_info (MDB_val const &);
pending_info (rai::account const &, rai::amount const &);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::pending_info const &) const;
rai::mdb_val val () const;
rai::account source;
rai::amount amount;
};
class pending_key
{
public:
pending_key (rai::account const &, rai::block_hash const &);
pending_key (MDB_val const &);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::pending_key const &) const;
rai::mdb_val val () const;
rai::account account;
rai::block_hash hash;
};
class block_info
{
public:
block_info ();
block_info (MDB_val const &);
block_info (rai::account const &, rai::amount const &);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::block_info const &) const;
rai::mdb_val val () const;
rai::account account;
rai::amount balance;
};
class block_counts
{
public:
block_counts ();
size_t sum ();
size_t send;
size_t receive;
size_t open;
size_t change;
};
class vote
{
public:
vote () = default;
vote (rai::vote const &);
vote (bool &, rai::stream &);
vote (bool &, rai::stream &, rai::block_type);
vote (rai::account const &, rai::raw_key const &, uint64_t, std::shared_ptr<rai::block>);
vote (MDB_val const &);
rai::uint256_union hash () const;
bool operator== (rai::vote const &) const;
bool operator!= (rai::vote const &) const;
void serialize (rai::stream &, rai::block_type);
void serialize (rai::stream &);
std::string to_json () const;
// Vote round sequence number
uint64_t sequence;
std::shared_ptr<rai::block> block;
// Account that's voting
rai::account account;
// Signature of sequence + block hash
rai::signature signature;
};
enum class vote_code
{
invalid, // Vote is not signed correctly
replay, // Vote does not have the highest sequence number, it's a replay
vote // Vote has the highest sequence number
};
class vote_result
{
public:
rai::vote_code code;
std::shared_ptr<rai::vote> vote;
};
enum class process_result
{
progress, // Hasn't been seen before, signed correctly
bad_signature, // Signature was bad, forged or transmission error
old, // Already seen and was valid
negative_spend, // Malicious attempt to spend a negative amount
fork, // Malicious fork based on previous
unreceivable, // Source block doesn't exist or has already been received
gap_previous, // Block marked as previous is unknown
gap_source, // Block marked as source is unknown
not_receive_from_send, // Receive does not have a send source
account_mismatch, // Account number in open block doesn't match send destination
opened_burn_account // The impossible happened, someone found the private key associated with the public key '0'.
};
class process_return
{
public:
rai::process_result code;
rai::account account;
rai::amount amount;
rai::account pending_account;
};
enum class tally_result
{
vote,
changed,
confirm
};
class votes
{
public:
votes (std::shared_ptr<rai::block>);
rai::tally_result vote (std::shared_ptr<rai::vote>);
// Root block of fork
rai::block_hash id;
// All votes received by account
std::unordered_map<rai::account, std::shared_ptr<rai::block>> rep_votes;
};
extern rai::keypair const & zero_key;
extern rai::keypair const & test_genesis_key;
extern rai::account const & rai_test_account;
extern rai::account const & rai_beta_account;
extern rai::account const & rai_live_account;
extern std::string const & rai_test_genesis;
extern std::string const & rai_beta_genesis;
extern std::string const & rai_live_genesis;
extern std::string const & genesis_block;
extern rai::account const & genesis_account;
extern rai::account const & burn_account;
extern rai::uint128_t const & genesis_amount;
// A block hash that compares inequal to any real block hash
extern rai::block_hash const & not_a_block;
// An account number that compares inequal to any real account number
extern rai::block_hash const & not_an_account;
class genesis
{
public:
explicit genesis ();
void initialize (MDB_txn *, rai::block_store &) const;
rai::block_hash hash () const;
std::unique_ptr<rai::open_block> open;
};
}

View file

@ -1,7 +1,7 @@
#include <gtest/gtest.h>
#include <rai/common.hpp>
#include <rai/lib/interface.h>
#include <rai/secure.hpp>
#include <ed25519-donna/ed25519.h>

View file

@ -1,6 +1,6 @@
#include <gtest/gtest.h>
#include <rai/secure.hpp>
#include <rai/blockstore.hpp>
#include <rai/versioning.hpp>
TEST (versioning, account_info_v1)

637
rai/ledger.cpp Normal file
View file

@ -0,0 +1,637 @@
#include <rai/blockstore.hpp>
#include <rai/ledger.hpp>
#include <rai/node/common.hpp>
namespace
{
/**
* Roll back the visited block
*/
class rollback_visitor : public rai::block_visitor
{
public:
rollback_visitor (MDB_txn * transaction_a, rai::ledger & ledger_a) :
transaction (transaction_a),
ledger (ledger_a)
{
}
virtual ~rollback_visitor () = default;
void send_block (rai::send_block const & block_a) override
{
auto hash (block_a.hash ());
rai::pending_info pending;
rai::pending_key key (block_a.hashables.destination, hash);
while (ledger.store.pending_get (transaction, key, pending))
{
ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.destination));
}
rai::account_info info;
auto error (ledger.store.account_get (transaction, pending.source, info));
assert (!error);
ledger.store.pending_del (transaction, key);
ledger.store.representation_add (transaction, ledger.representative (transaction, hash), pending.amount.number ());
ledger.change_latest (transaction, pending.source, block_a.hashables.previous, info.rep_block, ledger.balance (transaction, block_a.hashables.previous), info.block_count - 1);
ledger.store.block_del (transaction, hash);
ledger.store.frontier_del (transaction, hash);
ledger.store.frontier_put (transaction, block_a.hashables.previous, pending.source);
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
if (!(info.block_count % ledger.store.block_info_max))
{
ledger.store.block_info_del (transaction, hash);
}
}
void receive_block (rai::receive_block const & block_a) override
{
auto hash (block_a.hash ());
auto representative (ledger.representative (transaction, block_a.hashables.previous));
auto amount (ledger.amount (transaction, block_a.hashables.source));
auto destination_account (ledger.account (transaction, hash));
rai::account_info info;
auto error (ledger.store.account_get (transaction, destination_account, info));
assert (!error);
ledger.store.representation_add (transaction, ledger.representative (transaction, hash), 0 - amount);
ledger.change_latest (transaction, destination_account, block_a.hashables.previous, representative, ledger.balance (transaction, block_a.hashables.previous), info.block_count - 1);
ledger.store.block_del (transaction, hash);
ledger.store.pending_put (transaction, rai::pending_key (destination_account, block_a.hashables.source), { ledger.account (transaction, block_a.hashables.source), amount });
ledger.store.frontier_del (transaction, hash);
ledger.store.frontier_put (transaction, block_a.hashables.previous, destination_account);
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
if (!(info.block_count % ledger.store.block_info_max))
{
ledger.store.block_info_del (transaction, hash);
}
}
void open_block (rai::open_block const & block_a) override
{
auto hash (block_a.hash ());
auto amount (ledger.amount (transaction, block_a.hashables.source));
auto destination_account (ledger.account (transaction, hash));
ledger.store.representation_add (transaction, ledger.representative (transaction, hash), 0 - amount);
ledger.change_latest (transaction, destination_account, 0, 0, 0, 0);
ledger.store.block_del (transaction, hash);
ledger.store.pending_put (transaction, rai::pending_key (destination_account, block_a.hashables.source), { ledger.account (transaction, block_a.hashables.source), amount });
ledger.store.frontier_del (transaction, hash);
}
void change_block (rai::change_block const & block_a) override
{
auto hash (block_a.hash ());
auto representative (ledger.representative (transaction, block_a.hashables.previous));
auto account (ledger.account (transaction, block_a.hashables.previous));
rai::account_info info;
auto error (ledger.store.account_get (transaction, account, info));
assert (!error);
auto balance (ledger.balance (transaction, block_a.hashables.previous));
ledger.store.representation_add (transaction, representative, balance);
ledger.store.representation_add (transaction, hash, 0 - balance);
ledger.store.block_del (transaction, hash);
ledger.change_latest (transaction, account, block_a.hashables.previous, representative, info.balance, info.block_count - 1);
ledger.store.frontier_del (transaction, hash);
ledger.store.frontier_put (transaction, block_a.hashables.previous, account);
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
if (!(info.block_count % ledger.store.block_info_max))
{
ledger.store.block_info_del (transaction, hash);
}
}
MDB_txn * transaction;
rai::ledger & ledger;
};
class ledger_processor : public rai::block_visitor
{
public:
ledger_processor (rai::ledger &, MDB_txn *);
virtual ~ledger_processor () = default;
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;
rai::ledger & ledger;
MDB_txn * transaction;
rai::process_return result;
};
void ledger_processor::change_block (rai::change_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, hash));
result.code = existing ? rai::process_result::old : rai::process_result::progress; // Have we seen this block before? (Harmless)
if (result.code == rai::process_result::progress)
{
auto previous (ledger.store.block_exists (transaction, block_a.hashables.previous));
result.code = previous ? rai::process_result::progress : rai::process_result::gap_previous; // Have we seen the previous block already? (Harmless)
if (result.code == rai::process_result::progress)
{
auto account (ledger.store.frontier_get (transaction, block_a.hashables.previous));
result.code = account.is_zero () ? rai::process_result::fork : rai::process_result::progress;
if (result.code == rai::process_result::progress)
{
rai::account_info info;
auto latest_error (ledger.store.account_get (transaction, account, info));
assert (!latest_error);
assert (info.head == block_a.hashables.previous);
result.code = validate_message (account, hash, block_a.signature) ? rai::process_result::bad_signature : rai::process_result::progress; // Is this block signed correctly (Malformed)
if (result.code == rai::process_result::progress)
{
ledger.store.block_put (transaction, hash, block_a);
auto balance (ledger.balance (transaction, block_a.hashables.previous));
ledger.store.representation_add (transaction, hash, balance);
ledger.store.representation_add (transaction, info.rep_block, 0 - balance);
ledger.change_latest (transaction, account, hash, hash, info.balance, info.block_count + 1);
ledger.store.frontier_del (transaction, block_a.hashables.previous);
ledger.store.frontier_put (transaction, hash, account);
result.account = account;
result.amount = 0;
}
}
}
}
}
void ledger_processor::send_block (rai::send_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, hash));
result.code = existing ? rai::process_result::old : rai::process_result::progress; // Have we seen this block before? (Harmless)
if (result.code == rai::process_result::progress)
{
auto previous (ledger.store.block_exists (transaction, block_a.hashables.previous));
result.code = previous ? rai::process_result::progress : rai::process_result::gap_previous; // Have we seen the previous block already? (Harmless)
if (result.code == rai::process_result::progress)
{
auto account (ledger.store.frontier_get (transaction, block_a.hashables.previous));
result.code = account.is_zero () ? rai::process_result::fork : rai::process_result::progress;
if (result.code == rai::process_result::progress)
{
result.code = validate_message (account, hash, block_a.signature) ? rai::process_result::bad_signature : rai::process_result::progress; // Is this block signed correctly (Malformed)
if (result.code == rai::process_result::progress)
{
rai::account_info info;
auto latest_error (ledger.store.account_get (transaction, account, info));
assert (!latest_error);
assert (info.head == block_a.hashables.previous);
result.code = info.balance.number () >= block_a.hashables.balance.number () ? rai::process_result::progress : rai::process_result::negative_spend; // Is this trying to spend a negative amount (Malicious)
if (result.code == rai::process_result::progress)
{
auto amount (info.balance.number () - block_a.hashables.balance.number ());
ledger.store.representation_add (transaction, info.rep_block, 0 - amount);
ledger.store.block_put (transaction, hash, block_a);
ledger.change_latest (transaction, account, hash, info.rep_block, block_a.hashables.balance, info.block_count + 1);
ledger.store.pending_put (transaction, rai::pending_key (block_a.hashables.destination, hash), { account, amount });
ledger.store.frontier_del (transaction, block_a.hashables.previous);
ledger.store.frontier_put (transaction, hash, account);
result.account = account;
result.amount = amount;
result.pending_account = block_a.hashables.destination;
}
}
}
}
}
}
void ledger_processor::receive_block (rai::receive_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, hash));
result.code = existing ? rai::process_result::old : rai::process_result::progress; // Have we seen this block already? (Harmless)
if (result.code == rai::process_result::progress)
{
result.code = ledger.store.block_exists (transaction, block_a.hashables.source) ? rai::process_result::progress : rai::process_result::gap_source; // Have we seen the source block already? (Harmless)
if (result.code == rai::process_result::progress)
{
auto account (ledger.store.frontier_get (transaction, block_a.hashables.previous));
result.code = account.is_zero () ? rai::process_result::gap_previous : rai::process_result::progress; //Have we seen the previous block? No entries for account at all (Harmless)
if (result.code == rai::process_result::progress)
{
result.code = rai::validate_message (account, hash, block_a.signature) ? rai::process_result::bad_signature : rai::process_result::progress; // Is the signature valid (Malformed)
if (result.code == rai::process_result::progress)
{
rai::account_info info;
ledger.store.account_get (transaction, account, info);
result.code = info.head == block_a.hashables.previous ? rai::process_result::progress : rai::process_result::gap_previous; // Block doesn't immediately follow latest block (Harmless)
if (result.code == rai::process_result::progress)
{
rai::pending_key key (account, block_a.hashables.source);
rai::pending_info pending;
result.code = ledger.store.pending_get (transaction, key, pending) ? rai::process_result::unreceivable : rai::process_result::progress; // Has this source already been received (Malformed)
if (result.code == rai::process_result::progress)
{
auto new_balance (info.balance.number () + pending.amount.number ());
rai::account_info source_info;
auto error (ledger.store.account_get (transaction, pending.source, source_info));
assert (!error);
ledger.store.pending_del (transaction, key);
ledger.store.block_put (transaction, hash, block_a);
ledger.change_latest (transaction, account, hash, info.rep_block, new_balance, info.block_count + 1);
ledger.store.representation_add (transaction, info.rep_block, pending.amount.number ());
ledger.store.frontier_del (transaction, block_a.hashables.previous);
ledger.store.frontier_put (transaction, hash, account);
result.account = account;
result.amount = pending.amount;
}
}
}
}
else
{
result.code = ledger.store.block_exists (transaction, block_a.hashables.previous) ? rai::process_result::fork : rai::process_result::gap_previous; // If we have the block but it's not the latest we have a signed fork (Malicious)
}
}
}
}
void ledger_processor::open_block (rai::open_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, hash));
result.code = existing ? rai::process_result::old : rai::process_result::progress; // Have we seen this block already? (Harmless)
if (result.code == rai::process_result::progress)
{
auto source_missing (!ledger.store.block_exists (transaction, block_a.hashables.source));
result.code = source_missing ? rai::process_result::gap_source : rai::process_result::progress; // Have we seen the source block? (Harmless)
if (result.code == rai::process_result::progress)
{
result.code = rai::validate_message (block_a.hashables.account, hash, block_a.signature) ? rai::process_result::bad_signature : rai::process_result::progress; // Is the signature valid (Malformed)
if (result.code == rai::process_result::progress)
{
rai::account_info info;
result.code = ledger.store.account_get (transaction, block_a.hashables.account, info) ? rai::process_result::progress : rai::process_result::fork; // Has this account already been opened? (Malicious)
if (result.code == rai::process_result::progress)
{
rai::pending_key key (block_a.hashables.account, block_a.hashables.source);
rai::pending_info pending;
result.code = ledger.store.pending_get (transaction, key, pending) ? rai::process_result::unreceivable : rai::process_result::progress; // Has this source already been received (Malformed)
if (result.code == rai::process_result::progress)
{
result.code = block_a.hashables.account == rai::burn_account ? rai::process_result::opened_burn_account : rai::process_result::progress; // Is it burning 0 account? (Malicious)
if (result.code == rai::process_result::progress)
{
rai::account_info source_info;
auto error (ledger.store.account_get (transaction, pending.source, source_info));
assert (!error);
ledger.store.pending_del (transaction, key);
ledger.store.block_put (transaction, hash, block_a);
ledger.change_latest (transaction, block_a.hashables.account, hash, hash, pending.amount.number (), info.block_count + 1);
ledger.store.representation_add (transaction, hash, pending.amount.number ());
ledger.store.frontier_put (transaction, hash, block_a.hashables.account);
result.account = block_a.hashables.account;
result.amount = pending.amount;
}
}
}
}
}
}
}
ledger_processor::ledger_processor (rai::ledger & ledger_a, MDB_txn * transaction_a) :
ledger (ledger_a),
transaction (transaction_a)
{
}
} // namespace
size_t rai::shared_ptr_block_hash::operator() (std::shared_ptr<rai::block> const & block_a) const
{
auto hash (block_a->hash ());
auto result (static_cast<size_t> (hash.qwords[0]));
return result;
}
bool rai::shared_ptr_block_hash::operator() (std::shared_ptr<rai::block> const & lhs, std::shared_ptr<rai::block> const & rhs) const
{
return *lhs == *rhs;
}
rai::ledger::ledger (rai::block_store & store_a, rai::uint128_t const & inactive_supply_a) :
store (store_a),
inactive_supply (inactive_supply_a),
check_bootstrap_weights (true)
{
}
// Sum the weights for each vote and return the winning block with its vote tally
std::pair<rai::uint128_t, std::shared_ptr<rai::block>> rai::ledger::winner (MDB_txn * transaction_a, rai::votes const & votes_a)
{
auto tally_l (tally (transaction_a, votes_a));
auto existing (tally_l.begin ());
return std::make_pair (existing->first, existing->second);
}
std::map<rai::uint128_t, std::shared_ptr<rai::block>, std::greater<rai::uint128_t>> rai::ledger::tally (MDB_txn * transaction_a, rai::votes const & votes_a)
{
std::unordered_map<std::shared_ptr<block>, rai::uint128_t, rai::shared_ptr_block_hash, rai::shared_ptr_block_hash> totals;
// Construct a map of blocks -> vote total.
for (auto & i : votes_a.rep_votes)
{
auto existing (totals.find (i.second));
if (existing == totals.end ())
{
totals.insert (std::make_pair (i.second, 0));
existing = totals.find (i.second);
assert (existing != totals.end ());
}
auto weight_l (weight (transaction_a, i.first));
existing->second += weight_l;
}
// Construction a map of vote total -> block in decreasing order.
std::map<rai::uint128_t, std::shared_ptr<rai::block>, std::greater<rai::uint128_t>> result;
for (auto & i : totals)
{
result[i.second] = i.first;
}
return result;
}
// Balance for account containing hash
rai::uint128_t rai::ledger::balance (MDB_txn * transaction_a, rai::block_hash const & hash_a)
{
balance_visitor visitor (transaction_a, store);
visitor.compute (hash_a);
return visitor.result;
}
// Balance for an account by account number
rai::uint128_t rai::ledger::account_balance (MDB_txn * transaction_a, rai::account const & account_a)
{
rai::uint128_t result (0);
rai::account_info info;
auto none (store.account_get (transaction_a, account_a, info));
if (!none)
{
result = info.balance.number ();
}
return result;
}
rai::uint128_t rai::ledger::account_pending (MDB_txn * transaction_a, rai::account const & account_a)
{
rai::uint128_t result (0);
rai::account end (account_a.number () + 1);
for (auto i (store.pending_begin (transaction_a, rai::pending_key (account_a, 0))), n (store.pending_begin (transaction_a, rai::pending_key (end, 0))); i != n; ++i)
{
rai::pending_info info (i->second);
result += info.amount.number ();
}
return result;
}
rai::process_return rai::ledger::process (MDB_txn * transaction_a, rai::block const & block_a)
{
ledger_processor processor (*this, transaction_a);
block_a.visit (processor);
return processor.result;
}
// Money supply for heuristically calculating vote percentages
rai::uint128_t rai::ledger::supply (MDB_txn * transaction_a)
{
auto unallocated (account_balance (transaction_a, rai::genesis_account));
auto burned (account_pending (transaction_a, 0));
auto absolute_supply (rai::genesis_amount - unallocated - burned);
auto adjusted_supply (absolute_supply - inactive_supply);
return adjusted_supply <= absolute_supply ? adjusted_supply : 0;
}
rai::block_hash rai::ledger::representative (MDB_txn * transaction_a, rai::block_hash const & hash_a)
{
auto result (representative_calculated (transaction_a, hash_a));
assert (result.is_zero () || store.block_exists (transaction_a, result));
return result;
}
rai::block_hash rai::ledger::representative_calculated (MDB_txn * transaction_a, rai::block_hash const & hash_a)
{
representative_visitor visitor (transaction_a, store);
visitor.compute (hash_a);
return visitor.result;
}
bool rai::ledger::block_exists (rai::block_hash const & hash_a)
{
rai::transaction transaction (store.environment, nullptr, false);
auto result (store.block_exists (transaction, hash_a));
return result;
}
std::string rai::ledger::block_text (char const * hash_a)
{
return block_text (rai::block_hash (hash_a));
}
std::string rai::ledger::block_text (rai::block_hash const & hash_a)
{
std::string result;
rai::transaction transaction (store.environment, nullptr, false);
auto block (store.block_get (transaction, hash_a));
if (block != nullptr)
{
block->serialize_json (result);
}
return result;
}
// Vote weight of an account
rai::uint128_t rai::ledger::weight (MDB_txn * transaction_a, rai::account const & account_a)
{
if (check_bootstrap_weights.load ())
{
auto blocks = store.block_count (transaction_a);
if (blocks.sum () < bootstrap_weight_max_blocks)
{
auto weight = bootstrap_weights.find (account_a);
if (weight != bootstrap_weights.end ())
{
return weight->second;
}
}
else
{
check_bootstrap_weights = false;
}
}
return store.representation_get (transaction_a, account_a);
}
// Rollback blocks until `block_a' doesn't exist
void rai::ledger::rollback (MDB_txn * transaction_a, rai::block_hash const & block_a)
{
assert (store.block_exists (transaction_a, block_a));
auto account_l (account (transaction_a, block_a));
rollback_visitor rollback (transaction_a, *this);
rai::account_info info;
while (store.block_exists (transaction_a, block_a))
{
auto latest_error (store.account_get (transaction_a, account_l, info));
assert (!latest_error);
auto block (store.block_get (transaction_a, info.head));
block->visit (rollback);
}
}
// Return account containing hash
rai::account rai::ledger::account (MDB_txn * transaction_a, rai::block_hash const & hash_a)
{
assert (store.block_exists (transaction_a, hash_a));
auto hash (hash_a);
rai::block_hash successor (1);
rai::block_info block_info;
while (!successor.is_zero () && store.block_info_get (transaction_a, successor, block_info))
{
successor = store.block_successor (transaction_a, hash);
if (!successor.is_zero ())
{
hash = successor;
}
}
rai::account result;
if (successor.is_zero ())
{
result = store.frontier_get (transaction_a, hash);
}
else
{
result = block_info.account;
}
assert (!result.is_zero ());
return result;
}
// Return amount decrease or increase for block
rai::uint128_t rai::ledger::amount (MDB_txn * transaction_a, rai::block_hash const & hash_a)
{
amount_visitor amount (transaction_a, store);
amount.compute (hash_a);
return amount.result;
}
// Return latest block for account
rai::block_hash rai::ledger::latest (MDB_txn * transaction_a, rai::account const & account_a)
{
rai::account_info info;
auto latest_error (store.account_get (transaction_a, account_a, info));
return latest_error ? 0 : info.head;
}
// Return latest root for account, account number of there are no blocks for this account.
rai::block_hash rai::ledger::latest_root (MDB_txn * transaction_a, rai::account const & account_a)
{
rai::account_info info;
auto latest_error (store.account_get (transaction_a, account_a, info));
rai::block_hash result;
if (latest_error)
{
result = account_a;
}
else
{
result = info.head;
}
return result;
}
rai::checksum rai::ledger::checksum (MDB_txn * transaction_a, rai::account const & begin_a, rai::account const & end_a)
{
rai::checksum result;
auto error (store.checksum_get (transaction_a, 0, 0, result));
assert (!error);
return result;
}
void rai::ledger::dump_account_chain (rai::account const & account_a)
{
rai::transaction transaction (store.environment, nullptr, false);
auto hash (latest (transaction, account_a));
while (!hash.is_zero ())
{
auto block (store.block_get (transaction, hash));
assert (block != nullptr);
std::cerr << hash.to_string () << std::endl;
hash = block->previous ();
}
}
void rai::ledger::checksum_update (MDB_txn * transaction_a, rai::block_hash const & hash_a)
{
rai::checksum value;
auto error (store.checksum_get (transaction_a, 0, 0, value));
assert (!error);
value ^= hash_a;
store.checksum_put (transaction_a, 0, 0, value);
}
void rai::ledger::change_latest (MDB_txn * transaction_a, rai::account const & account_a, rai::block_hash const & hash_a, rai::block_hash const & rep_block_a, rai::amount const & balance_a, uint64_t block_count_a)
{
rai::account_info info;
auto exists (!store.account_get (transaction_a, account_a, info));
if (exists)
{
checksum_update (transaction_a, info.head);
}
else
{
assert (dynamic_cast<rai::open_block *> (store.block_get (transaction_a, hash_a).get ()) != nullptr);
info.open_block = hash_a;
}
if (!hash_a.is_zero ())
{
info.head = hash_a;
info.rep_block = rep_block_a;
info.balance = balance_a;
info.modified = rai::seconds_since_epoch ();
info.block_count = block_count_a;
store.account_put (transaction_a, account_a, info);
if (!(block_count_a % store.block_info_max))
{
rai::block_info block_info;
block_info.account = account_a;
block_info.balance = balance_a;
store.block_info_put (transaction_a, hash_a, block_info);
}
checksum_update (transaction_a, hash_a);
}
else
{
store.account_del (transaction_a, account_a);
}
}
std::unique_ptr<rai::block> rai::ledger::successor (MDB_txn * transaction_a, rai::block_hash const & block_a)
{
assert (store.account_exists (transaction_a, block_a) || store.block_exists (transaction_a, block_a));
assert (store.account_exists (transaction_a, block_a) || latest (transaction_a, account (transaction_a, block_a)) != block_a);
rai::block_hash successor;
if (store.account_exists (transaction_a, block_a))
{
rai::account_info info;
auto error (store.account_get (transaction_a, block_a, info));
assert (!error);
successor = info.open_block;
}
else
{
successor = store.block_successor (transaction_a, block_a);
}
assert (!successor.is_zero ());
auto result (store.block_get (transaction_a, successor));
assert (result != nullptr);
return result;
}
std::unique_ptr<rai::block> rai::ledger::forked_block (MDB_txn * transaction_a, rai::block const & block_a)
{
assert (!store.block_exists (transaction_a, block_a.hash ()));
auto root (block_a.root ());
assert (store.block_exists (transaction_a, root) || store.account_exists (transaction_a, root));
std::unique_ptr<rai::block> result (store.block_get (transaction_a, store.block_successor (transaction_a, root)));
if (result == nullptr)
{
rai::account_info info;
auto error (store.account_get (transaction_a, root, info));
assert (!error);
result = store.block_get (transaction_a, info.open_block);
assert (result != nullptr);
}
return result;
}

52
rai/ledger.hpp Normal file
View file

@ -0,0 +1,52 @@
#pragma once
#include <rai/common.hpp>
namespace rai
{
class block_store;
class shared_ptr_block_hash
{
public:
size_t operator() (std::shared_ptr<rai::block> const &) const;
bool operator() (std::shared_ptr<rai::block> const &, std::shared_ptr<rai::block> const &) const;
};
class ledger
{
public:
ledger (rai::block_store &, rai::uint128_t const & = 0);
std::pair<rai::uint128_t, std::shared_ptr<rai::block>> winner (MDB_txn *, rai::votes const & votes_a);
// Map of weight -> associated block, ordered greatest to least
std::map<rai::uint128_t, std::shared_ptr<rai::block>, std::greater<rai::uint128_t>> tally (MDB_txn *, rai::votes const &);
rai::account account (MDB_txn *, rai::block_hash const &);
rai::uint128_t amount (MDB_txn *, rai::block_hash const &);
rai::uint128_t balance (MDB_txn *, rai::block_hash const &);
rai::uint128_t account_balance (MDB_txn *, rai::account const &);
rai::uint128_t account_pending (MDB_txn *, rai::account const &);
rai::uint128_t weight (MDB_txn *, rai::account const &);
std::unique_ptr<rai::block> successor (MDB_txn *, rai::block_hash const &);
std::unique_ptr<rai::block> forked_block (MDB_txn *, rai::block const &);
rai::block_hash latest (MDB_txn *, rai::account const &);
rai::block_hash latest_root (MDB_txn *, rai::account const &);
rai::block_hash representative (MDB_txn *, rai::block_hash const &);
rai::block_hash representative_calculated (MDB_txn *, rai::block_hash const &);
bool block_exists (rai::block_hash const &);
std::string block_text (char const *);
std::string block_text (rai::block_hash const &);
rai::uint128_t supply (MDB_txn *);
rai::process_return process (MDB_txn *, rai::block const &);
void rollback (MDB_txn *, rai::block_hash const &);
void change_latest (MDB_txn *, rai::account const &, rai::block_hash const &, rai::account const &, rai::uint128_union const &, uint64_t);
void checksum_update (MDB_txn *, rai::block_hash const &);
rai::checksum checksum (MDB_txn *, rai::account const &, rai::account const &);
void dump_account_chain (rai::account const &);
static rai::uint128_t const unit;
rai::block_store & store;
rai::uint128_t inactive_supply;
std::unordered_map<rai::account, rai::uint128_t> bootstrap_weights;
uint64_t bootstrap_weight_max_blocks;
std::atomic<bool> check_bootstrap_weights;
};
};

View file

@ -1,7 +1,8 @@
#pragma once
#include <rai/blockstore.hpp>
#include <rai/ledger.hpp>
#include <rai/node/common.hpp>
#include <rai/secure.hpp>
#include <atomic>
#include <future>

View file

@ -1,7 +1,7 @@
#pragma once
#include <rai/common.hpp>
#include <rai/lib/interface.h>
#include <rai/secure.hpp>
#include <boost/asio.hpp>

View file

@ -1,5 +1,6 @@
#pragma once
#include <rai/ledger.hpp>
#include <rai/lib/work.hpp>
#include <rai/node/bootstrap.hpp>
#include <rai/node/wallet.hpp>

View file

@ -1,11 +1,29 @@
#include <rai/node/utility.hpp>
#include <rai/lib/interface.h>
#include <rai/node/utility.hpp>
#include <rai/node/working.hpp>
#include <lmdb/libraries/liblmdb/lmdb.h>
#include <ed25519-donna/ed25519.h>
boost::filesystem::path rai::working_path ()
{
auto result (rai::app_path ());
switch (rai::rai_network)
{
case rai::rai_networks::rai_test_network:
result /= "RaiBlocksTest";
break;
case rai::rai_networks::rai_beta_network:
result /= "RaiBlocksBeta";
break;
case rai::rai_networks::rai_live_network:
result /= "RaiBlocks";
break;
}
return result;
}
boost::filesystem::path rai::unique_path ()
{
auto result (working_path () / boost::filesystem::unique_path ());

View file

@ -98,6 +98,9 @@ bool fetch_object (T & object, boost::filesystem::path const & path_a, std::fstr
return error;
}
/**
* RAII wrapper for MDB_env
*/
class mdb_env
{
public:
@ -106,6 +109,10 @@ public:
operator MDB_env * () const;
MDB_env * environment;
};
/**
* Encapsulates MDB_val and provides uint256_union conversion of the data.
*/
class mdb_val
{
public:
@ -121,6 +128,11 @@ public:
operator MDB_val const & () const;
MDB_val value;
};
/**
* RAII wrapper of MDB_txn where the constructor starts the transaction
* and the destructor commits it.
*/
class transaction
{
public:

View file

@ -1,8 +1,9 @@
#pragma once
#include <rai/blockstore.hpp>
#include <rai/common.hpp>
#include <rai/node/common.hpp>
#include <rai/node/openclwork.hpp>
#include <rai/secure.hpp>
#include <mutex>
#include <queue>

View file

@ -1,8 +1,8 @@
#pragma once
#include <rai/secure.hpp>
#include <rai/common.hpp>
namespace rai
{
boost::filesystem::path app_path ();
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,410 +0,0 @@
#pragma once
#include <rai/lib/blocks.hpp>
#include <rai/node/utility.hpp>
#include <boost/property_tree/ptree.hpp>
#include <unordered_map>
#include <unordered_set>
#include <blake2/blake2.h>
namespace boost
{
template <>
struct hash<rai::uint256_union>
{
size_t operator() (rai::uint256_union const & value_a) const
{
std::hash<rai::uint256_union> hash;
return hash (value_a);
}
};
}
namespace rai
{
class keypair
{
public:
keypair ();
keypair (std::string const &);
rai::public_key pub;
rai::raw_key prv;
};
class shared_ptr_block_hash
{
public:
size_t operator() (std::shared_ptr<rai::block> const &) const;
bool operator() (std::shared_ptr<rai::block> const &, std::shared_ptr<rai::block> const &) const;
};
std::unique_ptr<rai::block> deserialize_block (MDB_val const &);
// Latest information about an account
class account_info
{
public:
account_info ();
account_info (MDB_val const &);
account_info (rai::account_info const &) = default;
account_info (rai::block_hash const &, rai::block_hash const &, rai::block_hash const &, rai::amount const &, uint64_t, uint64_t);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::account_info const &) const;
bool operator!= (rai::account_info const &) const;
rai::mdb_val val () const;
rai::block_hash head;
rai::block_hash rep_block;
rai::block_hash open_block;
rai::amount balance;
/** Seconds since posix epoch */
uint64_t modified;
uint64_t block_count;
};
class store_entry
{
public:
store_entry ();
void clear ();
store_entry * operator-> ();
rai::mdb_val first;
rai::mdb_val second;
};
class store_iterator
{
public:
store_iterator (MDB_txn *, MDB_dbi);
store_iterator (std::nullptr_t);
store_iterator (MDB_txn *, MDB_dbi, MDB_val const &);
store_iterator (rai::store_iterator &&);
store_iterator (rai::store_iterator const &) = delete;
~store_iterator ();
rai::store_iterator & operator++ ();
void next_dup ();
rai::store_iterator & operator= (rai::store_iterator &&);
rai::store_iterator & operator= (rai::store_iterator const &) = delete;
rai::store_entry & operator-> ();
bool operator== (rai::store_iterator const &) const;
bool operator!= (rai::store_iterator const &) const;
MDB_cursor * cursor;
rai::store_entry current;
};
// Information on an uncollected send, source account, amount, target account.
class pending_info
{
public:
pending_info ();
pending_info (MDB_val const &);
pending_info (rai::account const &, rai::amount const &);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::pending_info const &) const;
rai::mdb_val val () const;
rai::account source;
rai::amount amount;
};
class pending_key
{
public:
pending_key (rai::account const &, rai::block_hash const &);
pending_key (MDB_val const &);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::pending_key const &) const;
rai::mdb_val val () const;
rai::account account;
rai::block_hash hash;
};
class block_info
{
public:
block_info ();
block_info (MDB_val const &);
block_info (rai::account const &, rai::amount const &);
void serialize (rai::stream &) const;
bool deserialize (rai::stream &);
bool operator== (rai::block_info const &) const;
rai::mdb_val val () const;
rai::account account;
rai::amount balance;
};
class block_counts
{
public:
block_counts ();
size_t sum ();
size_t send;
size_t receive;
size_t open;
size_t change;
};
class vote
{
public:
vote () = default;
vote (rai::vote const &);
vote (bool &, rai::stream &);
vote (bool &, rai::stream &, rai::block_type);
vote (rai::account const &, rai::raw_key const &, uint64_t, std::shared_ptr<rai::block>);
vote (MDB_val const &);
rai::uint256_union hash () const;
bool operator== (rai::vote const &) const;
bool operator!= (rai::vote const &) const;
void serialize (rai::stream &, rai::block_type);
void serialize (rai::stream &);
std::string to_json () const;
// Vote round sequence number
uint64_t sequence;
std::shared_ptr<rai::block> block;
// Account that's voting
rai::account account;
// Signature of sequence + block hash
rai::signature signature;
};
enum class vote_code
{
invalid, // Vote is not signed correctly
replay, // Vote does not have the highest sequence number, it's a replay
vote // Vote has the highest sequence number
};
class vote_result
{
public:
rai::vote_code code;
std::shared_ptr<rai::vote> vote;
};
class block_store
{
public:
block_store (bool &, boost::filesystem::path const &, int lmdb_max_dbs = 128);
MDB_dbi block_database (rai::block_type);
void block_put_raw (MDB_txn *, MDB_dbi, rai::block_hash const &, MDB_val);
void block_put (MDB_txn *, rai::block_hash const &, rai::block const &, rai::block_hash const & = rai::block_hash (0));
MDB_val block_get_raw (MDB_txn *, rai::block_hash const &, rai::block_type &);
rai::block_hash block_successor (MDB_txn *, rai::block_hash const &);
void block_successor_clear (MDB_txn *, rai::block_hash const &);
std::unique_ptr<rai::block> block_get (MDB_txn *, rai::block_hash const &);
std::unique_ptr<rai::block> block_random (MDB_txn *);
std::unique_ptr<rai::block> block_random (MDB_txn *, MDB_dbi);
void block_del (MDB_txn *, rai::block_hash const &);
bool block_exists (MDB_txn *, rai::block_hash const &);
rai::block_counts block_count (MDB_txn *);
std::unordered_multimap<rai::block_hash, rai::block_hash> block_dependencies (MDB_txn *);
void frontier_put (MDB_txn *, rai::block_hash const &, rai::account const &);
rai::account frontier_get (MDB_txn *, rai::block_hash const &);
void frontier_del (MDB_txn *, rai::block_hash const &);
size_t frontier_count (MDB_txn *);
void account_put (MDB_txn *, rai::account const &, rai::account_info const &);
bool account_get (MDB_txn *, rai::account const &, rai::account_info &);
void account_del (MDB_txn *, rai::account const &);
bool account_exists (MDB_txn *, rai::account const &);
rai::store_iterator latest_begin (MDB_txn *, rai::account const &);
rai::store_iterator latest_begin (MDB_txn *);
rai::store_iterator latest_end ();
void pending_put (MDB_txn *, rai::pending_key const &, rai::pending_info const &);
void pending_del (MDB_txn *, rai::pending_key const &);
bool pending_get (MDB_txn *, rai::pending_key const &, rai::pending_info &);
bool pending_exists (MDB_txn *, rai::pending_key const &);
rai::store_iterator pending_begin (MDB_txn *, rai::pending_key const &);
rai::store_iterator pending_begin (MDB_txn *);
rai::store_iterator pending_end ();
void block_info_put (MDB_txn *, rai::block_hash const &, rai::block_info const &);
void block_info_del (MDB_txn *, rai::block_hash const &);
bool block_info_get (MDB_txn *, rai::block_hash const &, rai::block_info &);
bool block_info_exists (MDB_txn *, rai::block_hash const &);
rai::store_iterator block_info_begin (MDB_txn *, rai::block_hash const &);
rai::store_iterator block_info_begin (MDB_txn *);
rai::store_iterator block_info_end ();
rai::uint128_t block_balance (MDB_txn *, rai::block_hash const &);
static size_t const block_info_max = 32;
rai::uint128_t representation_get (MDB_txn *, rai::account const &);
void representation_put (MDB_txn *, rai::account const &, rai::uint128_t const &);
void representation_add (MDB_txn *, rai::account const &, rai::uint128_t const &);
rai::store_iterator representation_begin (MDB_txn *);
rai::store_iterator representation_end ();
void unchecked_clear (MDB_txn *);
void unchecked_put (MDB_txn *, rai::block_hash const &, std::shared_ptr<rai::block> const &);
std::vector<std::shared_ptr<rai::block>> unchecked_get (MDB_txn *, rai::block_hash const &);
void unchecked_del (MDB_txn *, rai::block_hash const &, rai::block const &);
rai::store_iterator unchecked_begin (MDB_txn *);
rai::store_iterator unchecked_begin (MDB_txn *, rai::block_hash const &);
rai::store_iterator unchecked_end ();
size_t unchecked_count (MDB_txn *);
std::unordered_multimap<rai::block_hash, std::shared_ptr<rai::block>> unchecked_cache;
void unsynced_put (MDB_txn *, rai::block_hash const &);
void unsynced_del (MDB_txn *, rai::block_hash const &);
bool unsynced_exists (MDB_txn *, rai::block_hash const &);
rai::store_iterator unsynced_begin (MDB_txn *, rai::block_hash const &);
rai::store_iterator unsynced_begin (MDB_txn *);
rai::store_iterator unsynced_end ();
void checksum_put (MDB_txn *, uint64_t, uint8_t, rai::checksum const &);
bool checksum_get (MDB_txn *, uint64_t, uint8_t, rai::checksum &);
void checksum_del (MDB_txn *, uint64_t, uint8_t);
rai::vote_result vote_validate (MDB_txn *, std::shared_ptr<rai::vote>);
// Return latest vote for an account from store
std::shared_ptr<rai::vote> vote_get (MDB_txn *, rai::account const &);
// Populate vote with the next sequence number
std::shared_ptr<rai::vote> vote_generate (MDB_txn *, rai::account const &, rai::raw_key const &, std::shared_ptr<rai::block>);
// Return either vote or the stored vote with a higher sequence number
std::shared_ptr<rai::vote> vote_max (MDB_txn *, std::shared_ptr<rai::vote>);
// Return latest vote for an account considering the vote cache
std::shared_ptr<rai::vote> vote_current (MDB_txn *, rai::account const &);
void flush (MDB_txn *);
rai::store_iterator vote_begin (MDB_txn *);
rai::store_iterator vote_end ();
std::mutex cache_mutex;
std::unordered_map<rai::account, std::shared_ptr<rai::vote>> vote_cache;
void version_put (MDB_txn *, int);
int version_get (MDB_txn *);
void do_upgrades (MDB_txn *);
void upgrade_v1_to_v2 (MDB_txn *);
void upgrade_v2_to_v3 (MDB_txn *);
void upgrade_v3_to_v4 (MDB_txn *);
void upgrade_v4_to_v5 (MDB_txn *);
void upgrade_v5_to_v6 (MDB_txn *);
void upgrade_v6_to_v7 (MDB_txn *);
void upgrade_v7_to_v8 (MDB_txn *);
void upgrade_v8_to_v9 (MDB_txn *);
void upgrade_v9_to_v10 (MDB_txn *);
void clear (MDB_dbi);
rai::mdb_env environment;
// block_hash -> account // Maps head blocks to owning account
MDB_dbi frontiers;
// account -> block_hash, representative, balance, timestamp // Account to head block, representative, balance, last_change
MDB_dbi accounts;
// block_hash -> send_block
MDB_dbi send_blocks;
// block_hash -> receive_block
MDB_dbi receive_blocks;
// block_hash -> open_block
MDB_dbi open_blocks;
// block_hash -> change_block
MDB_dbi change_blocks;
// block_hash -> sender, amount, destination // Pending blocks to sender account, amount, destination account
MDB_dbi pending;
// block_hash -> account, balance // Blocks info
MDB_dbi blocks_info;
// account -> weight // Representation
MDB_dbi representation;
// block_hash -> block // Unchecked bootstrap blocks
MDB_dbi unchecked;
// block_hash -> // Blocks that haven't been broadcast
MDB_dbi unsynced;
// (uint56_t, uint8_t) -> block_hash // Mapping of region to checksum
MDB_dbi checksum;
// account -> uint64_t // Highest vote observed for account
MDB_dbi vote;
// uint256_union -> ? // Meta information about block store
MDB_dbi meta;
};
enum class process_result
{
progress, // Hasn't been seen before, signed correctly
bad_signature, // Signature was bad, forged or transmission error
old, // Already seen and was valid
negative_spend, // Malicious attempt to spend a negative amount
fork, // Malicious fork based on previous
unreceivable, // Source block doesn't exist or has already been received
gap_previous, // Block marked as previous is unknown
gap_source, // Block marked as source is unknown
not_receive_from_send, // Receive does not have a send source
account_mismatch, // Account number in open block doesn't match send destination
opened_burn_account // The impossible happened, someone found the private key associated with the public key '0'.
};
class process_return
{
public:
rai::process_result code;
rai::account account;
rai::amount amount;
rai::account pending_account;
};
enum class tally_result
{
vote,
changed,
confirm
};
class votes
{
public:
votes (std::shared_ptr<rai::block>);
rai::tally_result vote (std::shared_ptr<rai::vote>);
// Root block of fork
rai::block_hash id;
// All votes received by account
std::unordered_map<rai::account, std::shared_ptr<rai::block>> rep_votes;
};
class ledger
{
public:
ledger (rai::block_store &, rai::uint128_t const & = 0);
std::pair<rai::uint128_t, std::shared_ptr<rai::block>> winner (MDB_txn *, rai::votes const & votes_a);
// Map of weight -> associated block, ordered greatest to least
std::map<rai::uint128_t, std::shared_ptr<rai::block>, std::greater<rai::uint128_t>> tally (MDB_txn *, rai::votes const &);
rai::account account (MDB_txn *, rai::block_hash const &);
rai::uint128_t amount (MDB_txn *, rai::block_hash const &);
rai::uint128_t balance (MDB_txn *, rai::block_hash const &);
rai::uint128_t account_balance (MDB_txn *, rai::account const &);
rai::uint128_t account_pending (MDB_txn *, rai::account const &);
rai::uint128_t weight (MDB_txn *, rai::account const &);
std::unique_ptr<rai::block> successor (MDB_txn *, rai::block_hash const &);
std::unique_ptr<rai::block> forked_block (MDB_txn *, rai::block const &);
rai::block_hash latest (MDB_txn *, rai::account const &);
rai::block_hash latest_root (MDB_txn *, rai::account const &);
rai::block_hash representative (MDB_txn *, rai::block_hash const &);
rai::block_hash representative_calculated (MDB_txn *, rai::block_hash const &);
bool block_exists (rai::block_hash const &);
std::string block_text (char const *);
std::string block_text (rai::block_hash const &);
rai::uint128_t supply (MDB_txn *);
rai::process_return process (MDB_txn *, rai::block const &);
void rollback (MDB_txn *, rai::block_hash const &);
void change_latest (MDB_txn *, rai::account const &, rai::block_hash const &, rai::account const &, rai::uint128_union const &, uint64_t);
void checksum_update (MDB_txn *, rai::block_hash const &);
rai::checksum checksum (MDB_txn *, rai::account const &, rai::account const &);
void dump_account_chain (rai::account const &);
static rai::uint128_t const unit;
rai::block_store & store;
rai::uint128_t inactive_supply;
std::unordered_map<rai::account, rai::uint128_t> bootstrap_weights;
uint64_t bootstrap_weight_max_blocks;
std::atomic<bool> check_bootstrap_weights;
};
extern rai::keypair const & zero_key;
extern rai::keypair const & test_genesis_key;
extern rai::account const & rai_test_account;
extern rai::account const & rai_beta_account;
extern rai::account const & rai_live_account;
extern std::string const & rai_test_genesis;
extern std::string const & rai_beta_genesis;
extern std::string const & rai_live_genesis;
extern std::string const & genesis_block;
extern rai::account const & genesis_account;
extern rai::account const & burn_account;
extern rai::uint128_t const & genesis_amount;
// A block hash that compares inequal to any real block hash
extern rai::block_hash const & not_a_block;
// An account number that compares inequal to any real account number
extern rai::block_hash const & not_an_account;
class genesis
{
public:
explicit genesis ();
void initialize (MDB_txn *, rai::block_store &) const;
rai::block_hash hash () const;
std::unique_ptr<rai::open_block> open;
};
}