From 2608433c7bc00621f6cf4bec88d4020f8303416e Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 13 Jul 2018 16:51:49 -0600 Subject: [PATCH] Add epoch blocks to disable old type blocks (#955) * Add version field to account_info and min_version to pending_info * Add epoch block support to ledger * Add epoch block settings to config, and remove state canaries * Add test for epoch blocks * Add receive upgrade test for epoch blocks * Ignore old nodes on the beta network * Add min_version to RPC pending and wallet_pending * Add account_version field to account_info RPC * Fix formatting * Remove epoch block config (de)serializing * Mark epoch blocks in history (#968) * Mark epoch blocks in history * Extra info for raw * Remove epoch block from non-raw RPC history * Move raw check --- rai/blockstore.cpp | 64 ++++++++++++- rai/blockstore.hpp | 4 +- rai/common.cpp | 36 ++++--- rai/common.hpp | 13 ++- rai/core_test/block_store.cpp | 15 +-- rai/core_test/ledger.cpp | 86 +++++++++++++++++ rai/core_test/node.cpp | 13 +-- rai/core_test/rpc.cpp | 15 ++- rai/ledger.cpp | 171 ++++++++++++++++++++++++++-------- rai/ledger.hpp | 6 +- rai/node/bootstrap.cpp | 2 +- rai/node/common.cpp | 65 +++++++------ rai/node/common.hpp | 3 +- rai/node/node.cpp | 35 ++++--- rai/node/node.hpp | 4 +- rai/node/rpc.cpp | 35 ++++++- rai/node/stats.cpp | 3 + rai/node/stats.hpp | 1 + rai/qt/qt.cpp | 5 + 19 files changed, 444 insertions(+), 132 deletions(-) diff --git a/rai/blockstore.cpp b/rai/blockstore.cpp index 706aab83..d6572100 100644 --- a/rai/blockstore.cpp +++ b/rai/blockstore.cpp @@ -347,6 +347,8 @@ void rai::block_store::do_upgrades (MDB_txn * transaction_a) case 10: upgrade_v10_to_v11 (transaction_a); case 11: + upgrade_v11_to_v12 (transaction_a); + case 12: break; default: assert (false); @@ -412,7 +414,7 @@ void rai::block_store::upgrade_v3_to_v4 (MDB_txn * transaction_a) { rai::block_hash hash (i->first.uint256 ()); rai::pending_info_v3 info (i->second); - items.push (std::make_pair (rai::pending_key (info.destination, hash), rai::pending_info (info.source, info.amount))); + items.push (std::make_pair (rai::pending_key (info.destination, hash), rai::pending_info (info.source, info.amount, 0))); } mdb_drop (transaction_a, pending, 0); while (!items.empty ()) @@ -461,7 +463,7 @@ void rai::block_store::upgrade_v5_to_v6 (MDB_txn * transaction_a) assert (block != nullptr); hash = block->previous (); } - rai::account_info info (info_old.head, info_old.rep_block, info_old.open_block, info_old.balance, info_old.modified, block_count); + rai::account_info info (info_old.head, info_old.rep_block, info_old.open_block, info_old.balance, info_old.modified, block_count, 0); headers.push_back (std::make_pair (account, info)); } for (auto i (headers.begin ()), n (headers.end ()); i != n; ++i) @@ -549,6 +551,32 @@ void rai::block_store::upgrade_v10_to_v11 (MDB_txn * transaction_a) mdb_drop (transaction_a, unsynced, 1); } +void rai::block_store::upgrade_v11_to_v12 (MDB_txn * transaction_a) +{ + version_put (transaction_a, 12); + for (rai::store_iterator i (transaction_a, accounts), n (nullptr); i != n; ++i) + { + assert (i->second.size () + 1 == sizeof (account_info)); + std::vector bytes ((uint8_t *)i->second.data (), (uint8_t *)i->second.data () + i->second.size ()); + bytes.push_back (0); // version field + mdb_cursor_put (i.cursor, i->first, rai::mdb_val (bytes.size (), bytes.data ()), MDB_CURRENT); + } + for (rai::store_iterator i (transaction_a, pending), n (nullptr); i != n; ++i) + { + assert (i->second.size () + 1 == sizeof (pending_info)); + std::vector bytes ((uint8_t *)i->second.data (), (uint8_t *)i->second.data () + i->second.size ()); + bytes.push_back (0); // min_version field + mdb_cursor_put (i.cursor, i->first, rai::mdb_val (bytes.size (), bytes.data ()), MDB_CURRENT); + } + for (rai::store_iterator i (transaction_a, state_blocks), n (nullptr); i != n; ++i) + { + assert (i->second.size () == rai::state_block::size + sizeof (rai::block_hash)); + std::vector bytes ((uint8_t *)i->second.data (), (uint8_t *)i->second.data () + i->second.size ()); + bytes.insert (bytes.begin () + rai::state_block::size, 0); // version field + mdb_cursor_put (i.cursor, i->first, rai::mdb_val (bytes.size (), bytes.data ()), MDB_CURRENT); + } +} + void rai::block_store::clear (MDB_dbi db_a) { rai::transaction transaction (environment, nullptr, true); @@ -563,6 +591,21 @@ rai::uint128_t rai::block_store::block_balance (MDB_txn * transaction_a, rai::bl return visitor.balance; } +uint8_t rai::block_store::block_version (MDB_txn * transaction_a, rai::block_hash const & hash_a) +{ + rai::block_type type; + rai::mdb_val value; + auto status (mdb_get (transaction_a, state_blocks, rai::mdb_val (hash_a), value)); + assert (status == 0 || status == MDB_NOTFOUND); + uint8_t result (0); + if (status == 0) + { + assert (value.size () > rai::state_block::size); + result = *(reinterpret_cast (value.data ()) + rai::state_block::size); + } + return result; +} + void rai::block_store::representation_add (MDB_txn * transaction_a, rai::block_hash const & source_a, rai::uint128_t const & amount_a) { auto source_block (block_get (transaction_a, source_a)); @@ -605,13 +648,21 @@ void rai::block_store::block_put_raw (MDB_txn * transaction_a, MDB_dbi database_ assert (status2 == 0); } -void rai::block_store::block_put (MDB_txn * transaction_a, rai::block_hash const & hash_a, rai::block const & block_a, rai::block_hash const & successor_a) +void rai::block_store::block_put (MDB_txn * transaction_a, rai::block_hash const & hash_a, rai::block const & block_a, rai::block_hash const & successor_a, uint8_t version_a) { assert (successor_a.is_zero () || block_exists (transaction_a, successor_a)); + if (block_a.type () != rai::block_type::state) + { + assert (version_a == 0); + } std::vector vector; { rai::vectorstream stream (vector); block_a.serialize (stream); + if (block_a.type () == rai::block_type::state) + { + rai::write (stream, version_a); + } rai::write (stream, successor_a.bytes); } block_put_raw (transaction_a, block_database (block_a.type ()), hash_a, { vector.size (), vector.data () }); @@ -747,7 +798,8 @@ rai::block_hash rai::block_store::block_successor (MDB_txn * transaction_a, rai: void rai::block_store::block_successor_clear (MDB_txn * transaction_a, rai::block_hash const & hash_a) { auto block (block_get (transaction_a, hash_a)); - block_put (transaction_a, hash_a, *block); + auto version (block_version (transaction_a, hash_a)); + block_put (transaction_a, hash_a, *block, version); } std::unique_ptr rai::block_store::block_get (MDB_txn * transaction_a, rai::block_hash const & hash_a) @@ -957,12 +1009,14 @@ bool rai::block_store::pending_get (MDB_txn * transaction_a, rai::pending_key co else { result = false; - assert (value.size () == sizeof (pending_a.source.bytes) + sizeof (pending_a.amount.bytes)); + assert (value.size () == sizeof (pending_a.source.bytes) + sizeof (pending_a.amount.bytes) + sizeof (pending_a.min_version)); rai::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); auto error1 (rai::read (stream, pending_a.source)); assert (!error1); auto error2 (rai::read (stream, pending_a.amount)); assert (!error2); + auto error3 (rai::read (stream, pending_a.min_version)); + assert (!error3); } return result; } diff --git a/rai/blockstore.hpp b/rai/blockstore.hpp index 1ffe5952..ab1025eb 100644 --- a/rai/blockstore.hpp +++ b/rai/blockstore.hpp @@ -38,7 +38,7 @@ public: 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)); + void block_put (MDB_txn *, rai::block_hash const &, rai::block const &, rai::block_hash const & = rai::block_hash (0), uint8_t version = 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 &); @@ -79,6 +79,7 @@ public: 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 &); + uint8_t block_version (MDB_txn *, rai::block_hash const &); static size_t const block_info_max = 32; rai::uint128_t representation_get (MDB_txn *, rai::account const &); @@ -128,6 +129,7 @@ public: void upgrade_v8_to_v9 (MDB_txn *); void upgrade_v9_to_v10 (MDB_txn *); void upgrade_v10_to_v11 (MDB_txn *); + void upgrade_v11_to_v12 (MDB_txn *); // Requires a write transaction rai::raw_key get_node_id (MDB_txn *); diff --git a/rai/common.cpp b/rai/common.cpp index d2432d6f..891634d4 100644 --- a/rai/common.cpp +++ b/rai/common.cpp @@ -195,24 +195,26 @@ rep_block (0), open_block (0), balance (0), modified (0), -block_count (0) +block_count (0), +version (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"); + static_assert (sizeof (head) + sizeof (rep_block) + sizeof (open_block) + sizeof (balance) + sizeof (modified) + sizeof (block_count) + sizeof (version) == sizeof (*this), "Class not packed"); std::copy (reinterpret_cast (val_a.mv_data), reinterpret_cast (val_a.mv_data) + sizeof (*this), reinterpret_cast (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) : +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, uint8_t version_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) +block_count (block_count_a), +version (version_a) { } @@ -224,6 +226,7 @@ void rai::account_info::serialize (rai::stream & stream_a) const write (stream_a, balance.bytes); write (stream_a, modified); write (stream_a, block_count); + write (stream_a, version); } bool rai::account_info::deserialize (rai::stream & stream_a) @@ -244,6 +247,10 @@ bool rai::account_info::deserialize (rai::stream & stream_a) if (!error) { error = read (stream_a, block_count); + if (!error) + { + error = read (stream_a, version); + } } } } @@ -254,7 +261,7 @@ bool rai::account_info::deserialize (rai::stream & stream_a) 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; + 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 && version == other_a.version; } bool rai::account_info::operator!= (rai::account_info const & other_a) const @@ -283,20 +290,22 @@ size_t rai::block_counts::sum () rai::pending_info::pending_info () : source (0), -amount (0) +amount (0), +min_version (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"); + static_assert (sizeof (source) + sizeof (amount) + sizeof (min_version) == sizeof (*this), "Packed class"); std::copy (reinterpret_cast (val_a.mv_data), reinterpret_cast (val_a.mv_data) + sizeof (*this), reinterpret_cast (this)); } -rai::pending_info::pending_info (rai::account const & source_a, rai::amount const & amount_a) : +rai::pending_info::pending_info (rai::account const & source_a, rai::amount const & amount_a, uint8_t min_version_a) : source (source_a), -amount (amount_a) +amount (amount_a), +min_version (min_version_a) { } @@ -304,6 +313,7 @@ void rai::pending_info::serialize (rai::stream & stream_a) const { rai::write (stream_a, source.bytes); rai::write (stream_a, amount.bytes); + rai::write (stream_a, min_version); } bool rai::pending_info::deserialize (rai::stream & stream_a) @@ -312,13 +322,17 @@ bool rai::pending_info::deserialize (rai::stream & stream_a) if (!result) { result = rai::read (stream_a, amount.bytes); + if (!result) + { + result = rai::read (stream_a, min_version); + } } return result; } bool rai::pending_info::operator== (rai::pending_info const & other_a) const { - return source == other_a.source && amount == other_a.amount; + return source == other_a.source && amount == other_a.amount && min_version == other_a.min_version; } rai::mdb_val rai::pending_info::val () const @@ -780,7 +794,7 @@ void rai::genesis::initialize (MDB_txn * transaction_a, rai::block_store & store 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::max (), rai::seconds_since_epoch (), 1 }); + store_a.account_put (transaction_a, genesis_account, { hash_l, open->hash (), open->hash (), std::numeric_limits::max (), rai::seconds_since_epoch (), 1, 0 }); store_a.representation_put (transaction_a, genesis_account, std::numeric_limits::max ()); store_a.checksum_put (transaction_a, 0, 0, hash_l); store_a.frontier_put (transaction_a, hash_l, genesis_account); diff --git a/rai/common.hpp b/rai/common.hpp index d69a8b3d..95f80311 100644 --- a/rai/common.hpp +++ b/rai/common.hpp @@ -110,13 +110,14 @@ std::unique_ptr deserialize_block (MDB_val const &); /** * Latest information about an account */ +#pragma pack(push, 1) 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); + account_info (rai::block_hash const &, rai::block_hash const &, rai::block_hash const &, rai::amount const &, uint64_t, uint64_t, uint8_t); void serialize (rai::stream &) const; bool deserialize (rai::stream &); bool operator== (rai::account_info const &) const; @@ -129,24 +130,29 @@ public: /** Seconds since posix epoch */ uint64_t modified; uint64_t block_count; + uint8_t version; }; +#pragma pack(pop) /** * Information on an uncollected send */ +#pragma pack(push, 1) class pending_info { public: pending_info (); pending_info (MDB_val const &); - pending_info (rai::account const &, rai::amount const &); + pending_info (rai::account const &, rai::amount const &, uint8_t); 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; + uint8_t min_version; }; +#pragma pack(pop) class pending_key { public: @@ -222,11 +228,12 @@ enum class process_result 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 + unreceivable, // Source block doesn't exist, has already been received, or requires an account upgrade (epoch blocks) gap_previous, // Block marked as previous is unknown gap_source, // Block marked as source is unknown opened_burn_account, // The impossible happened, someone found the private key associated with the public key '0'. balance_mismatch, // Balance and amount delta don't match + representative_mismatch, // Representative is changed when it is not allowed block_position // This block cannot follow the previous block }; class process_return diff --git a/rai/core_test/block_store.cpp b/rai/core_test/block_store.cpp index 1362437a..a7b9124f 100644 --- a/rai/core_test/block_store.cpp +++ b/rai/core_test/block_store.cpp @@ -128,7 +128,7 @@ TEST (block_store, pending_iterator) ASSERT_TRUE (!init); rai::transaction transaction (store.environment, nullptr, true); ASSERT_EQ (store.pending_end (), store.pending_begin (transaction)); - store.pending_put (transaction, rai::pending_key (1, 2), { 2, 3 }); + store.pending_put (transaction, rai::pending_key (1, 2), { 2, 3, 4 }); auto current (store.pending_begin (transaction)); ASSERT_NE (store.pending_end (), current); rai::pending_key key1 (current->first); @@ -137,6 +137,7 @@ TEST (block_store, pending_iterator) rai::pending_info pending (current->second); ASSERT_EQ (rai::account (2), pending.source); ASSERT_EQ (rai::amount (3), pending.amount); + ASSERT_EQ (4, pending.min_version); } TEST (block_store, genesis) @@ -313,7 +314,7 @@ TEST (block_store, frontier_retrieval) rai::block_store store (init, rai::unique_path ()); ASSERT_TRUE (!init); rai::account account1 (0); - rai::account_info info1 (0, 0, 0, 0, 0, 0); + rai::account_info info1 (0, 0, 0, 0, 0, 0, 0); rai::transaction transaction (store.environment, nullptr, true); store.account_put (transaction, account1, info1); rai::account_info info2; @@ -329,7 +330,7 @@ TEST (block_store, one_account) rai::account account (0); rai::block_hash hash (0); rai::transaction transaction (store.environment, nullptr, true); - store.account_put (transaction, account, { hash, account, hash, 42, 100, 200 }); + store.account_put (transaction, account, { hash, account, hash, 42, 100, 200, 0 }); auto begin (store.latest_begin (transaction)); auto end (store.latest_end ()); ASSERT_NE (end, begin); @@ -374,8 +375,8 @@ TEST (block_store, two_account) rai::account account2 (3); rai::block_hash hash2 (4); rai::transaction transaction (store.environment, nullptr, true); - store.account_put (transaction, account1, { hash1, account1, hash1, 42, 100, 300 }); - store.account_put (transaction, account2, { hash2, account2, hash2, 84, 200, 400 }); + store.account_put (transaction, account1, { hash1, account1, hash1, 42, 100, 300, 0 }); + store.account_put (transaction, account2, { hash2, account2, hash2, 84, 200, 400, 0 }); auto begin (store.latest_begin (transaction)); auto end (store.latest_end ()); ASSERT_NE (end, begin); @@ -407,8 +408,8 @@ TEST (block_store, latest_find) rai::account account2 (3); rai::block_hash hash2 (4); rai::transaction transaction (store.environment, nullptr, true); - store.account_put (transaction, account1, { hash1, account1, hash1, 100, 0, 300 }); - store.account_put (transaction, account2, { hash2, account2, hash2, 200, 0, 400 }); + store.account_put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, 0 }); + store.account_put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, 0 }); auto first (store.latest_begin (transaction)); auto second (store.latest_begin (transaction)); ++second; diff --git a/rai/core_test/ledger.cpp b/rai/core_test/ledger.cpp index 9b3fd0e6..b6cbb3b0 100644 --- a/rai/core_test/ledger.cpp +++ b/rai/core_test/ledger.cpp @@ -2265,3 +2265,89 @@ TEST (ledger, state_receive_change_rollback) ASSERT_EQ (rai::genesis_amount - rai::Gxrb_ratio, ledger.weight (transaction, rai::genesis_account)); ASSERT_EQ (0, ledger.weight (transaction, rep.pub)); } + +TEST (ledger, epoch_blocks_general) +{ + bool init (false); + rai::block_store store (init, rai::unique_path ()); + ASSERT_TRUE (!init); + rai::stat stats; + rai::keypair epoch_key; + rai::ledger ledger (store, stats, 123, epoch_key.pub); + rai::genesis genesis; + rai::transaction transaction (store.environment, nullptr, true); + genesis.initialize (transaction, store); + rai::keypair destination; + rai::state_block epoch1 (rai::genesis_account, genesis.hash (), rai::genesis_account, rai::genesis_amount, 123, epoch_key.prv, epoch_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, epoch1).code); + rai::state_block epoch2 (rai::genesis_account, epoch1.hash (), rai::genesis_account, rai::genesis_amount, 123, epoch_key.prv, epoch_key.pub, 0); + ASSERT_EQ (rai::process_result::block_position, ledger.process (transaction, epoch2).code); + rai::account_info genesis_info; + ASSERT_FALSE (ledger.store.account_get (transaction, rai::genesis_account, genesis_info)); + ASSERT_EQ (genesis_info.version, 1); + ledger.rollback (transaction, epoch1.hash ()); + ASSERT_FALSE (ledger.store.account_get (transaction, rai::genesis_account, genesis_info)); + ASSERT_EQ (genesis_info.version, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, epoch1).code); + ASSERT_FALSE (ledger.store.account_get (transaction, rai::genesis_account, genesis_info)); + ASSERT_EQ (genesis_info.version, 1); + rai::change_block change1 (epoch1.hash (), rai::genesis_account, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ASSERT_EQ (rai::process_result::block_position, ledger.process (transaction, change1).code); + rai::state_block send1 (rai::genesis_account, epoch1.hash (), rai::genesis_account, rai::genesis_amount - rai::Gxrb_ratio, destination.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, send1).code); + rai::open_block open1 (send1.hash (), rai::genesis_account, destination.pub, destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::unreceivable, ledger.process (transaction, open1).code); + rai::state_block epoch3 (destination.pub, 0, rai::genesis_account, 0, 123, epoch_key.prv, epoch_key.pub, 0); + ASSERT_EQ (rai::process_result::representative_mismatch, ledger.process (transaction, epoch3).code); + rai::state_block epoch4 (destination.pub, 0, 0, 0, 123, epoch_key.prv, epoch_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, epoch4).code); + rai::receive_block receive1 (epoch4.hash (), send1.hash (), destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::block_position, ledger.process (transaction, receive1).code); + rai::state_block receive2 (destination.pub, epoch4.hash (), destination.pub, rai::Gxrb_ratio, send1.hash (), destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, receive2).code); + ASSERT_EQ (0, ledger.balance (transaction, epoch4.hash ())); + ASSERT_EQ (rai::Gxrb_ratio, ledger.balance (transaction, receive2.hash ())); + ASSERT_EQ (rai::Gxrb_ratio, ledger.amount (transaction, receive2.hash ())); + ASSERT_EQ (rai::genesis_amount - rai::Gxrb_ratio, ledger.weight (transaction, rai::genesis_account)); + ASSERT_EQ (rai::Gxrb_ratio, ledger.weight (transaction, destination.pub)); +} + +TEST (ledger, epoch_blocks_receive_upgrade) +{ + bool init (false); + rai::block_store store (init, rai::unique_path ()); + ASSERT_TRUE (!init); + rai::stat stats; + rai::keypair epoch_key; + rai::ledger ledger (store, stats, 123, epoch_key.pub); + rai::genesis genesis; + rai::transaction transaction (store.environment, nullptr, true); + genesis.initialize (transaction, store); + rai::keypair destination; + rai::state_block send1 (rai::genesis_account, genesis.hash (), rai::genesis_account, rai::genesis_amount - rai::Gxrb_ratio, destination.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, send1).code); + rai::state_block epoch1 (rai::genesis_account, send1.hash (), rai::genesis_account, rai::genesis_amount - rai::Gxrb_ratio, 123, epoch_key.prv, epoch_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, epoch1).code); + rai::state_block send2 (rai::genesis_account, epoch1.hash (), rai::genesis_account, rai::genesis_amount - rai::Gxrb_ratio * 2, destination.pub, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, send2).code); + rai::open_block open1 (send1.hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, open1).code); + rai::receive_block receive1 (open1.hash (), send2.hash (), destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::unreceivable, ledger.process (transaction, receive1).code); + rai::state_block receive2 (destination.pub, open1.hash (), destination.pub, rai::Gxrb_ratio * 2, send2.hash (), destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, receive2).code); + rai::account_info destination_info; + ASSERT_FALSE (ledger.store.account_get (transaction, destination.pub, destination_info)); + ASSERT_EQ (destination_info.version, 1); + ledger.rollback (transaction, receive2.hash ()); + ASSERT_FALSE (ledger.store.account_get (transaction, destination.pub, destination_info)); + ASSERT_EQ (destination_info.version, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, receive2).code); + ASSERT_FALSE (ledger.store.account_get (transaction, destination.pub, destination_info)); + ASSERT_EQ (destination_info.version, 1); + rai::keypair destination2; + rai::state_block send3 (destination.pub, receive2.hash (), destination.pub, rai::Gxrb_ratio, destination2.pub, destination.prv, destination.pub, 0); + ASSERT_EQ (rai::process_result::progress, ledger.process (transaction, send3).code); + rai::open_block open2 (send3.hash (), destination2.pub, destination2.pub, destination2.prv, destination2.pub, 0); + ASSERT_EQ (rai::process_result::unreceivable, ledger.process (transaction, open2).code); +} diff --git a/rai/core_test/node.cpp b/rai/core_test/node.cpp index 833ee698..cc5fd6f2 100644 --- a/rai/core_test/node.cpp +++ b/rai/core_test/node.cpp @@ -513,14 +513,12 @@ TEST (node_config, serialization) config1.receive_minimum = 10; config1.online_weight_minimum = 10; config1.online_weight_quorum = 10; - config1.password_fanout = 10; + config1.password_fanout = 20; config1.enable_voting = false; config1.callback_address = "test"; config1.callback_port = 10; config1.callback_target = "test"; config1.lmdb_max_dbs = 256; - config1.state_block_parse_canary = 10; - config1.state_block_generate_canary = 10; boost::property_tree::ptree tree; config1.serialize_json (tree); rai::logging logging2; @@ -538,11 +536,12 @@ TEST (node_config, serialization) ASSERT_NE (config2.callback_port, config1.callback_port); ASSERT_NE (config2.callback_target, config1.callback_target); ASSERT_NE (config2.lmdb_max_dbs, config1.lmdb_max_dbs); - ASSERT_NE (config2.state_block_parse_canary, config1.state_block_parse_canary); - ASSERT_NE (config2.state_block_generate_canary, config1.state_block_generate_canary); + + ASSERT_FALSE (tree.get_optional ("epoch_block_link")); + ASSERT_FALSE (tree.get_optional ("epoch_block_signer")); bool upgraded (false); - config2.deserialize_json (upgraded, tree); + ASSERT_FALSE (config2.deserialize_json (upgraded, tree)); ASSERT_FALSE (upgraded); ASSERT_EQ (config2.bootstrap_fraction_numerator, config1.bootstrap_fraction_numerator); ASSERT_EQ (config2.peering_port, config1.peering_port); @@ -555,8 +554,6 @@ TEST (node_config, serialization) ASSERT_EQ (config2.callback_port, config1.callback_port); ASSERT_EQ (config2.callback_target, config1.callback_target); ASSERT_EQ (config2.lmdb_max_dbs, config1.lmdb_max_dbs); - ASSERT_EQ (config2.state_block_parse_canary, config1.state_block_parse_canary); - ASSERT_EQ (config2.state_block_generate_canary, config1.state_block_generate_canary); } TEST (node_config, v1_v2_upgrade) diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index 8f550e1a..d73ff06b 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -819,7 +819,7 @@ TEST (rpc, frontier) { rai::keypair key; source[key.pub] = key.prv.data; - system.nodes[0]->store.account_put (transaction, key.pub, rai::account_info (key.prv.data, 0, 0, 0, 0, 0)); + system.nodes[0]->store.account_put (transaction, key.pub, rai::account_info (key.prv.data, 0, 0, 0, 0, 0, 0)); } } rai::keypair key; @@ -859,7 +859,7 @@ TEST (rpc, frontier_limited) { rai::keypair key; source[key.pub] = key.prv.data; - system.nodes[0]->store.account_put (transaction, key.pub, rai::account_info (key.prv.data, 0, 0, 0, 0, 0)); + system.nodes[0]->store.account_put (transaction, key.pub, rai::account_info (key.prv.data, 0, 0, 0, 0, 0, 0)); } } rai::keypair key; @@ -889,7 +889,7 @@ TEST (rpc, frontier_startpoint) { rai::keypair key; source[key.pub] = key.prv.data; - system.nodes[0]->store.account_put (transaction, key.pub, rai::account_info (key.prv.data, 0, 0, 0, 0, 0)); + system.nodes[0]->store.account_put (transaction, key.pub, rai::account_info (key.prv.data, 0, 0, 0, 0, 0, 0)); } } rai::keypair key; @@ -1419,6 +1419,8 @@ TEST (rpc, pending) blocks[hash] = amount; boost::optional source (i->second.get_optional ("source")); ASSERT_FALSE (source.is_initialized ()); + boost::optional min_version (i->second.get_optional ("min_version")); + ASSERT_FALSE (min_version.is_initialized ()); } ASSERT_EQ (blocks[block1->hash ()], 100); request.put ("threshold", "101"); @@ -1432,6 +1434,7 @@ TEST (rpc, pending) ASSERT_EQ (0, blocks_node.size ()); request.put ("threshold", "0"); request.put ("source", "true"); + request.put ("min_version", "true"); test_response response2 (request, rpc, system.service); while (response2.status == 0) { @@ -1448,6 +1451,7 @@ TEST (rpc, pending) hash.decode_hex (i->first); amounts[hash].decode_dec (i->second.get ("amount")); sources[hash].decode_account (i->second.get ("source")); + ASSERT_EQ (i->second.get ("min_version"), 0); } ASSERT_EQ (amounts[block1->hash ()], 100); ASSERT_EQ (sources[block1->hash ()], rai::test_genesis_key.pub); @@ -2574,6 +2578,8 @@ TEST (rpc, wallet_pending) blocks[hash] = amount; boost::optional source (i->second.get_optional ("source")); ASSERT_FALSE (source.is_initialized ()); + boost::optional min_version (i->second.get_optional ("min_version")); + ASSERT_FALSE (min_version.is_initialized ()); } } ASSERT_EQ (blocks[block1->hash ()], 100); @@ -2588,6 +2594,7 @@ TEST (rpc, wallet_pending) ASSERT_EQ (0, pending1.size ()); request.put ("threshold", "0"); request.put ("source", "true"); + request.put ("min_version", "true"); test_response response2 (request, rpc, system0.service); while (response2.status == 0) { @@ -2606,6 +2613,7 @@ TEST (rpc, wallet_pending) hash.decode_hex (i->first); amounts[hash].decode_dec (i->second.get ("amount")); sources[hash].decode_account (i->second.get ("source")); + ASSERT_EQ (i->second.get ("min_version"), 0); } } ASSERT_EQ (amounts[block1->hash ()], 100); @@ -2889,6 +2897,7 @@ TEST (rpc, account_info) ASSERT_LT (std::abs ((long)time - stol (modified_timestamp)), 5); std::string block_count (response.json.get ("block_count")); ASSERT_EQ ("2", block_count); + ASSERT_EQ (0, response.json.get ("account_version")); boost::optional weight (response.json.get_optional ("weight")); ASSERT_FALSE (weight.is_initialized ()); boost::optional pending (response.json.get_optional ("pending")); diff --git a/rai/ledger.cpp b/rai/ledger.cpp index 76a2f7fd..c657923d 100644 --- a/rai/ledger.cpp +++ b/rai/ledger.cpp @@ -55,7 +55,7 @@ public: 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), { source_account, amount }); + ledger.store.pending_put (transaction, rai::pending_key (destination_account, block_a.hashables.source), { source_account, amount, 0 }); 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); @@ -74,7 +74,7 @@ public: 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), { source_account, amount }); + ledger.store.pending_put (transaction, rai::pending_key (destination_account, block_a.hashables.source), { source_account, amount, 0 }); ledger.store.frontier_del (transaction, hash); ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::open); } @@ -118,6 +118,9 @@ public: ledger.store.representation_add (transaction, representative, balance); } + rai::account_info info; + auto error (ledger.store.account_get (transaction, block_a.hashables.account, info)); + if (is_send) { rai::pending_key key (block_a.hashables.link, hash); @@ -128,17 +131,17 @@ public: ledger.store.pending_del (transaction, key); ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::send); } - else if (!block_a.hashables.link.is_zero ()) + else if (!block_a.hashables.link.is_zero () && block_a.hashables.link != ledger.epoch_link) { - rai::pending_info info (ledger.account (transaction, block_a.hashables.link), block_a.hashables.balance.number () - balance); - ledger.store.pending_put (transaction, rai::pending_key (block_a.hashables.account, block_a.hashables.link), info); + auto source_version (ledger.store.block_version (transaction, block_a.hashables.link)); + rai::pending_info pending_info (ledger.account (transaction, block_a.hashables.link), block_a.hashables.balance.number () - balance, source_version); + ledger.store.pending_put (transaction, rai::pending_key (block_a.hashables.account, block_a.hashables.link), pending_info); ledger.stats.inc (rai::stat::type::rollback, rai::stat::detail::receive); } - rai::account_info info; - auto error (ledger.store.account_get (transaction, block_a.hashables.account, info)); assert (!error); - ledger.change_latest (transaction, block_a.hashables.account, block_a.hashables.previous, representative, balance, info.block_count - 1); + auto previous_version (ledger.store.block_version (transaction, block_a.hashables.previous)); + ledger.change_latest (transaction, block_a.hashables.account, block_a.hashables.previous, representative, balance, info.block_count - 1, false, previous_version); auto previous (ledger.store.block_get (transaction, block_a.hashables.previous)); if (previous != nullptr) @@ -170,6 +173,7 @@ public: void change_block (rai::change_block const &) override; void state_block (rai::state_block const &) override; void state_block_impl (rai::state_block const &); + void epoch_block_impl (rai::state_block const &); rai::ledger & ledger; MDB_txn * transaction; rai::process_return result; @@ -177,7 +181,17 @@ public: void ledger_processor::state_block (rai::state_block const & block_a) { - state_block_impl (block_a); + // Check if this is an epoch block + rai::account_info info; + ledger.store.account_get (transaction, block_a.hashables.account, info); + if (block_a.hashables.balance == info.balance && !ledger.epoch_link.is_zero () && block_a.hashables.link == ledger.epoch_link) + { + epoch_block_impl (block_a); + } + else + { + state_block_impl (block_a); + } } void ledger_processor::state_block_impl (rai::state_block const & block_a) @@ -193,12 +207,14 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) result.code = block_a.hashables.account.is_zero () ? rai::process_result::opened_burn_account : rai::process_result::progress; // Is this for the burn account? (Unambiguous) if (result.code == rai::process_result::progress) { + uint8_t account_version (0); rai::account_info info; result.amount = block_a.hashables.balance; auto is_send (false); auto account_error (ledger.store.account_get (transaction, block_a.hashables.account, info)); if (!account_error) { + account_version = info.version; // Account already exists result.code = block_a.hashables.previous.is_zero () ? rai::process_result::fork : rai::process_result::progress; // Has this account already been opened? (Ambigious) if (result.code == rai::process_result::progress) @@ -236,6 +252,7 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) if (result.code == rai::process_result::progress) { result.code = result.amount == pending.amount ? rai::process_result::progress : rai::process_result::balance_mismatch; + account_version = std::max (account_version, pending.min_version); } } } @@ -250,7 +267,7 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) { ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::state_block); result.state_is_send = is_send; - ledger.store.block_put (transaction, hash, block_a); + ledger.store.block_put (transaction, hash, block_a, 0, account_version); if (!info.rep_block.is_zero ()) { @@ -263,7 +280,7 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) if (is_send) { rai::pending_key key (block_a.hashables.link, hash); - rai::pending_info info (block_a.hashables.account, result.amount.number ()); + rai::pending_info info (block_a.hashables.account, result.amount.number (), account_version); ledger.store.pending_put (transaction, key, info); } else if (!block_a.hashables.link.is_zero ()) @@ -271,7 +288,7 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) ledger.store.pending_del (transaction, rai::pending_key (block_a.hashables.account, block_a.hashables.link)); } - ledger.change_latest (transaction, block_a.hashables.account, hash, hash, block_a.hashables.balance, info.block_count + 1, true); + ledger.change_latest (transaction, block_a.hashables.account, hash, hash, block_a.hashables.balance, info.block_count + 1, true, account_version); if (!ledger.store.frontier_get (transaction, info.head).is_zero ()) { ledger.store.frontier_del (transaction, info.head); @@ -284,6 +301,69 @@ void ledger_processor::state_block_impl (rai::state_block const & block_a) } } +void ledger_processor::epoch_block_impl (rai::state_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? (Unambiguous) + if (result.code == rai::process_result::progress) + { + result.code = validate_message (ledger.epoch_signer, hash, block_a.signature) ? rai::process_result::bad_signature : rai::process_result::progress; // Is this block signed correctly (Unambiguous) + if (result.code == rai::process_result::progress) + { + result.code = block_a.hashables.account.is_zero () ? rai::process_result::opened_burn_account : rai::process_result::progress; // Is this for the burn account? (Unambiguous) + if (result.code == rai::process_result::progress) + { + rai::account_info info; + auto account_error (ledger.store.account_get (transaction, block_a.hashables.account, info)); + if (!account_error) + { + // Account already exists + result.code = block_a.hashables.previous.is_zero () ? rai::process_result::fork : rai::process_result::progress; // Has this account already been opened? (Ambigious) + if (result.code == rai::process_result::progress) + { + result.code = ledger.store.block_exists (transaction, block_a.hashables.previous) ? rai::process_result::progress : rai::process_result::gap_previous; // Does the previous block exist in the ledger? (Unambigious) + if (result.code == rai::process_result::progress) + { + result.code = block_a.hashables.previous == info.head ? rai::process_result::progress : rai::process_result::fork; // Is the previous block the account's head block? (Ambigious) + if (result.code == rai::process_result::progress) + { + auto last_rep_block (ledger.store.block_get (transaction, info.rep_block)); + assert (last_rep_block != nullptr); + result.code = block_a.hashables.representative == last_rep_block->representative () ? rai::process_result::progress : rai::process_result::representative_mismatch; + } + } + } + } + else + { + result.code = block_a.hashables.representative.is_zero () ? rai::process_result::progress : rai::process_result::representative_mismatch; + } + if (result.code == rai::process_result::progress) + { + result.code = info.version == 0 ? rai::process_result::progress : rai::process_result::block_position; + if (result.code == rai::process_result::progress) + { + result.code = block_a.hashables.balance == info.balance ? rai::process_result::progress : rai::process_result::balance_mismatch; + if (result.code == rai::process_result::progress) + { + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::epoch_block); + result.account = block_a.hashables.account; + result.amount = 0; + ledger.store.block_put (transaction, hash, block_a, 0, 1); + ledger.change_latest (transaction, block_a.hashables.account, hash, hash, info.balance, info.block_count + 1, true, 1); + if (!ledger.store.frontier_get (transaction, info.head).is_zero ()) + { + ledger.store.frontier_del (transaction, info.head); + } + } + } + } + } + } + } +} + void ledger_processor::change_block (rai::change_block const & block_a) { auto hash (block_a.hash ()); @@ -358,7 +438,7 @@ void ledger_processor::send_block (rai::send_block const & block_a) 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.pending_put (transaction, rai::pending_key (block_a.hashables.destination, hash), { account, amount, 0 }); ledger.store.frontier_del (transaction, block_a.hashables.previous); ledger.store.frontier_put (transaction, hash, account); result.account = account; @@ -407,19 +487,23 @@ void ledger_processor::receive_block (rai::receive_block const & block_a) 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; - ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::receive); + result.code = pending.min_version == 0 ? rai::process_result::progress : rai::process_result::unreceivable; // Are we receiving a state-only send? (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; + ledger.stats.inc (rai::stat::type::ledger, rai::stat::detail::receive); + } } } } @@ -460,17 +544,21 @@ void ledger_processor::open_block (rai::open_block const & block_a) 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.stats.inc (rai::stat::type::ledger, rai::stat::detail::open); + result.code = pending.min_version == 0 ? rai::process_result::progress : rai::process_result::unreceivable; // Are we receiving a state-only send? (Malformed) + 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.stats.inc (rai::stat::type::ledger, rai::stat::detail::open); + } } } } @@ -498,10 +586,12 @@ bool rai::shared_ptr_block_hash::operator() (std::shared_ptr const & return lhs->hash () == rhs->hash (); } -rai::ledger::ledger (rai::block_store & store_a, rai::stat & stat_a) : +rai::ledger::ledger (rai::block_store & store_a, rai::stat & stat_a, rai::uint256_union const & epoch_link_a, rai::account const & epoch_signer_a) : store (store_a), stats (stat_a), -check_bootstrap_weights (true) +check_bootstrap_weights (true), +epoch_link (epoch_link_a), +epoch_signer (epoch_signer_a) { } @@ -794,7 +884,7 @@ void rai::ledger::checksum_update (MDB_txn * transaction_a, rai::block_hash cons 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, bool is_state) +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, bool is_state, uint8_t version_a) { rai::account_info info; auto exists (!store.account_get (transaction_a, account_a, info)); @@ -814,6 +904,7 @@ void rai::ledger::change_latest (MDB_txn * transaction_a, rai::account const & a info.balance = balance_a; info.modified = rai::seconds_since_epoch (); info.block_count = block_count_a; + info.version = version_a; store.account_put (transaction_a, account_a, info); if (!(block_count_a % store.block_info_max) && !is_state) { diff --git a/rai/ledger.hpp b/rai/ledger.hpp index 5f72cf45..c549d100 100644 --- a/rai/ledger.hpp +++ b/rai/ledger.hpp @@ -17,7 +17,7 @@ using tally_t = std::map, std::great class ledger { public: - ledger (rai::block_store &, rai::stat &); + ledger (rai::block_store &, rai::stat &, rai::uint256_union const & = 1, rai::account const & = 0); std::pair> winner (MDB_txn *, rai::votes const & votes_a); // Map of weight -> associated block, ordered greatest to least rai::tally_t tally (MDB_txn *, rai::votes const &); @@ -41,7 +41,7 @@ public: rai::block_hash block_source (MDB_txn *, rai::block const &); 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, bool = false); + void change_latest (MDB_txn *, rai::account const &, rai::block_hash const &, rai::account const &, rai::uint128_union const &, uint64_t, bool = false, uint8_t = 0); 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 &); @@ -51,5 +51,7 @@ public: std::unordered_map bootstrap_weights; uint64_t bootstrap_weight_max_blocks; std::atomic check_bootstrap_weights; + rai::uint256_union epoch_link; + rai::account epoch_signer; }; }; diff --git a/rai/node/bootstrap.cpp b/rai/node/bootstrap.cpp index caead313..ca45ab0b 100644 --- a/rai/node/bootstrap.cpp +++ b/rai/node/bootstrap.cpp @@ -1984,7 +1984,7 @@ void rai::bulk_push_server::received_block (boost::system::error_code const & ec rai::frontier_req_server::frontier_req_server (std::shared_ptr const & connection_a, std::unique_ptr request_a) : connection (connection_a), current (request_a->start.number () - 1), -info (0, 0, 0, 0, 0, 0), +info (0, 0, 0, 0, 0, 0, 0), request (std::move (request_a)), send_buffer (std::make_shared> ()) { diff --git a/rai/node/common.cpp b/rai/node/common.cpp index 03fafbac..bb99ee4a 100644 --- a/rai/node/common.cpp +++ b/rai/node/common.cpp @@ -99,37 +99,44 @@ void rai::message_parser::deserialize_buffer (uint8_t const * buffer_a, size_t s rai::message_header header (error, stream); if (!error) { - switch (header.type) + if (rai::rai_network == rai::rai_networks::rai_beta_network && header.version_using < rai::protocol_version) { - case rai::message_type::keepalive: + status = parse_status::outdated_version; + } + else + { + switch (header.type) { - deserialize_keepalive (stream, header); - break; - } - case rai::message_type::publish: - { - deserialize_publish (stream, header); - break; - } - case rai::message_type::confirm_req: - { - deserialize_confirm_req (stream, header); - break; - } - case rai::message_type::confirm_ack: - { - deserialize_confirm_ack (stream, header); - break; - } - case rai::message_type::node_id_handshake: - { - deserialize_node_id_handshake (stream, header); - break; - } - default: - { - status = parse_status::invalid_message_type; - break; + case rai::message_type::keepalive: + { + deserialize_keepalive (stream, header); + break; + } + case rai::message_type::publish: + { + deserialize_publish (stream, header); + break; + } + case rai::message_type::confirm_req: + { + deserialize_confirm_req (stream, header); + break; + } + case rai::message_type::confirm_ack: + { + deserialize_confirm_ack (stream, header); + break; + } + case rai::message_type::node_id_handshake: + { + deserialize_node_id_handshake (stream, header); + break; + } + default: + { + status = parse_status::invalid_message_type; + break; + } } } } diff --git a/rai/node/common.hpp b/rai/node/common.hpp index d7214d26..6239b6aa 100644 --- a/rai/node/common.hpp +++ b/rai/node/common.hpp @@ -190,7 +190,8 @@ public: invalid_publish_message, invalid_confirm_req_message, invalid_confirm_ack_message, - invalid_node_id_handshake_message + invalid_node_id_handshake_message, + outdated_version }; message_parser (rai::message_visitor &, rai::work_pool &); void deserialize_buffer (uint8_t const *, size_t); diff --git a/rai/node/node.cpp b/rai/node/node.cpp index 2dd5683a..3ff911ab 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -899,6 +899,9 @@ bootstrap_connections_max (64), callback_port (0), lmdb_max_dbs (128) { + const char * epoch_message ("epoch v1 block"); + strncpy ((char *)epoch_block_link.bytes.data (), epoch_message, epoch_block_link.bytes.size ()); + epoch_block_signer = rai::genesis_account; switch (rai::rai_network) { case rai::rai_networks::rai_test_network: @@ -911,8 +914,6 @@ lmdb_max_dbs (128) preconfigured_representatives.push_back (rai::account ("259A40656144FAA16D2A8516F7BE9C74A63C6CA399960EDB747D144ABB0F7ABD")); preconfigured_representatives.push_back (rai::account ("259A40A92FA42E2240805DE8618EC4627F0BA41937160B4CFF7F5335FD1933DF")); preconfigured_representatives.push_back (rai::account ("259A40FF3262E273EC451E873C4CDF8513330425B38860D882A16BCC74DA9B73")); - state_block_parse_canary = rai::block_hash ("5005F5283DE8D2DAB0DAC41DE9BD23640F962B4F0EA7D3128C2EA3D78D578E27"); - state_block_generate_canary = rai::block_hash ("FC18E2265FB835E8CF60E63531053A768CEDF5194263B01A5C95574944E4660D"); break; case rai::rai_networks::rai_live_network: preconfigured_peers.push_back ("rai.raiblocks.net"); @@ -924,8 +925,6 @@ lmdb_max_dbs (128) preconfigured_representatives.push_back (rai::account ("2399A083C600AA0572F5E36247D978FCFC840405F8D4B6D33161C0066A55F431")); preconfigured_representatives.push_back (rai::account ("2298FAB7C61058E77EA554CB93EDEEDA0692CBFCC540AB213B2836B29029E23A")); preconfigured_representatives.push_back (rai::account ("3FE80B4BC842E82C1C18ABFEEC47EA989E63953BC82AC411F304D13833D52A56")); - state_block_parse_canary = rai::block_hash ("89F1C0AC4C5AD23964AB880571E3EA67FDC41BD11AB20E67F0A29CF94CD4E24A"); - state_block_generate_canary = rai::block_hash ("B6DC4D64801BEC7D81DAA086A5733D251E8CBA0E9226FD6173D97C0569EC2998"); break; default: assert (false); @@ -935,7 +934,7 @@ lmdb_max_dbs (128) void rai::node_config::serialize_json (boost::property_tree::ptree & tree_a) const { - tree_a.put ("version", "12"); + tree_a.put ("version", "13"); tree_a.put ("peering_port", std::to_string (peering_port)); tree_a.put ("bootstrap_fraction_numerator", std::to_string (bootstrap_fraction_numerator)); tree_a.put ("receive_minimum", receive_minimum.to_string_dec ()); @@ -978,8 +977,6 @@ void rai::node_config::serialize_json (boost::property_tree::ptree & tree_a) con tree_a.put ("callback_port", std::to_string (callback_port)); tree_a.put ("callback_target", callback_target); tree_a.put ("lmdb_max_dbs", lmdb_max_dbs); - tree_a.put ("state_block_parse_canary", state_block_parse_canary.to_string ()); - tree_a.put ("state_block_generate_canary", state_block_generate_canary.to_string ()); } bool rai::node_config::upgrade_json (unsigned version, boost::property_tree::ptree & tree_a) @@ -1054,8 +1051,8 @@ bool rai::node_config::upgrade_json (unsigned version, boost::property_tree::ptr tree_a.put ("version", "9"); result = true; case 9: - tree_a.put ("state_block_parse_canary", state_block_parse_canary.to_string ()); - tree_a.put ("state_block_generate_canary", state_block_generate_canary.to_string ()); + tree_a.put ("state_block_parse_canary", rai::block_hash (0).to_string ()); + tree_a.put ("state_block_generate_canary", rai::block_hash (0).to_string ()); tree_a.erase ("version"); tree_a.put ("version", "10"); result = true; @@ -1076,6 +1073,12 @@ bool rai::node_config::upgrade_json (unsigned version, boost::property_tree::ptr result = true; } case 12: + tree_a.erase ("state_block_parse_canary"); + tree_a.erase ("state_block_generate_canary"); + tree_a.erase ("version"); + tree_a.put ("version", "13"); + result = true; + case 13: break; default: throw std::runtime_error ("Unknown node_config version"); @@ -1161,8 +1164,6 @@ bool rai::node_config::deserialize_json (bool & upgraded_a, boost::property_tree callback_target = tree_a.get ("callback_target"); auto lmdb_max_dbs_l = tree_a.get ("lmdb_max_dbs"); result |= parse_port (callback_port_l, callback_port); - auto state_block_parse_canary_l = tree_a.get ("state_block_parse_canary"); - auto state_block_generate_canary_l = tree_a.get ("state_block_generate_canary"); try { peering_port = std::stoul (peering_port_l); @@ -1182,8 +1183,6 @@ bool rai::node_config::deserialize_json (bool & upgraded_a, boost::property_tree result |= password_fanout < 16; result |= password_fanout > 1024 * 1024; result |= io_threads == 0; - result |= state_block_parse_canary.decode_hex (state_block_parse_canary_l); - result |= state_block_generate_canary.decode_hex (state_block_generate_canary_l); } catch (std::logic_error const &) { @@ -1532,6 +1531,14 @@ rai::process_return rai::block_processor::process_receive_one (MDB_txn * transac } break; } + case rai::process_result::representative_mismatch: + { + if (node.config.logging.ledger_logging ()) + { + BOOST_LOG (node.log) << boost::str (boost::format ("Representative mismatch for: %1%") % hash.to_string ()); + } + break; + } case rai::process_result::block_position: { if (node.config.logging.ledger_logging ()) @@ -1568,7 +1575,7 @@ alarm (alarm_a), work (work_a), store (init_a.block_store_init, application_path_a / "data.ldb", config_a.lmdb_max_dbs), gap_cache (*this), -ledger (store, stats), +ledger (store, stats, config.epoch_block_link, config.epoch_block_signer), active (*this), network (*this, config.peering_port), bootstrap_initiator (*this), diff --git a/rai/node/node.hpp b/rai/node/node.hpp index 465e933b..10737705 100644 --- a/rai/node/node.hpp +++ b/rai/node/node.hpp @@ -483,8 +483,8 @@ public: std::string callback_target; int lmdb_max_dbs; rai::stat_config stat_config; - rai::block_hash state_block_parse_canary; - rai::block_hash state_block_generate_canary; + rai::uint256_union epoch_block_link; + rai::account epoch_block_signer; static std::chrono::seconds constexpr keepalive_period = std::chrono::seconds (60); static std::chrono::seconds constexpr keepalive_cutoff = keepalive_period * 5; static std::chrono::minutes constexpr wallet_backup_interval = std::chrono::minutes (5); diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index de72cd64..2c8ab3e8 100644 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -352,6 +352,7 @@ void rai::rpc_handler::account_info () response_l.put ("balance", balance); response_l.put ("modified_timestamp", std::to_string (info.modified)); response_l.put ("block_count", std::to_string (info.block_count)); + response_l.put ("account_version", std::to_string (info.version)); if (representative) { auto block (node.store.block_get (transaction, info.rep_block)); @@ -1795,6 +1796,14 @@ public: tree.put ("subtype", "change"); } } + else if (balance == previous_balance && !handler.node.ledger.epoch_link.is_zero () && block_a.hashables.link == handler.node.ledger.epoch_link) + { + if (raw) + { + tree.put ("subtype", "epoch"); + tree.put ("account", handler.node.ledger.epoch_signer.to_account ()); + } + } else { if (raw) @@ -2323,6 +2332,7 @@ void rai::rpc_handler::pending () } } const bool source = request.get ("source", false); + const bool min_version = request.get ("min_version", false); boost::property_tree::ptree response_l; boost::property_tree::ptree peers_l; { @@ -2331,7 +2341,7 @@ void rai::rpc_handler::pending () for (auto i (node.store.pending_begin (transaction, rai::pending_key (account, 0))), n (node.store.pending_begin (transaction, rai::pending_key (end, 0))); i != n && peers_l.size () < count; ++i) { rai::pending_key key (i->first); - if (threshold.is_zero () && !source) + if (threshold.is_zero () && !source && !min_version) { boost::property_tree::ptree entry; entry.put ("", key.hash.to_string ()); @@ -2342,11 +2352,18 @@ void rai::rpc_handler::pending () rai::pending_info info (i->second); if (info.amount.number () >= threshold.number ()) { - if (source) + if (source || min_version) { boost::property_tree::ptree pending_tree; pending_tree.put ("amount", info.amount.number ().convert_to ()); - pending_tree.put ("source", info.source.to_account ()); + if (source) + { + pending_tree.put ("source", info.source.to_account ()); + } + if (min_version) + { + pending_tree.put ("min_version", std::to_string (info.min_version)); + } peers_l.add_child (key.hash.to_string (), pending_tree); } else @@ -3992,6 +4009,7 @@ void rai::rpc_handler::wallet_pending () } } const bool source = request.get ("source", false); + const bool min_version = request.get ("min_version", false); boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); @@ -4014,11 +4032,18 @@ void rai::rpc_handler::wallet_pending () rai::pending_info info (ii->second); if (info.amount.number () >= threshold.number ()) { - if (source) + if (source || min_version) { boost::property_tree::ptree pending_tree; pending_tree.put ("amount", info.amount.number ().convert_to ()); - pending_tree.put ("source", info.source.to_account ()); + if (source) + { + pending_tree.put ("source", info.source.to_account ()); + } + if (min_version) + { + pending_tree.put ("min_version", std::to_string (info.min_version)); + } peers_l.add_child (key.hash.to_string (), pending_tree); } else diff --git a/rai/node/stats.cpp b/rai/node/stats.cpp index ba3796db..3762e9e3 100644 --- a/rai/node/stats.cpp +++ b/rai/node/stats.cpp @@ -411,6 +411,9 @@ std::string rai::stat::detail_to_string (uint32_t key) case rai::stat::detail::state_block: res = "state_block"; break; + case rai::stat::detail::epoch_block: + res = "epoch_block"; + break; case rai::stat::detail::vote_valid: res = "vote_valid"; break; diff --git a/rai/node/stats.hpp b/rai/node/stats.hpp index 5a266c78..15b589d1 100644 --- a/rai/node/stats.hpp +++ b/rai/node/stats.hpp @@ -202,6 +202,7 @@ public: open, change, state_block, + epoch_block, // message specific keepalive, diff --git a/rai/qt/qt.cpp b/rai/qt/qt.cpp index d306e34a..8a10f1f5 100644 --- a/rai/qt/qt.cpp +++ b/rai/qt/qt.cpp @@ -547,6 +547,11 @@ public: type = "Change"; account = block_a.hashables.representative; } + else if (balance == previous_balance && !ledger.epoch_link.is_zero () && block_a.hashables.link == ledger.epoch_link) + { + type = "Epoch"; + account = ledger.epoch_signer; + } else { type = "Receive";