dncurrency/nano/core_test/ledger.cpp
Dimitrios Siganos 78631a6b1c
Move test code into namespace nano::test (#3890)
* Move nano::establish_tcp into nano::test namespace
* Move system.hpp declarations into namespace nano::test
* Move telemetry test code to namespace nano::test
* Move testutil declarations into namespace nano::test
2022-08-10 20:48:31 +01:00

5677 lines
239 KiB
C++

#include <nano/lib/stats.hpp>
#include <nano/lib/threading.hpp>
#include <nano/node/election.hpp>
#include <nano/node/rocksdb/rocksdb.hpp>
#include <nano/node/transport/inproc.hpp>
#include <nano/test_common/ledger.hpp>
#include <nano/test_common/system.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
using namespace std::chrono_literals;
// Init returns an error if it can't open files at the path
TEST (ledger, store_error)
{
if (nano::rocksdb_config::using_rocksdb_in_tests ())
{
// Don't test this in rocksdb mode
return;
}
auto ctx = nano::test::context::ledger_empty ();
}
// Ledger can be initialized and returns a basic query for an empty account
TEST (ledger, empty)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_read ();
nano::account account;
auto balance (ledger.account_balance (transaction, account));
ASSERT_TRUE (balance.is_zero ());
}
// Genesis account should have the max balance on empty initialization
TEST (ledger, genesis_balance)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
auto balance = ledger.account_balance (transaction, nano::dev::genesis->account ());
ASSERT_EQ (nano::dev::constants.genesis_amount, balance);
auto amount = ledger.amount (transaction, nano::dev::genesis->account ());
ASSERT_EQ (nano::dev::constants.genesis_amount, amount);
nano::account_info info;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis->account (), info));
ASSERT_EQ (1, ledger.cache.account_count);
// Frontier time should have been updated when genesis balance was added
ASSERT_GE (nano::seconds_since_epoch (), info.modified);
ASSERT_LT (nano::seconds_since_epoch () - info.modified, 10);
// Genesis block should be confirmed by default
nano::confirmation_height_info confirmation_height_info;
ASSERT_FALSE (store.confirmation_height.get (transaction, nano::dev::genesis->account (), confirmation_height_info));
ASSERT_EQ (confirmation_height_info.height, 1);
ASSERT_EQ (confirmation_height_info.frontier, nano::dev::genesis->hash ());
}
TEST (ledger, process_modifies_sideband)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (store.tx_begin_write (), *send1).code);
ASSERT_EQ (send1->sideband ().timestamp, store.block.get (store.tx_begin_read (), send1->hash ())->sideband ().timestamp);
}
// Create a send block and publish it.
TEST (ledger, process_send)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::keypair key2;
nano::block_builder builder;
auto send = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
nano::block_hash hash1 = send->hash ();
ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1.head));
ASSERT_EQ (1, info1.block_count);
// This was a valid block, it should progress.
auto return1 = ledger.process (transaction, *send);
ASSERT_EQ (nano::dev::genesis_key.pub, send->sideband ().account);
ASSERT_EQ (2, send->sideband ().height);
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.amount (transaction, hash1));
ASSERT_TRUE (store.frontier.get (transaction, info1.head).is_zero ());
ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, hash1));
ASSERT_EQ (nano::process_result::progress, return1.code);
ASSERT_EQ (nano::dev::genesis_key.pub, store.block.account_calculated (*send));
ASSERT_EQ (50, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_receivable (transaction, key2.pub));
nano::account_info info2;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info2));
ASSERT_EQ (2, info2.block_count);
auto latest6 = store.block.get (transaction, info2.head);
ASSERT_NE (nullptr, latest6);
auto latest7 = dynamic_cast<nano::send_block *> (latest6.get ());
ASSERT_NE (nullptr, latest7);
ASSERT_EQ (*send, *latest7);
// Create an open block opening an account accepting the send we just created
auto open = builder
.open ()
.source (hash1)
.representative (key2.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
nano::block_hash hash2 (open->hash ());
// This was a valid block, it should progress.
auto return2 = ledger.process (transaction, *open);
ASSERT_EQ (nano::process_result::progress, return2.code);
ASSERT_EQ (key2.pub, open->sideband ().account);
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, open->sideband ().balance.number ());
ASSERT_EQ (1, open->sideband ().height);
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.amount (transaction, hash2));
ASSERT_EQ (nano::process_result::progress, return2.code);
ASSERT_EQ (key2.pub, store.block.account_calculated (*open));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.amount (transaction, hash2));
ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash2));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_balance (transaction, key2.pub));
ASSERT_EQ (0, ledger.account_receivable (transaction, key2.pub));
ASSERT_EQ (50, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.weight (key2.pub));
nano::account_info info3;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info3));
auto latest2 = store.block.get (transaction, info3.head);
ASSERT_NE (nullptr, latest2);
auto latest3 = dynamic_cast<nano::send_block *> (latest2.get ());
ASSERT_NE (nullptr, latest3);
ASSERT_EQ (*send, *latest3);
nano::account_info info4;
ASSERT_FALSE (store.account.get (transaction, key2.pub, info4));
auto latest4 = store.block.get (transaction, info4.head);
ASSERT_NE (nullptr, latest4);
auto latest5 = dynamic_cast<nano::open_block *> (latest4.get ());
ASSERT_NE (nullptr, latest5);
ASSERT_EQ (*open, *latest5);
ASSERT_FALSE (ledger.rollback (transaction, hash2));
ASSERT_TRUE (store.frontier.get (transaction, hash2).is_zero ());
nano::account_info info5;
ASSERT_TRUE (ledger.store.account.get (transaction, key2.pub, info5));
nano::pending_info pending1;
ASSERT_FALSE (ledger.store.pending.get (transaction, nano::pending_key (key2.pub, hash1), pending1));
ASSERT_EQ (nano::dev::genesis_key.pub, pending1.source);
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, pending1.amount.number ());
ASSERT_EQ (0, ledger.account_balance (transaction, key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_receivable (transaction, key2.pub));
ASSERT_EQ (50, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (50, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.weight (key2.pub));
nano::account_info info6;
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis_key.pub, info6));
ASSERT_EQ (hash1, info6.head);
ASSERT_FALSE (ledger.rollback (transaction, info6.head));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1.head));
ASSERT_TRUE (store.frontier.get (transaction, hash1).is_zero ());
nano::account_info info7;
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis_key.pub, info7));
ASSERT_EQ (1, info7.block_count);
ASSERT_EQ (info1.head, info7.head);
nano::pending_info pending2;
ASSERT_TRUE (ledger.store.pending.get (transaction, nano::pending_key (key2.pub, hash1), pending2));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.account_receivable (transaction, key2.pub));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, process_receive)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::keypair key2;
nano::block_builder builder;
auto send = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
nano::block_hash hash1 (send->hash ());
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
nano::keypair key3;
auto open = builder
.open ()
.source (hash1)
.representative (key3.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
nano::block_hash hash2 (open->hash ());
auto return1 = ledger.process (transaction, *open);
ASSERT_EQ (nano::process_result::progress, return1.code);
ASSERT_EQ (key2.pub, store.block.account_calculated (*open));
ASSERT_EQ (key2.pub, open->sideband ().account);
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, open->sideband ().balance.number ());
ASSERT_EQ (1, open->sideband ().height);
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.amount (transaction, hash2));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.weight (key3.pub));
auto send2 = builder
.send ()
.previous (hash1)
.destination (key2.pub)
.balance (25)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (hash1))
.build ();
nano::block_hash hash3 = send2->hash ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
auto receive = builder
.receive ()
.previous (hash2)
.source (hash3)
.sign (key2.prv, key2.pub)
.work (*pool.generate (hash2))
.build ();
auto hash4 = receive->hash ();
ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash2));
auto return2 = ledger.process (transaction, *receive);
ASSERT_EQ (key2.pub, receive->sideband ().account);
ASSERT_EQ (nano::dev::constants.genesis_amount - 25, receive->sideband ().balance.number ());
ASSERT_EQ (2, receive->sideband ().height);
ASSERT_EQ (25, ledger.amount (transaction, hash4));
ASSERT_TRUE (store.frontier.get (transaction, hash2).is_zero ());
ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash4));
ASSERT_EQ (nano::process_result::progress, return2.code);
ASSERT_EQ (key2.pub, store.block.account_calculated (*receive));
ASSERT_EQ (hash4, ledger.latest (transaction, key2.pub));
ASSERT_EQ (25, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.account_receivable (transaction, key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 25, ledger.account_balance (transaction, key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 25, ledger.weight (key3.pub));
ASSERT_FALSE (ledger.rollback (transaction, hash4));
ASSERT_TRUE (store.block.successor (transaction, hash2).is_zero ());
ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash2));
ASSERT_TRUE (store.frontier.get (transaction, hash4).is_zero ());
ASSERT_EQ (25, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (25, ledger.account_receivable (transaction, key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_balance (transaction, key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.weight (key3.pub));
ASSERT_EQ (hash2, ledger.latest (transaction, key2.pub));
nano::pending_info pending1;
ASSERT_FALSE (ledger.store.pending.get (transaction, nano::pending_key (key2.pub, hash3), pending1));
ASSERT_EQ (nano::dev::genesis_key.pub, pending1.source);
ASSERT_EQ (25, pending1.amount.number ());
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, rollback_receiver)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::keypair key2;
nano::block_builder builder;
auto send = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
nano::block_hash hash1 (send->hash ());
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
nano::keypair key3;
auto open = builder
.open ()
.source (hash1)
.representative (key3.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
nano::block_hash hash2 (open->hash ());
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open).code);
ASSERT_EQ (hash2, ledger.latest (transaction, key2.pub));
ASSERT_EQ (50, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_balance (transaction, key2.pub));
ASSERT_EQ (50, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.weight (key3.pub));
ASSERT_FALSE (ledger.rollback (transaction, hash1));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.account_balance (transaction, key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (key3.pub));
nano::account_info info2;
ASSERT_TRUE (store.account.get (transaction, key2.pub, info2));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
nano::pending_info pending1;
ASSERT_TRUE (store.pending.get (transaction, nano::pending_key{ key2.pub, info2.head }, pending1));
}
TEST (ledger, rollback_representation)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key5;
nano::block_builder builder;
auto change1 = builder
.change ()
.previous (nano::dev::genesis->hash ())
.representative (key5.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change1).code);
nano::keypair key3;
auto change2 = builder
.change ()
.previous (change1->hash ())
.representative (key3.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (change1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change2).code);
nano::keypair key2;
auto send1 = builder
.send ()
.previous (change2->hash ())
.destination (key2.pub)
.balance (50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (change2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
nano::keypair key4;
auto open = builder
.open ()
.source (send1->hash ())
.representative (key4.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open).code);
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (key2.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
auto receive1 = builder
.receive ()
.previous (open->hash ())
.source (send2->hash ())
.sign (key2.prv, key2.pub)
.work (*pool.generate (open->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_EQ (1, ledger.weight (key3.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 1, ledger.weight (key4.pub));
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, key2.pub, info1));
ASSERT_EQ (key4.pub, info1.representative);
ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ()));
nano::account_info info2;
ASSERT_FALSE (store.account.get (transaction, key2.pub, info2));
ASSERT_EQ (key4.pub, info2.representative);
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.weight (key4.pub));
ASSERT_FALSE (ledger.rollback (transaction, open->hash ()));
ASSERT_EQ (1, ledger.weight (key3.pub));
ASSERT_EQ (0, ledger.weight (key4.pub));
ledger.rollback (transaction, send1->hash ());
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (key3.pub));
nano::account_info info3;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info3));
ASSERT_EQ (key3.pub, info3.representative);
ASSERT_FALSE (ledger.rollback (transaction, change2->hash ()));
nano::account_info info4;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info4));
ASSERT_EQ (key5.pub, info4.representative);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (key5.pub));
ASSERT_EQ (0, ledger.weight (key3.pub));
}
TEST (ledger, receive_rollback)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
auto receive = builder
.receive ()
.previous (send->hash ())
.source (send->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive).code);
ASSERT_FALSE (ledger.rollback (transaction, receive->hash ()));
}
TEST (ledger, process_duplicate)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::keypair key2;
nano::block_builder builder;
auto send = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
nano::block_hash hash1 (send->hash ());
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
ASSERT_EQ (nano::process_result::old, ledger.process (transaction, *send).code);
auto open = builder
.open ()
.source (hash1)
.representative (1)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open).code);
ASSERT_EQ (nano::process_result::old, ledger.process (transaction, *open).code);
}
TEST (ledger, representative_genesis)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
auto latest = ledger.latest (transaction, nano::dev::genesis_key.pub);
ASSERT_FALSE (latest.is_zero ());
ASSERT_EQ (nano::dev::genesis->hash (), ledger.representative (transaction, latest));
}
TEST (ledger, weight)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
}
TEST (ledger, representative_change)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::keypair key2;
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.weight (key2.pub));
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::block_builder builder;
auto block = builder
.change ()
.previous (info1.head)
.representative (key2.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1.head));
auto return1 (ledger.process (transaction, *block));
ASSERT_EQ (0, ledger.amount (transaction, block->hash ()));
ASSERT_TRUE (store.frontier.get (transaction, info1.head).is_zero ());
ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, block->hash ()));
ASSERT_EQ (nano::process_result::progress, return1.code);
ASSERT_EQ (nano::dev::genesis_key.pub, store.block.account_calculated (*block));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (key2.pub));
nano::account_info info2;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info2));
ASSERT_EQ (block->hash (), info2.head);
ASSERT_FALSE (ledger.rollback (transaction, info2.head));
ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1.head));
ASSERT_TRUE (store.frontier.get (transaction, block->hash ()).is_zero ());
nano::account_info info3;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info3));
ASSERT_EQ (info1.head, info3.head);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.weight (key2.pub));
}
TEST (ledger, send_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::keypair key2;
nano::keypair key3;
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::block_builder builder;
auto block = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block).code);
auto block2 = builder
.send ()
.previous (info1.head)
.destination (key3.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *block2).code);
}
TEST (ledger, receive_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::keypair key2;
nano::keypair key3;
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::block_builder builder;
auto block = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block).code);
auto block2 = builder
.open ()
.source (block->hash ())
.representative (key2.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
auto block3 = builder
.change ()
.previous (block2->hash ())
.representative (key3.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (block2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block3).code);
auto block4 = builder
.send ()
.previous (block->hash ())
.destination (key2.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block4).code);
auto block5 = builder
.receive ()
.previous (block2->hash ())
.source (block4->hash ())
.sign (key2.prv, key2.pub)
.work (*pool.generate (block2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *block5).code);
}
TEST (ledger, open_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::keypair key2;
nano::keypair key3;
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::block_builder builder;
auto block = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block).code);
auto block2 = builder
.open ()
.source (block->hash ())
.representative (key2.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
auto block3 = builder
.open ()
.source (block->hash ())
.representative (key3.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *block3).code);
}
TEST (ledger, representation_changes)
{
nano::keypair key1;
nano::rep_weights rep_weights;
ASSERT_EQ (0, rep_weights.representation_get (key1.pub));
rep_weights.representation_put (key1.pub, 1);
ASSERT_EQ (1, rep_weights.representation_get (key1.pub));
rep_weights.representation_put (key1.pub, 2);
ASSERT_EQ (2, rep_weights.representation_get (key1.pub));
}
TEST (ledger, representation)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto & rep_weights = ledger.cache.rep_weights;
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
ASSERT_EQ (nano::dev::constants.genesis_amount, rep_weights.representation_get (nano::dev::genesis_key.pub));
nano::keypair key2;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (nano::dev::constants.genesis_amount - 100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, rep_weights.representation_get (nano::dev::genesis_key.pub));
nano::keypair key3;
auto block2 = builder
.open ()
.source (block1->hash ())
.representative (key3.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (100, rep_weights.representation_get (key3.pub));
auto block3 = builder
.send ()
.previous (block1->hash ())
.destination (key2.pub)
.balance (nano::dev::constants.genesis_amount - 200)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block3).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (100, rep_weights.representation_get (key3.pub));
auto block4 = builder
.receive ()
.previous (block2->hash ())
.source (block3->hash ())
.sign (key2.prv, key2.pub)
.work (*pool.generate (block2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block4).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (200, rep_weights.representation_get (key3.pub));
nano::keypair key4;
auto block5 = builder
.change ()
.previous (block4->hash ())
.representative (key4.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (block4->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block5).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (0, rep_weights.representation_get (key3.pub));
ASSERT_EQ (200, rep_weights.representation_get (key4.pub));
nano::keypair key5;
auto block6 = builder
.send ()
.previous (block5->hash ())
.destination (key5.pub)
.balance (100)
.sign (key2.prv, key2.pub)
.work (*pool.generate (block5->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block6).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (0, rep_weights.representation_get (key3.pub));
ASSERT_EQ (100, rep_weights.representation_get (key4.pub));
ASSERT_EQ (0, rep_weights.representation_get (key5.pub));
nano::keypair key6;
auto block7 = builder
.open ()
.source (block6->hash ())
.representative (key6.pub)
.account (key5.pub)
.sign (key5.prv, key5.pub)
.work (*pool.generate (key5.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block7).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (0, rep_weights.representation_get (key3.pub));
ASSERT_EQ (100, rep_weights.representation_get (key4.pub));
ASSERT_EQ (0, rep_weights.representation_get (key5.pub));
ASSERT_EQ (100, rep_weights.representation_get (key6.pub));
auto block8 = builder
.send ()
.previous (block6->hash ())
.destination (key5.pub)
.balance (0)
.sign (key2.prv, key2.pub)
.work (*pool.generate (block6->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block8).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (0, rep_weights.representation_get (key3.pub));
ASSERT_EQ (0, rep_weights.representation_get (key4.pub));
ASSERT_EQ (0, rep_weights.representation_get (key5.pub));
ASSERT_EQ (100, rep_weights.representation_get (key6.pub));
auto block9 = builder
.receive ()
.previous (block7->hash ())
.source (block8->hash ())
.sign (key5.prv, key5.pub)
.work (*pool.generate (block7->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block9).code);
ASSERT_EQ (nano::dev::constants.genesis_amount - 200, rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, rep_weights.representation_get (key2.pub));
ASSERT_EQ (0, rep_weights.representation_get (key3.pub));
ASSERT_EQ (0, rep_weights.representation_get (key4.pub));
ASSERT_EQ (0, rep_weights.representation_get (key5.pub));
ASSERT_EQ (200, rep_weights.representation_get (key6.pub));
}
TEST (ledger, double_open)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key2;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (key2.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
auto open2 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis_key.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *open2).code);
}
TEST (ledger, double_receive)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key2;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (key2.pub)
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*pool.generate (key2.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
auto receive1 = builder
.receive ()
.previous (open1->hash ())
.source (send1->hash ())
.sign (key2.prv, key2.pub)
.work (*pool.generate (open1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *receive1).code);
}
TEST (votes, check_signature)
{
nano::test::system system;
nano::node_config node_config (nano::test::get_available_port (), system.logging);
node_config.online_weight_minimum = std::numeric_limits<nano::uint128_t>::max ();
auto & node1 = *system.add_node (node_config);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
{
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
}
node1.scheduler.activate (nano::dev::genesis_key.pub, node1.store.tx_begin_read ());
node1.scheduler.flush ();
auto election1 = node1.active.election (send1->qualified_root ());
ASSERT_EQ (1, election1->votes ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ send1->hash () }));
vote1->signature.bytes[0] ^= 1;
ASSERT_EQ (nano::vote_code::invalid, node1.vote_processor.vote_blocking (vote1, std::make_shared<nano::transport::inproc::channel> (node1, node1)));
vote1->signature.bytes[0] ^= 1;
ASSERT_EQ (nano::vote_code::vote, node1.vote_processor.vote_blocking (vote1, std::make_shared<nano::transport::inproc::channel> (node1, node1)));
ASSERT_EQ (nano::vote_code::replay, node1.vote_processor.vote_blocking (vote1, std::make_shared<nano::transport::inproc::channel> (node1, node1)));
}
TEST (votes, add_one)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
node1.block_confirm (send1);
node1.scheduler.flush ();
auto election1 = node1.active.election (send1->qualified_root ());
ASSERT_EQ (1, election1->votes ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ send1->hash () }));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
auto vote2 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 2, 0, std::vector<nano::block_hash>{ send1->hash () }));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
ASSERT_EQ (2, election1->votes ().size ());
auto votes1 (election1->votes ());
auto existing1 (votes1.find (nano::dev::genesis_key.pub));
ASSERT_NE (votes1.end (), existing1);
ASSERT_EQ (send1->hash (), existing1->second.hash);
nano::lock_guard<nano::mutex> guard (node1.active.mutex);
auto winner (*election1->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, winner.first);
}
namespace nano
{
// Higher timestamps change the vote
TEST (votes, add_existing)
{
nano::test::system system;
nano::node_config node_config (nano::test::get_available_port (), system.logging);
node_config.online_weight_minimum = nano::dev::constants.genesis_amount;
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 = *system.add_node (node_config);
nano::keypair key1;
nano::block_builder builder;
std::shared_ptr<nano::block> send1 = builder.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub) // No representative, blocks can't confirm
.balance (nano::dev::constants.genesis_amount / 2 - nano::Gxrb_ratio)
.link (key1.pub)
.work (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.build ();
node1.work_generate_blocking (*send1);
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (node1.store.tx_begin_write (), *send1).code);
node1.scheduler.activate (nano::dev::genesis_key.pub, node1.store.tx_begin_read ());
node1.scheduler.flush ();
auto election1 = node1.active.election (send1->qualified_root ());
auto vote1 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ send1->hash () }));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
// Block is already processed from vote
ASSERT_TRUE (node1.active.publish (send1));
ASSERT_EQ (nano::vote::timestamp_min * 1, election1->last_votes[nano::dev::genesis_key.pub].timestamp);
nano::keypair key2;
std::shared_ptr<nano::block> send2 = builder.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub) // No representative, blocks can't confirm
.balance (nano::dev::constants.genesis_amount / 2 - nano::Gxrb_ratio)
.link (key2.pub)
.work (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.build ();
node1.work_generate_blocking (*send2);
ASSERT_FALSE (node1.active.publish (send2));
ASSERT_TIMELY (5s, node1.active.active (*send2));
auto vote2 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 2, 0, std::vector<nano::block_hash>{ send2->hash () }));
// Pretend we've waited the timeout
nano::unique_lock<nano::mutex> lock (election1->mutex);
election1->last_votes[nano::dev::genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
lock.unlock ();
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
ASSERT_EQ (nano::vote::timestamp_min * 2, election1->last_votes[nano::dev::genesis_key.pub].timestamp);
// Also resend the old vote, and see if we respect the timestamp
lock.lock ();
election1->last_votes[nano::dev::genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
lock.unlock ();
ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1));
ASSERT_EQ (nano::vote::timestamp_min * 2, election1->votes ()[nano::dev::genesis_key.pub].timestamp);
auto votes (election1->votes ());
ASSERT_EQ (2, votes.size ());
ASSERT_NE (votes.end (), votes.find (nano::dev::genesis_key.pub));
ASSERT_EQ (send2->hash (), votes[nano::dev::genesis_key.pub].hash);
ASSERT_EQ (*send2, *election1->tally ().begin ()->second);
}
// Lower timestamps are ignored
TEST (votes, add_old)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
node1.block_confirm (send1);
node1.scheduler.flush ();
auto election1 = node1.active.election (send1->qualified_root ());
auto vote1 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 2, 0, std::vector<nano::block_hash>{ send1->hash () }));
auto channel (std::make_shared<nano::transport::inproc::channel> (node1, node1));
node1.vote_processor.vote_blocking (vote1, channel);
nano::keypair key2;
auto send2 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send2);
auto vote2 = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ send2->hash () });
{
nano::lock_guard<nano::mutex> lock (election1->mutex);
election1->last_votes[nano::dev::genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
}
node1.vote_processor.vote_blocking (vote2, channel);
ASSERT_EQ (2, election1->votes ().size ());
auto votes (election1->votes ());
ASSERT_NE (votes.end (), votes.find (nano::dev::genesis_key.pub));
ASSERT_EQ (send1->hash (), votes[nano::dev::genesis_key.pub].hash);
ASSERT_EQ (*send1, *election1->winner ());
}
}
// Lower timestamps are accepted for different accounts
// Test disabled because it's failing intermittently.
// PR in which it got disabled: https://github.com/nanocurrency/nano-node/pull/3629
// Issue for investigating it: https://github.com/nanocurrency/nano-node/issues/3631
TEST (votes, DISABLED_add_old_different_account)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send2);
ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code);
ASSERT_EQ (nano::process_result::progress, node1.process (*send2).code);
nano::test::blocks_confirm (node1, { send1, send2 });
auto election1 = node1.active.election (send1->qualified_root ());
ASSERT_NE (nullptr, election1);
auto election2 = node1.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election2);
ASSERT_EQ (1, election1->votes ().size ());
ASSERT_EQ (1, election2->votes ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 2, 0, std::vector<nano::block_hash>{ send1->hash () }));
auto channel (std::make_shared<nano::transport::inproc::channel> (node1, node1));
auto vote_result1 (node1.vote_processor.vote_blocking (vote1, channel));
ASSERT_EQ (nano::vote_code::vote, vote_result1);
ASSERT_EQ (2, election1->votes ().size ());
ASSERT_EQ (1, election2->votes ().size ());
auto vote2 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ send2->hash () }));
auto vote_result2 (node1.vote_processor.vote_blocking (vote2, channel));
ASSERT_EQ (nano::vote_code::vote, vote_result2);
ASSERT_EQ (2, election1->votes ().size ());
ASSERT_EQ (2, election2->votes ().size ());
auto votes1 (election1->votes ());
auto votes2 (election2->votes ());
ASSERT_NE (votes1.end (), votes1.find (nano::dev::genesis_key.pub));
ASSERT_NE (votes2.end (), votes2.find (nano::dev::genesis_key.pub));
ASSERT_EQ (send1->hash (), votes1[nano::dev::genesis_key.pub].hash);
ASSERT_EQ (send2->hash (), votes2[nano::dev::genesis_key.pub].hash);
ASSERT_EQ (*send1, *election1->winner ());
ASSERT_EQ (*send2, *election2->winner ());
}
// The voting cooldown is respected
TEST (votes, add_cooldown)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
node1.block_confirm (send1);
node1.scheduler.flush ();
auto election1 = node1.active.election (send1->qualified_root ());
auto vote1 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ send1->hash () }));
auto channel (std::make_shared<nano::transport::inproc::channel> (node1, node1));
node1.vote_processor.vote_blocking (vote1, channel);
nano::keypair key2;
auto send2 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send2);
auto vote2 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_min * 2, 0, std::vector<nano::block_hash>{ send2->hash () }));
node1.vote_processor.vote_blocking (vote2, channel);
ASSERT_EQ (2, election1->votes ().size ());
auto votes (election1->votes ());
ASSERT_NE (votes.end (), votes.find (nano::dev::genesis_key.pub));
ASSERT_EQ (send1->hash (), votes[nano::dev::genesis_key.pub].hash);
ASSERT_EQ (*send1, *election1->winner ());
}
// Query for block successor
TEST (ledger, successor)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build ();
node1.work_generate_blocking (*send1);
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
ASSERT_EQ (*send1, *node1.ledger.successor (transaction, nano::qualified_root (nano::root (0), nano::dev::genesis->hash ())));
ASSERT_EQ (*nano::dev::genesis, *node1.ledger.successor (transaction, nano::dev::genesis->qualified_root ()));
ASSERT_EQ (nullptr, node1.ledger.successor (transaction, nano::qualified_root (0)));
}
TEST (ledger, fail_change_old)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block = builder
.change ()
.previous (nano::dev::genesis->hash ())
.representative (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto result2 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::old, result2.code);
}
TEST (ledger, fail_change_gap_previous)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block = builder
.change ()
.previous (1)
.representative (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::root (1)))
.build ();
auto result1 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::gap_previous, result1.code);
}
TEST (ledger, fail_change_bad_signature)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block = builder
.change ()
.previous (nano::dev::genesis->hash ())
.representative (key1.pub)
.sign (nano::keypair ().prv, 0)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::bad_signature, result1.code);
}
TEST (ledger, fail_change_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.change ()
.previous (nano::dev::genesis->hash ())
.representative (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
nano::keypair key2;
auto block2 = builder
.change ()
.previous (nano::dev::genesis->hash ())
.representative (key2.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::fork, result2.code);
}
TEST (ledger, fail_send_old)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto result2 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::old, result2.code);
}
TEST (ledger, fail_send_gap_previous)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block = builder
.send ()
.previous (1)
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::root (1)))
.build ();
auto result1 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::gap_previous, result1.code);
}
TEST (ledger, fail_send_bad_signature)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::keypair ().prv, 0)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block);
ASSERT_EQ (nano::process_result::bad_signature, result1.code);
}
TEST (ledger, fail_send_negative_spend)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
nano::keypair key2;
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key2.pub)
.balance (2)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::negative_spend, ledger.process (transaction, *block2).code);
}
TEST (ledger, fail_send_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
nano::keypair key2;
auto block2 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *block2).code);
}
TEST (ledger, fail_open_old)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
auto block2 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
ASSERT_EQ (nano::process_result::old, ledger.process (transaction, *block2).code);
}
TEST (ledger, fail_open_gap_source)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block2 = builder
.open ()
.source (1)
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::gap_source, result2.code);
}
TEST (ledger, fail_open_bad_signature)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
auto block2 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
block2->signature.clear ();
ASSERT_EQ (nano::process_result::bad_signature, ledger.process (transaction, *block2).code);
}
TEST (ledger, fail_open_fork_previous)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block3).code);
auto block4 = builder
.open ()
.source (block2->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *block4).code);
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, fail_open_account_mismatch)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
nano::keypair badkey;
auto block2 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (badkey.pub)
.sign (badkey.prv, badkey.pub)
.work (*pool.generate (badkey.pub))
.build ();
ASSERT_NE (nano::process_result::progress, ledger.process (transaction, *block2).code);
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, fail_receive_old)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block3).code);
auto block4 = builder
.receive ()
.previous (block3->hash ())
.source (block2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (block3->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block4).code);
ASSERT_EQ (nano::process_result::old, ledger.process (transaction, *block4).code);
}
TEST (ledger, fail_receive_gap_source)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result2.code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result3 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::progress, result3.code);
auto block4 = builder
.receive ()
.previous (block3->hash ())
.source (1)
.sign (key1.prv, key1.pub)
.work (*pool.generate (block3->hash ()))
.build ();
auto result4 = ledger.process (transaction, *block4);
ASSERT_EQ (nano::process_result::gap_source, result4.code);
}
TEST (ledger, fail_receive_overreceive)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result3 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result3.code);
auto block3 = builder
.receive ()
.previous (block2->hash ())
.source (block1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (block2->hash ()))
.build ();
auto result4 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::unreceivable, result4.code);
}
TEST (ledger, fail_receive_bad_signature)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result2.code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result3 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::progress, result3.code);
auto block4 = builder
.receive ()
.previous (block3->hash ())
.source (block2->hash ())
.sign (nano::keypair ().prv, 0)
.work (*pool.generate (block3->hash ()))
.build ();
auto result4 = ledger.process (transaction, *block4);
ASSERT_EQ (nano::process_result::bad_signature, result4.code);
}
TEST (ledger, fail_receive_gap_previous_opened)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result2.code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result3 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::progress, result3.code);
auto block4 = builder
.receive ()
.previous (1)
.source (block2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (nano::root (1)))
.build ();
auto result4 = ledger.process (transaction, *block4);
ASSERT_EQ (nano::process_result::gap_previous, result4.code);
}
TEST (ledger, fail_receive_gap_previous_unopened)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result2.code);
auto block3 = builder
.receive ()
.previous (1)
.source (block2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (nano::root (1)))
.build ();
auto result3 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::gap_previous, result3.code);
}
TEST (ledger, fail_receive_fork_previous)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result2.code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result3 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::progress, result3.code);
nano::keypair key2;
auto block4 = builder
.send ()
.previous (block3->hash ())
.destination (key1.pub)
.balance (1)
.sign (key1.prv, key1.pub)
.work (*pool.generate (block3->hash ()))
.build ();
auto result4 = ledger.process (transaction, *block4);
ASSERT_EQ (nano::process_result::progress, result4.code);
auto block5 = builder
.receive ()
.previous (block3->hash ())
.source (block2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (block3->hash ()))
.build ();
auto result5 = ledger.process (transaction, *block5);
ASSERT_EQ (nano::process_result::fork, result5.code);
}
TEST (ledger, fail_receive_received_source)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (2)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto result1 = ledger.process (transaction, *block1);
ASSERT_EQ (nano::process_result::progress, result1.code);
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (key1.pub)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
auto result2 = ledger.process (transaction, *block2);
ASSERT_EQ (nano::process_result::progress, result2.code);
auto block6 = builder
.send ()
.previous (block2->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block2->hash ()))
.build ();
auto result6 = ledger.process (transaction, *block6);
ASSERT_EQ (nano::process_result::progress, result6.code);
auto block3 = builder
.open ()
.source (block1->hash ())
.representative (1)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto result3 = ledger.process (transaction, *block3);
ASSERT_EQ (nano::process_result::progress, result3.code);
nano::keypair key2;
auto block4 = builder
.send ()
.previous (block3->hash ())
.destination (key1.pub)
.balance (1)
.sign (key1.prv, key1.pub)
.work (*pool.generate (block3->hash ()))
.build ();
auto result4 = ledger.process (transaction, *block4);
ASSERT_EQ (nano::process_result::progress, result4.code);
auto block5 = builder
.receive ()
.previous (block4->hash ())
.source (block2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (block4->hash ()))
.build ();
auto result5 = ledger.process (transaction, *block5);
ASSERT_EQ (nano::process_result::progress, result5.code);
auto block7 = builder
.receive ()
.previous (block3->hash ())
.source (block2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (block3->hash ()))
.build ();
auto result7 = ledger.process (transaction, *block7);
ASSERT_EQ (nano::process_result::fork, result7.code);
}
TEST (ledger, latest_empty)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::keypair key;
auto transaction = store.tx_begin_read ();
auto latest = ledger.latest (transaction, key.pub);
ASSERT_TRUE (latest.is_zero ());
}
TEST (ledger, latest_root)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key;
ASSERT_EQ (key.pub, ledger.latest_root (transaction, key.pub));
auto hash1 = ledger.latest (transaction, nano::dev::genesis_key.pub);
nano::block_builder builder;
auto send = builder
.send ()
.previous (hash1)
.destination (0)
.balance (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (hash1))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
ASSERT_EQ (send->hash (), ledger.latest_root (transaction, nano::dev::genesis_key.pub));
}
TEST (ledger, change_representative_move_representation)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::keypair key1;
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub));
nano::block_builder builder;
auto send = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
ASSERT_EQ (0, ledger.weight (nano::dev::genesis_key.pub));
nano::keypair key2;
auto change = builder
.change ()
.previous (send->hash ())
.representative (key2.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change).code);
nano::keypair key3;
auto open = builder
.open ()
.source (send->hash ())
.representative (key3.pub)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open).code);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (key3.pub));
}
TEST (ledger, send_open_receive_rollback)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info info1;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (info1.head)
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
auto return1 = ledger.process (transaction, *send1);
ASSERT_EQ (nano::process_result::progress, return1.code);
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
auto return2 = ledger.process (transaction, *send2);
ASSERT_EQ (nano::process_result::progress, return2.code);
nano::keypair key2;
auto open = builder
.open ()
.source (send2->hash ())
.representative (key2.pub)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto return4 = ledger.process (transaction, *open);
ASSERT_EQ (nano::process_result::progress, return4.code);
auto receive = builder
.receive ()
.previous (open->hash ())
.source (send1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (open->hash ()))
.build ();
auto return5 = ledger.process (transaction, *receive);
ASSERT_EQ (nano::process_result::progress, return5.code);
nano::keypair key3;
ASSERT_EQ (100, ledger.weight (key2.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (0, ledger.weight (key3.pub));
auto change1 = builder
.change ()
.previous (send2->hash ())
.representative (key3.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send2->hash ()))
.build ();
auto return6 = ledger.process (transaction, *change1);
ASSERT_EQ (nano::process_result::progress, return6.code);
ASSERT_EQ (100, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, ledger.weight (key3.pub));
ASSERT_FALSE (ledger.rollback (transaction, receive->hash ()));
ASSERT_EQ (50, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, ledger.weight (key3.pub));
ASSERT_FALSE (ledger.rollback (transaction, open->hash ()));
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, ledger.weight (key3.pub));
ASSERT_FALSE (ledger.rollback (transaction, change1->hash ()));
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (key3.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_FALSE (ledger.rollback (transaction, send2->hash ()));
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (key3.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.weight (nano::dev::genesis_key.pub));
ASSERT_FALSE (ledger.rollback (transaction, send1->hash ()));
ASSERT_EQ (0, ledger.weight (key2.pub));
ASSERT_EQ (0, ledger.weight (key3.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - 0, ledger.weight (nano::dev::genesis_key.pub));
}
TEST (ledger, bootstrap_rep_weight)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::account_info info1;
nano::keypair key2;
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
{
auto transaction = store.tx_begin_write ();
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::block_builder builder;
auto send = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (std::numeric_limits<nano::uint128_t>::max () - 50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
}
ASSERT_EQ (2, ledger.cache.block_count);
{
ledger.bootstrap_weight_max_blocks = 3;
ledger.bootstrap_weights[key2.pub] = 1000;
ASSERT_EQ (1000, ledger.weight (key2.pub));
}
{
auto transaction = store.tx_begin_write ();
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, info1));
nano::block_builder builder;
auto send = builder
.send ()
.previous (info1.head)
.destination (key2.pub)
.balance (std::numeric_limits<nano::uint128_t>::max () - 100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (info1.head))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
}
ASSERT_EQ (3, ledger.cache.block_count);
ASSERT_EQ (0, ledger.weight (key2.pub));
}
TEST (ledger, block_destination_source)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair dest;
nano::uint128_t balance (nano::dev::constants.genesis_amount);
balance -= nano::Gxrb_ratio;
nano::block_builder builder;
auto block1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (dest.pub)
.balance (balance)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
balance -= nano::Gxrb_ratio;
auto block2 = builder
.send ()
.previous (block1->hash ())
.destination (nano::dev::genesis->account ())
.balance (balance)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block1->hash ()))
.build ();
balance += nano::Gxrb_ratio;
auto block3 = builder
.receive ()
.previous (block2->hash ())
.source (block2->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block2->hash ()))
.build ();
balance -= nano::Gxrb_ratio;
auto block4 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (block3->hash ())
.representative (nano::dev::genesis->account ())
.balance (balance)
.link (dest.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block3->hash ()))
.build ();
balance -= nano::Gxrb_ratio;
auto block5 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (block4->hash ())
.representative (nano::dev::genesis->account ())
.balance (balance)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block4->hash ()))
.build ();
balance += nano::Gxrb_ratio;
auto block6 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (block5->hash ())
.representative (nano::dev::genesis->account ())
.balance (balance)
.link (block5->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (block5->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block1).code);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block2).code);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block3).code);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block4).code);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block5).code);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *block6).code);
ASSERT_EQ (balance, ledger.balance (transaction, block6->hash ()));
ASSERT_EQ (dest.pub, ledger.block_destination (transaction, *block1));
ASSERT_TRUE (ledger.block_source (transaction, *block1).is_zero ());
ASSERT_EQ (nano::dev::genesis->account (), ledger.block_destination (transaction, *block2));
ASSERT_TRUE (ledger.block_source (transaction, *block2).is_zero ());
ASSERT_TRUE (ledger.block_destination (transaction, *block3) == nullptr);
ASSERT_EQ (block2->hash (), ledger.block_source (transaction, *block3));
ASSERT_EQ (dest.pub, ledger.block_destination (transaction, *block4));
ASSERT_TRUE (ledger.block_source (transaction, *block4).is_zero ());
ASSERT_EQ (nano::dev::genesis->account (), ledger.block_destination (transaction, *block5));
ASSERT_TRUE (ledger.block_source (transaction, *block5).is_zero ());
ASSERT_TRUE (ledger.block_destination (transaction, *block6) == nullptr);
ASSERT_EQ (block5->hash (), ledger.block_source (transaction, *block6));
}
TEST (ledger, state_account)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_EQ (nano::dev::genesis->account (), ledger.account (transaction, send1->hash ()));
}
TEST (ledger, state_send_receive)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
ASSERT_TRUE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (2, send2->sideband ().height);
ASSERT_TRUE (send2->sideband ().details.is_send);
ASSERT_FALSE (send2->sideband ().details.is_receive);
ASSERT_FALSE (send2->sideband ().details.is_epoch);
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_TRUE (store.block.exists (transaction, receive1->hash ()));
auto receive2 = store.block.get (transaction, receive1->hash ());
ASSERT_NE (nullptr, receive2);
ASSERT_EQ (*receive1, *receive2);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.balance (transaction, receive1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
ASSERT_EQ (3, receive2->sideband ().height);
ASSERT_FALSE (receive2->sideband ().details.is_send);
ASSERT_TRUE (receive2->sideband ().details.is_receive);
ASSERT_FALSE (receive2->sideband ().details.is_epoch);
}
TEST (ledger, state_receive)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_TRUE (store.block.exists (transaction, receive1->hash ()));
auto receive2 = store.block.get (transaction, receive1->hash ());
ASSERT_NE (nullptr, receive2);
ASSERT_EQ (*receive1, *receive2);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.balance (transaction, receive1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (3, receive2->sideband ().height);
ASSERT_FALSE (receive2->sideband ().details.is_send);
ASSERT_TRUE (receive2->sideband ().details.is_receive);
ASSERT_FALSE (receive2->sideband ().details.is_epoch);
}
TEST (ledger, state_rep_change)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair rep;
nano::block_builder builder;
auto change1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount)
.link (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change1).code);
ASSERT_TRUE (store.block.exists (transaction, change1->hash ()));
auto change2 = store.block.get (transaction, change1->hash ());
ASSERT_NE (nullptr, change2);
ASSERT_EQ (*change1, *change2);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.balance (transaction, change1->hash ()));
ASSERT_EQ (0, ledger.amount (transaction, change1->hash ()));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (rep.pub));
ASSERT_EQ (2, change2->sideband ().height);
ASSERT_FALSE (change2->sideband ().details.is_send);
ASSERT_FALSE (change2->sideband ().details.is_receive);
ASSERT_FALSE (change2->sideband ().details.is_epoch);
}
TEST (ledger, state_open)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
ASSERT_TRUE (store.pending.exists (transaction, nano::pending_key (destination.pub, send1->hash ())));
auto open1 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (destination.pub, send1->hash ())));
ASSERT_TRUE (store.block.exists (transaction, open1->hash ()));
auto open2 = store.block.get (transaction, open1->hash ());
ASSERT_NE (nullptr, open2);
ASSERT_EQ (*open1, *open2);
ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, open1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, open1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (ledger.cache.account_count, store.account.count (transaction));
ASSERT_EQ (1, open2->sideband ().height);
ASSERT_FALSE (open2->sideband ().details.is_send);
ASSERT_TRUE (open2->sideband ().details.is_receive);
ASSERT_FALSE (open2->sideband ().details.is_epoch);
}
// Make sure old block types can't be inserted after a state block.
TEST (ledger, send_after_state_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - (2 * nano::Gxrb_ratio))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *send2).code);
}
// Make sure old block types can't be inserted after a state block.
TEST (ledger, receive_after_state_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto receive1 = builder
.receive ()
.previous (send1->hash ())
.source (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *receive1).code);
}
// Make sure old block types can't be inserted after a state block.
TEST (ledger, change_after_state_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
nano::keypair rep;
auto change1 = builder
.change ()
.previous (send1->hash ())
.representative (rep.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *change1).code);
}
TEST (ledger, state_unreceivable_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::gap_source, ledger.process (transaction, *receive1).code);
}
TEST (ledger, state_receive_bad_amount_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::balance_mismatch, ledger.process (transaction, *receive1).code);
}
TEST (ledger, state_no_link_amount_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
nano::keypair rep;
auto change1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount)
.link (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::balance_mismatch, ledger.process (transaction, *change1).code);
}
TEST (ledger, state_receive_wrong_account_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
nano::keypair key;
auto receive1 = builder
.state ()
.account (key.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (key.prv, key.pub)
.work (*pool.generate (key.pub))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *receive1).code);
}
TEST (ledger, state_open_state_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
auto open2 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *open2).code);
ASSERT_EQ (open1->root (), open2->root ());
}
TEST (ledger, state_state_open_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
auto open2 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *open2).code);
ASSERT_EQ (open1->root (), open2->root ());
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, state_open_previous_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.state ()
.account (destination.pub)
.previous (1)
.representative (nano::dev::genesis->account ())
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (1))
.build ();
ASSERT_EQ (nano::process_result::gap_previous, ledger.process (transaction, *open1).code);
}
TEST (ledger, state_open_source_fail)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (0)
.link (0)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::gap_source, ledger.process (transaction, *open1).code);
}
TEST (ledger, state_send_change)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair rep;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (rep.pub));
ASSERT_EQ (2, send2->sideband ().height);
ASSERT_TRUE (send2->sideband ().details.is_send);
ASSERT_FALSE (send2->sideband ().details.is_receive);
ASSERT_FALSE (send2->sideband ().details.is_epoch);
}
TEST (ledger, state_receive_change)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
nano::keypair rep;
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_TRUE (store.block.exists (transaction, receive1->hash ()));
auto receive2 = store.block.get (transaction, receive1->hash ());
ASSERT_NE (nullptr, receive2);
ASSERT_EQ (*receive1, *receive2);
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.balance (transaction, receive1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ()));
ASSERT_EQ (0, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (rep.pub));
ASSERT_EQ (3, receive2->sideband ().height);
ASSERT_FALSE (receive2->sideband ().details.is_send);
ASSERT_TRUE (receive2->sideband ().details.is_receive);
ASSERT_FALSE (receive2->sideband ().details.is_epoch);
}
TEST (ledger, state_open_old)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, open1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, open1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
}
TEST (ledger, state_receive_old)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - (2 * nano::Gxrb_ratio))
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
auto receive1 = builder
.receive ()
.previous (open1->hash ())
.source (send2->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (open1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_EQ (2 * nano::Gxrb_ratio, ledger.balance (transaction, receive1->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
}
TEST (ledger, state_rollback_send)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store.block.exists (transaction, send1->hash ()));
auto send2 = store.block.get (transaction, send1->hash ());
ASSERT_NE (nullptr, send2);
ASSERT_EQ (*send1, *send2);
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
nano::pending_info info;
ASSERT_FALSE (store.pending.get (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ()), info));
ASSERT_EQ (nano::dev::genesis->account (), info.source);
ASSERT_EQ (nano::Gxrb_ratio, info.amount.number ());
ASSERT_FALSE (ledger.rollback (transaction, send1->hash ()));
ASSERT_FALSE (store.block.exists (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_TRUE (store.block.successor (transaction, nano::dev::genesis->hash ()).is_zero ());
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, state_rollback_receive)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), receive1->hash ())));
ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ()));
nano::pending_info info;
ASSERT_FALSE (store.pending.get (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ()), info));
ASSERT_EQ (nano::dev::genesis->account (), info.source);
ASSERT_EQ (nano::Gxrb_ratio, info.amount.number ());
ASSERT_FALSE (store.block.exists (transaction, receive1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, state_rollback_received_send)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (key.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto receive1 = builder
.state ()
.account (key.pub)
.previous (0)
.representative (key.pub)
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (key.prv, key.pub)
.work (*pool.generate (key.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), receive1->hash ())));
ASSERT_FALSE (ledger.rollback (transaction, send1->hash ()));
ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_FALSE (store.block.exists (transaction, send1->hash ()));
ASSERT_FALSE (store.block.exists (transaction, receive1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (0, ledger.account_balance (transaction, key.pub));
ASSERT_EQ (0, ledger.weight (key.pub));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, state_rep_change_rollback)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair rep;
nano::block_builder builder;
auto change1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount)
.link (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change1).code);
ASSERT_FALSE (ledger.rollback (transaction, change1->hash ()));
ASSERT_FALSE (store.block.exists (transaction, change1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (0, ledger.weight (rep.pub));
}
TEST (ledger, state_open_rollback)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto open1 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_FALSE (ledger.rollback (transaction, open1->hash ()));
ASSERT_FALSE (store.block.exists (transaction, open1->hash ()));
ASSERT_EQ (0, ledger.account_balance (transaction, destination.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
nano::pending_info info;
ASSERT_FALSE (store.pending.get (transaction, nano::pending_key (destination.pub, send1->hash ()), info));
ASSERT_EQ (nano::dev::genesis->account (), info.source);
ASSERT_EQ (nano::Gxrb_ratio, info.amount.number ());
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, state_send_change_rollback)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair rep;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_FALSE (ledger.rollback (transaction, send1->hash ()));
ASSERT_FALSE (store.block.exists (transaction, send1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (0, ledger.weight (rep.pub));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, state_receive_change_rollback)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
nano::keypair rep;
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (rep.pub)
.balance (nano::dev::constants.genesis_amount)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ()));
ASSERT_FALSE (store.block.exists (transaction, receive1->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis->account ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (0, ledger.weight (rep.pub));
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, epoch_blocks_v1_general)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto epoch1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
ASSERT_FALSE (epoch1->sideband ().details.is_send);
ASSERT_FALSE (epoch1->sideband ().details.is_receive);
ASSERT_TRUE (epoch1->sideband ().details.is_epoch);
ASSERT_EQ (nano::epoch::epoch_1, epoch1->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch1->sideband ().source_epoch); // Not used for epoch blocks
auto epoch2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *epoch2).code);
nano::account_info genesis_info;
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis->account (), genesis_info));
ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_1);
ASSERT_FALSE (ledger.rollback (transaction, epoch1->hash ()));
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis->account (), genesis_info));
ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_0);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis->account (), genesis_info));
ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_1);
ASSERT_FALSE (epoch1->sideband ().details.is_send);
ASSERT_FALSE (epoch1->sideband ().details.is_receive);
ASSERT_TRUE (epoch1->sideband ().details.is_epoch);
ASSERT_EQ (nano::epoch::epoch_1, epoch1->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch1->sideband ().source_epoch); // Not used for epoch blocks
auto change1 = builder
.change ()
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *change1).code);
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (send1->sideband ().details.is_send);
ASSERT_FALSE (send1->sideband ().details.is_receive);
ASSERT_FALSE (send1->sideband ().details.is_epoch);
ASSERT_EQ (nano::epoch::epoch_1, send1->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, send1->sideband ().source_epoch); // Not used for send blocks
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *open1).code);
auto epoch3 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (0)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::representative_mismatch, ledger.process (transaction, *epoch3).code);
auto epoch4 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (0)
.balance (0)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch4).code);
ASSERT_FALSE (epoch4->sideband ().details.is_send);
ASSERT_FALSE (epoch4->sideband ().details.is_receive);
ASSERT_TRUE (epoch4->sideband ().details.is_epoch);
ASSERT_EQ (nano::epoch::epoch_1, epoch4->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch4->sideband ().source_epoch); // Not used for epoch blocks
auto receive1 = builder
.receive ()
.previous (epoch4->hash ())
.source (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (epoch4->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *receive1).code);
auto receive2 = builder
.state ()
.account (destination.pub)
.previous (epoch4->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (epoch4->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive2).code);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().source_epoch);
ASSERT_EQ (0, ledger.balance (transaction, epoch4->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, receive2->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive2->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.weight (destination.pub));
ASSERT_FALSE (receive2->sideband ().details.is_send);
ASSERT_TRUE (receive2->sideband ().details.is_receive);
ASSERT_FALSE (receive2->sideband ().details.is_epoch);
}
TEST (ledger, epoch_blocks_v2_general)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto epoch1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
// Trying to upgrade from epoch 0 to epoch 2. It is a requirement epoch upgrades are sequential unless the account is unopened
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *epoch1).code);
// Set it to the first epoch and it should now succeed
epoch1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (epoch1->work)
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
ASSERT_EQ (nano::epoch::epoch_1, epoch1->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch1->sideband ().source_epoch); // Not used for epoch blocks
auto epoch2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch2).code);
ASSERT_EQ (nano::epoch::epoch_2, epoch2->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch2->sideband ().source_epoch); // Not used for epoch blocks
auto epoch3 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch2->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *epoch3).code);
nano::account_info genesis_info;
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis->account (), genesis_info));
ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_2);
ASSERT_FALSE (ledger.rollback (transaction, epoch1->hash ()));
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis->account (), genesis_info));
ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_0);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
ASSERT_FALSE (ledger.store.account.get (transaction, nano::dev::genesis->account (), genesis_info));
ASSERT_EQ (genesis_info.epoch (), nano::epoch::epoch_1);
auto change1 = builder
.change ()
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *change1).code);
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_EQ (nano::epoch::epoch_1, send1->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, send1->sideband ().source_epoch); // Not used for send blocks
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *open1).code);
auto epoch4 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (0)
.balance (0)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch4).code);
ASSERT_EQ (nano::epoch::epoch_1, epoch4->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch4->sideband ().source_epoch); // Not used for epoch blocks
auto epoch5 = builder
.state ()
.account (destination.pub)
.previous (epoch4->hash ())
.representative (nano::dev::genesis->account ())
.balance (0)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch4->hash ()))
.build ();
ASSERT_EQ (nano::process_result::representative_mismatch, ledger.process (transaction, *epoch5).code);
auto epoch6 = builder
.state ()
.account (destination.pub)
.previous (epoch4->hash ())
.representative (0)
.balance (0)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch4->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch6).code);
ASSERT_EQ (nano::epoch::epoch_2, epoch6->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch6->sideband ().source_epoch); // Not used for epoch blocks
auto receive1 = builder
.receive ()
.previous (epoch6->hash ())
.source (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (epoch6->hash ()))
.build ();
ASSERT_EQ (nano::process_result::block_position, ledger.process (transaction, *receive1).code);
auto receive2 = builder
.state ()
.account (destination.pub)
.previous (epoch6->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (epoch6->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive2).code);
ASSERT_EQ (nano::epoch::epoch_2, receive2->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().source_epoch);
ASSERT_EQ (0, ledger.balance (transaction, epoch6->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, receive2->hash ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive2->hash ()));
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis->account ()));
ASSERT_EQ (nano::Gxrb_ratio, ledger.weight (destination.pub));
}
TEST (ledger, epoch_blocks_receive_upgrade)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto epoch1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_EQ (nano::epoch::epoch_1, send2->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, send2->sideband ().source_epoch); // Not used for send blocks
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (destination.pub)
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (destination.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_EQ (nano::epoch::epoch_0, open1->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, open1->sideband ().source_epoch);
auto receive1 = builder
.receive ()
.previous (open1->hash ())
.source (send2->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (open1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *receive1).code);
auto receive2 = builder
.state ()
.account (destination.pub)
.previous (open1->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio * 2)
.link (send2->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (open1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive2).code);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().source_epoch);
nano::account_info destination_info;
ASSERT_FALSE (ledger.store.account.get (transaction, destination.pub, destination_info));
ASSERT_EQ (destination_info.epoch (), nano::epoch::epoch_1);
ASSERT_FALSE (ledger.rollback (transaction, receive2->hash ()));
ASSERT_FALSE (ledger.store.account.get (transaction, destination.pub, destination_info));
ASSERT_EQ (destination_info.epoch (), nano::epoch::epoch_0);
nano::pending_info pending_send2;
ASSERT_FALSE (ledger.store.pending.get (transaction, nano::pending_key (destination.pub, send2->hash ()), pending_send2));
ASSERT_EQ (nano::dev::genesis_key.pub, pending_send2.source);
ASSERT_EQ (nano::Gxrb_ratio, pending_send2.amount.number ());
ASSERT_EQ (nano::epoch::epoch_1, pending_send2.epoch);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive2).code);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_1, receive2->sideband ().source_epoch);
ASSERT_FALSE (ledger.store.account.get (transaction, destination.pub, destination_info));
ASSERT_EQ (destination_info.epoch (), nano::epoch::epoch_1);
nano::keypair destination2;
auto send3 = builder
.state ()
.account (destination.pub)
.previous (receive2->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (destination2.pub)
.sign (destination.prv, destination.pub)
.work (*pool.generate (receive2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send3).code);
auto open2 = builder
.open ()
.source (send3->hash ())
.representative (destination2.pub)
.account (destination2.pub)
.sign (destination2.prv, destination2.pub)
.work (*pool.generate (destination2.pub))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *open2).code);
// Upgrade to epoch 2 and send to destination. Try to create an open block from an epoch 2 source block.
nano::keypair destination3;
auto epoch2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send2->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch2).code);
auto send4 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch2->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3)
.link (destination3.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send4).code);
auto open3 = builder
.open ()
.source (send4->hash ())
.representative (destination3.pub)
.account (destination3.pub)
.sign (destination3.prv, destination3.pub)
.work (*pool.generate (destination3.pub))
.build ();
ASSERT_EQ (nano::process_result::unreceivable, ledger.process (transaction, *open3).code);
// Send it to an epoch 1 account
auto send5 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send4->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 4)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send4->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send5).code);
ASSERT_FALSE (ledger.store.account.get (transaction, destination.pub, destination_info));
ASSERT_EQ (destination_info.epoch (), nano::epoch::epoch_1);
auto receive3 = builder
.state ()
.account (destination.pub)
.previous (send3->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio * 2)
.link (send5->hash ())
.sign (destination.prv, destination.pub)
.work (*pool.generate (send3->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive3).code);
ASSERT_EQ (nano::epoch::epoch_2, receive3->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_2, receive3->sideband ().source_epoch);
ASSERT_FALSE (ledger.store.account.get (transaction, destination.pub, destination_info));
ASSERT_EQ (destination_info.epoch (), nano::epoch::epoch_2);
// Upgrade an unopened account straight to epoch 2
nano::keypair destination4;
auto send6 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send5->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 5)
.link (destination4.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send5->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send6).code);
auto epoch4 = builder
.state ()
.account (destination4.pub)
.previous (0)
.representative (0)
.balance (0)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (destination4.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch4).code);
ASSERT_EQ (nano::epoch::epoch_2, epoch4->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch4->sideband ().source_epoch); // Not used for epoch blocks
ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count);
}
TEST (ledger, epoch_blocks_fork)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::account{})
.balance (nano::dev::constants.genesis_amount)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto epoch1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *epoch1).code);
auto epoch2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *epoch2).code);
auto epoch3 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch3).code);
ASSERT_EQ (nano::epoch::epoch_1, epoch3->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch3->sideband ().source_epoch); // Not used for epoch state blocks
auto epoch4 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_2))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::fork, ledger.process (transaction, *epoch2).code);
}
TEST (ledger, successor_epoch)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto open = builder
.state ()
.account (key1.pub)
.previous (0)
.representative (key1.pub)
.balance (1)
.link (send1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto change = builder
.state ()
.account (key1.pub)
.previous (open->hash ())
.representative (key1.pub)
.balance (1)
.link (0)
.sign (key1.prv, key1.pub)
.work (*pool.generate (open->hash ()))
.build ();
auto open_hash = open->hash ();
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (reinterpret_cast<nano::account const &> (open_hash))
.balance (nano::dev::constants.genesis_amount - 2)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
auto epoch_open = builder
.state ()
.account (reinterpret_cast<nano::account const &> (open_hash))
.previous (0)
.representative (0)
.balance (0)
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (open->hash ()))
.build ();
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *open).code);
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *change).code);
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send2).code);
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *epoch_open).code);
ASSERT_EQ (*change, *node1.ledger.successor (transaction, change->qualified_root ()));
ASSERT_EQ (*epoch_open, *node1.ledger.successor (transaction, epoch_open->qualified_root ()));
ASSERT_EQ (nano::epoch::epoch_1, epoch_open->sideband ().details.epoch);
ASSERT_EQ (nano::epoch::epoch_0, epoch_open->sideband ().source_epoch); // Not used for epoch state blocks
}
TEST (ledger, epoch_open_pending)
{
nano::block_builder builder{};
nano::test::system system{ 1 };
auto & node1 = *system.nodes[0];
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1{};
auto epoch_open = builder.state ()
.account (key1.pub)
.previous (0)
.representative (0)
.balance (0)
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (key1.pub))
.build_shared ();
auto process_result = node1.ledger.process (node1.store.tx_begin_write (), *epoch_open);
ASSERT_EQ (nano::process_result::gap_epoch_open_pending, process_result.code);
ASSERT_EQ (nano::signature_verification::valid_epoch, process_result.verified);
node1.block_processor.add (epoch_open);
// Waits for the block to get saved in the database
ASSERT_TIMELY (10s, 1 == node1.unchecked.count (node1.store.tx_begin_read ()));
ASSERT_FALSE (node1.ledger.block_or_pruned_exists (epoch_open->hash ()));
// Open block should be inserted into unchecked
auto blocks = node1.unchecked.get (node1.store.tx_begin_read (), nano::hash_or_account (epoch_open->account ()).hash);
ASSERT_EQ (blocks.size (), 1);
ASSERT_EQ (blocks[0].block->full_hash (), epoch_open->full_hash ());
ASSERT_EQ (blocks[0].verified, nano::signature_verification::valid_epoch);
// New block to process epoch open
auto send1 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 100)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build_shared ();
node1.block_processor.add (send1);
ASSERT_TIMELY (10s, node1.ledger.block_or_pruned_exists (epoch_open->hash ()));
}
TEST (ledger, block_hash_account_conflict)
{
nano::block_builder builder;
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::keypair key2;
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
/*
* Generate a send block whose destination is a block hash already
* in the ledger and not an account
*/
auto send1 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 100)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build_shared ();
auto receive1 = builder.state ()
.account (key1.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (100)
.link (send1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build_shared ();
/*
* Note that the below link is a block hash when this is intended
* to represent a send state block. This can generally never be
* received , except by epoch blocks, which can sign an open block
* for arbitrary accounts.
*/
auto send2 = builder.state ()
.account (key1.pub)
.previous (receive1->hash ())
.representative (nano::dev::genesis->account ())
.balance (90)
.link (receive1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (receive1->hash ()))
.build_shared ();
/*
* Generate an epoch open for the account with the same value as the block hash
*/
auto receive1_hash = receive1->hash ();
auto open_epoch1 = builder.state ()
.account (reinterpret_cast<nano::account const &> (receive1_hash))
.previous (0)
.representative (0)
.balance (0)
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (receive1->hash ()))
.build_shared ();
node1.work_generate_blocking (*send1);
node1.work_generate_blocking (*receive1);
node1.work_generate_blocking (*send2);
node1.work_generate_blocking (*open_epoch1);
ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code);
ASSERT_EQ (nano::process_result::progress, node1.process (*receive1).code);
ASSERT_EQ (nano::process_result::progress, node1.process (*send2).code);
ASSERT_EQ (nano::process_result::progress, node1.process (*open_epoch1).code);
nano::test::blocks_confirm (node1, { send1, receive1, send2, open_epoch1 });
auto election1 = node1.active.election (send1->qualified_root ());
ASSERT_NE (nullptr, election1);
auto election2 = node1.active.election (receive1->qualified_root ());
ASSERT_NE (nullptr, election2);
auto election3 = node1.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election3);
auto election4 = node1.active.election (open_epoch1->qualified_root ());
ASSERT_NE (nullptr, election4);
auto winner1 (election1->winner ());
auto winner2 (election2->winner ());
auto winner3 (election3->winner ());
auto winner4 (election4->winner ());
ASSERT_EQ (*send1, *winner1);
ASSERT_EQ (*receive1, *winner2);
ASSERT_EQ (*send2, *winner3);
ASSERT_EQ (*open_epoch1, *winner4);
}
TEST (ledger, could_fit)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair destination;
// Test legacy and state change blocks could_fit
nano::block_builder builder;
auto change1 = builder
.change ()
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
auto change2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_TRUE (ledger.could_fit (transaction, *change1));
ASSERT_TRUE (ledger.could_fit (transaction, *change2));
// Test legacy and state send
nano::keypair key1;
auto send1 = builder
.send ()
.previous (change1->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (change1->hash ()))
.build ();
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (change1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 1)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (change1->hash ()))
.build ();
ASSERT_FALSE (ledger.could_fit (transaction, *send1));
ASSERT_FALSE (ledger.could_fit (transaction, *send2));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change1).code);
ASSERT_TRUE (ledger.could_fit (transaction, *change1));
ASSERT_TRUE (ledger.could_fit (transaction, *change2));
ASSERT_TRUE (ledger.could_fit (transaction, *send1));
ASSERT_TRUE (ledger.could_fit (transaction, *send2));
// Test legacy and state open
auto open1 = builder
.open ()
.source (send2->hash ())
.representative (nano::dev::genesis->account ())
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
auto open2 = builder
.state ()
.account (key1.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (1)
.link (send2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_FALSE (ledger.could_fit (transaction, *open1));
ASSERT_FALSE (ledger.could_fit (transaction, *open2));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_TRUE (ledger.could_fit (transaction, *send1));
ASSERT_TRUE (ledger.could_fit (transaction, *send2));
ASSERT_TRUE (ledger.could_fit (transaction, *open1));
ASSERT_TRUE (ledger.could_fit (transaction, *open2));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_TRUE (ledger.could_fit (transaction, *open1));
ASSERT_TRUE (ledger.could_fit (transaction, *open2));
// Create another send to receive
auto send3 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send2->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 2)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send2->hash ()))
.build ();
// Test legacy and state receive
auto receive1 = builder
.receive ()
.previous (open1->hash ())
.source (send3->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (open1->hash ()))
.build ();
auto receive2 = builder
.state ()
.account (key1.pub)
.previous (open1->hash ())
.representative (nano::dev::genesis->account ())
.balance (2)
.link (send3->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (open1->hash ()))
.build ();
ASSERT_FALSE (ledger.could_fit (transaction, *receive1));
ASSERT_FALSE (ledger.could_fit (transaction, *receive2));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send3).code);
ASSERT_TRUE (ledger.could_fit (transaction, *receive1));
ASSERT_TRUE (ledger.could_fit (transaction, *receive2));
// Test epoch (state)
auto epoch1 = builder
.state ()
.account (key1.pub)
.previous (receive1->hash ())
.representative (nano::dev::genesis->account ())
.balance (2)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (receive1->hash ()))
.build ();
ASSERT_FALSE (ledger.could_fit (transaction, *epoch1));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_TRUE (ledger.could_fit (transaction, *receive1));
ASSERT_TRUE (ledger.could_fit (transaction, *receive2));
ASSERT_TRUE (ledger.could_fit (transaction, *epoch1));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
ASSERT_TRUE (ledger.could_fit (transaction, *epoch1));
}
TEST (ledger, unchecked_epoch)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto open1 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*open1);
auto epoch1 = builder
.state ()
.account (destination.pub)
.previous (open1->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*epoch1);
node1.block_processor.add (epoch1);
{
// Waits for the epoch1 block to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (10s, 1 == node1.unchecked.count (node1.store.tx_begin_read ()));
auto blocks = node1.unchecked.get (node1.store.tx_begin_read (), epoch1->previous ());
ASSERT_EQ (blocks.size (), 1);
ASSERT_EQ (blocks[0].verified, nano::signature_verification::valid_epoch);
}
node1.block_processor.add (send1);
node1.block_processor.add (open1);
ASSERT_TIMELY (5s, node1.store.block.exists (node1.store.tx_begin_read (), epoch1->hash ()));
{
// Waits for the last blocks to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (10s, 0 == node1.unchecked.count (node1.store.tx_begin_read ()));
nano::account_info info{};
ASSERT_FALSE (node1.store.account.get (node1.store.tx_begin_read (), destination.pub, info));
ASSERT_EQ (info.epoch (), nano::epoch::epoch_1);
}
}
TEST (ledger, unchecked_epoch_invalid)
{
nano::test::system system;
nano::node_config node_config (nano::test::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 (*system.add_node (node_config));
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto open1 = builder
.state ()
.account (destination.pub)
.previous (0)
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (send1->hash ())
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*open1);
// Epoch block with account own signature
auto epoch1 = builder
.state ()
.account (destination.pub)
.previous (open1->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio)
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*epoch1);
// Pseudo epoch block (send subtype, destination - epoch link)
auto epoch2 = builder
.state ()
.account (destination.pub)
.previous (open1->hash ())
.representative (destination.pub)
.balance (nano::Gxrb_ratio - 1)
.link (node1.ledger.epoch_link (nano::epoch::epoch_1))
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*epoch2);
node1.block_processor.add (epoch1);
node1.block_processor.add (epoch2);
{
// Waits for the last blocks to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (10s, 2 == node1.unchecked.count (node1.store.tx_begin_read ()));
auto blocks = node1.unchecked.get (node1.store.tx_begin_read (), epoch1->previous ());
ASSERT_EQ (blocks.size (), 2);
ASSERT_EQ (blocks[0].verified, nano::signature_verification::valid);
ASSERT_EQ (blocks[1].verified, nano::signature_verification::valid);
}
node1.block_processor.add (send1);
node1.block_processor.add (open1);
// Waits for the last blocks to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (10s, node1.store.block.exists (node1.store.tx_begin_read (), epoch2->hash ()));
{
auto transaction = node1.store.tx_begin_read ();
ASSERT_FALSE (node1.store.block.exists (transaction, epoch1->hash ()));
ASSERT_TRUE (node1.active.empty ());
auto unchecked_count = node1.unchecked.count (transaction);
ASSERT_EQ (unchecked_count, 0);
ASSERT_EQ (unchecked_count, node1.unchecked.count (transaction));
nano::account_info info{};
ASSERT_FALSE (node1.store.account.get (transaction, destination.pub, info));
ASSERT_NE (info.epoch (), nano::epoch::epoch_1);
auto epoch2_store = node1.store.block.get (transaction, epoch2->hash ());
ASSERT_NE (nullptr, epoch2_store);
ASSERT_EQ (nano::epoch::epoch_0, epoch2_store->sideband ().details.epoch);
ASSERT_TRUE (epoch2_store->sideband ().details.is_send);
ASSERT_FALSE (epoch2_store->sideband ().details.is_epoch);
ASSERT_FALSE (epoch2_store->sideband ().details.is_receive);
}
}
TEST (ledger, unchecked_open)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair destination;
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (destination.pub)
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*open1);
// Invalid signature for open block
auto open2 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis_key.pub)
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*open2);
open2->signature.bytes[0] ^= 1;
node1.block_processor.add (open1);
node1.block_processor.add (open2);
{
// Waits for the last blocks to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (10s, 1 == node1.unchecked.count (node1.store.tx_begin_read ()));
auto blocks = node1.unchecked.get (node1.store.tx_begin_read (), open1->source ());
ASSERT_EQ (blocks.size (), 1);
ASSERT_EQ (blocks[0].verified, nano::signature_verification::valid);
}
node1.block_processor.add (send1);
// Waits for the send1 block to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (10s, node1.store.block.exists (node1.store.tx_begin_read (), open1->hash ()));
ASSERT_EQ (0, node1.unchecked.count (node1.store.tx_begin_read ()));
}
TEST (ledger, unchecked_receive)
{
nano::test::system system{ 1 };
auto & node1 = *system.nodes[0];
nano::keypair destination{};
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio)
.link (destination.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send2);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (destination.pub)
.account (destination.pub)
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*open1);
auto receive1 = builder
.receive ()
.previous (open1->hash ())
.source (send2->hash ())
.sign (destination.prv, destination.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*receive1);
node1.block_processor.add (send1);
node1.block_processor.add (receive1);
auto check_block_is_listed = [&] (nano::transaction const & transaction_a, nano::block_hash const & block_hash_a) {
return !node1.unchecked.get (transaction_a, block_hash_a).empty ();
};
// Previous block for receive1 is unknown, signature cannot be validated
{
// Waits for the last blocks to pass through block_processor and unchecked.put queues
ASSERT_TIMELY (15s, check_block_is_listed (node1.store.tx_begin_read (), receive1->previous ()));
auto blocks = node1.unchecked.get (node1.store.tx_begin_read (), receive1->previous ());
ASSERT_EQ (blocks.size (), 1);
ASSERT_EQ (blocks[0].verified, nano::signature_verification::unknown);
}
// Waits for the open1 block to pass through block_processor and unchecked.put queues
node1.block_processor.add (open1);
ASSERT_TIMELY (15s, check_block_is_listed (node1.store.tx_begin_read (), receive1->source ()));
// Previous block for receive1 is known, signature was validated
{
auto transaction = node1.store.tx_begin_read ();
auto blocks (node1.unchecked.get (transaction, receive1->source ()));
ASSERT_EQ (blocks.size (), 1);
ASSERT_EQ (blocks[0].verified, nano::signature_verification::valid);
}
node1.block_processor.add (send2);
ASSERT_TIMELY (10s, node1.store.block.exists (node1.store.tx_begin_read (), receive1->hash ()));
ASSERT_EQ (0, node1.unchecked.count (node1.store.tx_begin_read ()));
}
TEST (ledger, confirmation_height_not_updated)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::account_info account_info;
ASSERT_FALSE (store.account.get (transaction, nano::dev::genesis_key.pub, account_info));
nano::keypair key;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (account_info.head)
.destination (key.pub)
.balance (50)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (account_info.head))
.build ();
nano::confirmation_height_info confirmation_height_info;
ASSERT_FALSE (store.confirmation_height.get (transaction, nano::dev::genesis->account (), confirmation_height_info));
ASSERT_EQ (1, confirmation_height_info.height);
ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier);
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_FALSE (store.confirmation_height.get (transaction, nano::dev::genesis->account (), confirmation_height_info));
ASSERT_EQ (1, confirmation_height_info.height);
ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier);
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (key.pub)
.sign (key.prv, key.pub)
.work (*pool.generate (key.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_TRUE (store.confirmation_height.get (transaction, key.pub, confirmation_height_info));
ASSERT_EQ (0, confirmation_height_info.height);
ASSERT_EQ (nano::block_hash (0), confirmation_height_info.frontier);
}
TEST (ledger, zero_rep)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::block_builder builder;
auto block1 = builder.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (0)
.balance (nano::dev::constants.genesis_amount)
.link (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (nano::dev::genesis->hash ()))
.build ();
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *block1).code);
ASSERT_EQ (0, node1.ledger.cache.rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (nano::dev::constants.genesis_amount, node1.ledger.cache.rep_weights.representation_get (0));
auto block2 = builder.state ()
.account (nano::dev::genesis_key.pub)
.previous (block1->hash ())
.representative (nano::dev::genesis_key.pub)
.balance (nano::dev::constants.genesis_amount)
.link (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (block1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *block2).code);
ASSERT_EQ (nano::dev::constants.genesis_amount, node1.ledger.cache.rep_weights.representation_get (nano::dev::genesis_key.pub));
ASSERT_EQ (0, node1.ledger.cache.rep_weights.representation_get (0));
}
TEST (ledger, work_validation)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto gen = nano::dev::genesis_key;
nano::keypair key;
// With random work the block doesn't pass, then modifies the block with sufficient work and ensures a correct result
auto process_block = [&store, &ledger, &pool] (nano::block & block_a, nano::block_details const details_a) {
auto threshold = nano::dev::network_params.work.threshold (block_a.work_version (), details_a);
// Rarely failed with random work, so modify until it doesn't have enough difficulty
while (nano::dev::network_params.work.difficulty (block_a) >= threshold)
{
block_a.block_work_set (block_a.block_work () + 1);
}
EXPECT_EQ (nano::process_result::insufficient_work, ledger.process (store.tx_begin_write (), block_a).code);
block_a.block_work_set (*pool.generate (block_a.root (), threshold));
EXPECT_EQ (nano::process_result::progress, ledger.process (store.tx_begin_write (), block_a).code);
};
std::error_code ec;
auto send = *builder.send ()
.previous (nano::dev::genesis->hash ())
.destination (gen.pub)
.balance (nano::dev::constants.genesis_amount - 1)
.sign (gen.prv, gen.pub)
.work (0)
.build (ec);
ASSERT_FALSE (ec);
auto receive = *builder.receive ()
.previous (send.hash ())
.source (send.hash ())
.sign (gen.prv, gen.pub)
.work (0)
.build (ec);
ASSERT_FALSE (ec);
auto change = *builder.change ()
.previous (receive.hash ())
.representative (key.pub)
.sign (gen.prv, gen.pub)
.work (0)
.build (ec);
ASSERT_FALSE (ec);
auto state = *builder.state ()
.account (gen.pub)
.previous (change.hash ())
.representative (gen.pub)
.balance (nano::dev::constants.genesis_amount - 1)
.link (key.pub)
.sign (gen.prv, gen.pub)
.work (0)
.build (ec);
ASSERT_FALSE (ec);
auto open = *builder.open ()
.account (key.pub)
.source (state.hash ())
.representative (key.pub)
.sign (key.prv, key.pub)
.work (0)
.build (ec);
ASSERT_FALSE (ec);
auto epoch = *builder.state ()
.account (key.pub)
.previous (open.hash ())
.balance (1)
.representative (key.pub)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (gen.prv, gen.pub)
.work (0)
.build (ec);
ASSERT_FALSE (ec);
process_block (send, {});
process_block (receive, {});
process_block (change, {});
process_block (state, nano::block_details (nano::epoch::epoch_0, true, false, false));
process_block (open, {});
process_block (epoch, nano::block_details (nano::epoch::epoch_1, false, false, true));
}
TEST (ledger, dependents_confirmed)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::block_builder builder;
ASSERT_TRUE (ledger.dependents_confirmed (transaction, *nano::dev::genesis));
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
auto send1 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 100)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (ledger.dependents_confirmed (transaction, *send1));
auto send2 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 200)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_FALSE (ledger.dependents_confirmed (transaction, *send2));
auto receive1 = builder.state ()
.account (key1.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (100)
.link (send1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive1));
nano::confirmation_height_info height;
ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis->account (), height));
height.height += 1;
ledger.store.confirmation_height.put (transaction, nano::dev::genesis->account (), height);
ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive1));
auto receive2 = builder.state ()
.account (key1.pub)
.previous (receive1->hash ())
.representative (nano::dev::genesis->account ())
.balance (200)
.link (send2->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (receive1->hash ()))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive2).code);
ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive2));
ASSERT_TRUE (ledger.store.confirmation_height.get (transaction, key1.pub, height));
height.height += 1;
ledger.store.confirmation_height.put (transaction, key1.pub, height);
ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive2));
ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis->account (), height));
height.height += 1;
ledger.store.confirmation_height.put (transaction, nano::dev::genesis->account (), height);
ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive2));
}
TEST (ledger, dependents_confirmed_pruning)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_FALSE (store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::block_builder builder;
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
auto send1 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 100)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
auto send2 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 200)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
nano::confirmation_height_info height;
ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis->account (), height));
height.height = 3;
ledger.store.confirmation_height.put (transaction, nano::dev::genesis->account (), height);
ASSERT_TRUE (ledger.block_confirmed (transaction, send1->hash ()));
ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1));
auto receive1 = builder.state ()
.account (key1.pub)
.previous (0)
.representative (nano::dev::genesis->account ())
.balance (100)
.link (send1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build_shared ();
ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive1));
}
TEST (ledger, block_confirmed)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = store.tx_begin_write ();
nano::block_builder builder;
ASSERT_TRUE (ledger.block_confirmed (transaction, nano::dev::genesis->hash ()));
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::keypair key1;
auto send1 = builder.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 100)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
// Must be safe against non-existing blocks
ASSERT_FALSE (ledger.block_confirmed (transaction, send1->hash ()));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_FALSE (ledger.block_confirmed (transaction, send1->hash ()));
nano::confirmation_height_info height;
ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis->account (), height));
++height.height;
ledger.store.confirmation_height.put (transaction, nano::dev::genesis->account (), height);
ASSERT_TRUE (ledger.block_confirmed (transaction, send1->hash ()));
}
TEST (ledger, cache)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto & stats = ctx.stats ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
size_t const total = 100;
// Check existing ledger (incremental cache update) and reload on a new ledger
for (size_t i (0); i < total; ++i)
{
auto account_count = 1 + i;
auto block_count = 1 + 2 * (i + 1) - 2;
auto cemented_count = 1 + 2 * (i + 1) - 2;
auto genesis_weight = nano::dev::constants.genesis_amount - i;
auto pruned_count = i;
auto cache_check = [&, i] (nano::ledger_cache const & cache_a) {
ASSERT_EQ (account_count, cache_a.account_count);
ASSERT_EQ (block_count, cache_a.block_count);
ASSERT_EQ (cemented_count, cache_a.cemented_count);
ASSERT_EQ (genesis_weight, cache_a.rep_weights.representation_get (nano::dev::genesis->account ()));
ASSERT_EQ (pruned_count, cache_a.pruned_count);
};
nano::keypair key;
auto const latest = ledger.latest (store.tx_begin_read (), nano::dev::genesis->account ());
auto send = builder.state ()
.account (nano::dev::genesis->account ())
.previous (latest)
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - (i + 1))
.link (key.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (latest))
.build ();
auto open = builder.state ()
.account (key.pub)
.previous (0)
.representative (key.pub)
.balance (1)
.link (send->hash ())
.sign (key.prv, key.pub)
.work (*pool.generate (key.pub))
.build ();
{
auto transaction (store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
}
++block_count;
--genesis_weight;
cache_check (ledger.cache);
cache_check (nano::ledger (store, stats, nano::dev::constants).cache);
{
auto transaction (store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open).code);
}
++block_count;
++account_count;
cache_check (ledger.cache);
cache_check (nano::ledger (store, stats, nano::dev::constants).cache);
{
auto transaction (store.tx_begin_write ());
nano::confirmation_height_info height;
ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis->account (), height));
++height.height;
height.frontier = send->hash ();
ledger.store.confirmation_height.put (transaction, nano::dev::genesis->account (), height);
ASSERT_TRUE (ledger.block_confirmed (transaction, send->hash ()));
++ledger.cache.cemented_count;
}
++cemented_count;
cache_check (ledger.cache);
cache_check (nano::ledger (store, stats, nano::dev::constants).cache);
{
auto transaction (store.tx_begin_write ());
nano::confirmation_height_info height;
ledger.store.confirmation_height.get (transaction, key.pub, height);
height.height += 1;
height.frontier = open->hash ();
ledger.store.confirmation_height.put (transaction, key.pub, height);
ASSERT_TRUE (ledger.block_confirmed (transaction, open->hash ()));
++ledger.cache.cemented_count;
}
++cemented_count;
cache_check (ledger.cache);
cache_check (nano::ledger (store, stats, nano::dev::constants).cache);
{
auto transaction (store.tx_begin_write ());
ledger.store.pruned.put (transaction, open->hash ());
++ledger.cache.pruned_count;
}
++pruned_count;
cache_check (ledger.cache);
cache_check (nano::ledger (store, stats, nano::dev::constants).cache);
}
}
TEST (ledger, pruning_action)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store->block.exists (transaction, send1->hash ()));
auto send1_stored (store->block.get (transaction, send1->hash ()));
ASSERT_NE (nullptr, send1_stored);
ASSERT_EQ (*send1, *send1_stored);
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Pruning action
ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1));
ASSERT_EQ (0, ledger.pruning_action (transaction, nano::dev::genesis->hash (), 1));
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (ledger.block_or_pruned_exists (transaction, send1->hash ()));
// Pruned ledger start without proper flags emulation
ledger.pruning = false;
ASSERT_TRUE (ledger.block_or_pruned_exists (transaction, send1->hash ()));
ledger.pruning = true;
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Receiving pruned block
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send2->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_TRUE (store->block.exists (transaction, receive1->hash ()));
auto receive1_stored (store->block.get (transaction, receive1->hash ()));
ASSERT_NE (nullptr, receive1_stored);
ASSERT_EQ (*receive1, *receive1_stored);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (4, receive1_stored->sideband ().height);
ASSERT_FALSE (receive1_stored->sideband ().details.is_send);
ASSERT_TRUE (receive1_stored->sideband ().details.is_receive);
ASSERT_FALSE (receive1_stored->sideband ().details.is_epoch);
// Middle block pruning
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
ASSERT_EQ (1, ledger.pruning_action (transaction, send2->hash (), 1));
ASSERT_TRUE (store->pruned.exists (transaction, send2->hash ()));
ASSERT_FALSE (store->block.exists (transaction, send2->hash ()));
ASSERT_EQ (store->account.count (transaction), ledger.cache.account_count);
ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count);
ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count);
}
TEST (ledger, pruning_large_chain)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
size_t send_receive_pairs (20);
auto last_hash (nano::dev::genesis->hash ());
nano::block_builder builder;
for (auto i (0); i < send_receive_pairs; i++)
{
auto send = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (last_hash)
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (last_hash))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send).code);
ASSERT_TRUE (store->block.exists (transaction, send->hash ()));
auto receive = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (send->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive).code);
ASSERT_TRUE (store->block.exists (transaction, receive->hash ()));
last_hash = receive->hash ();
}
ASSERT_EQ (0, store->pruned.count (transaction));
ASSERT_EQ (send_receive_pairs * 2 + 1, store->block.count (transaction));
// Pruning action
ASSERT_EQ (send_receive_pairs * 2, ledger.pruning_action (transaction, last_hash, 5));
ASSERT_TRUE (store->pruned.exists (transaction, last_hash));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
ASSERT_FALSE (store->block.exists (transaction, last_hash));
ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count);
ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count);
ASSERT_EQ (send_receive_pairs * 2, store->pruned.count (transaction));
ASSERT_EQ (1, store->block.count (transaction)); // Genesis
}
TEST (ledger, pruning_source_rollback)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto epoch1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount)
.link (ledger.epoch_link (nano::epoch::epoch_1))
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *epoch1).code);
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (epoch1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (epoch1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Pruning action
ASSERT_EQ (2, ledger.pruning_action (transaction, send1->hash (), 1));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
ASSERT_FALSE (store->block.exists (transaction, epoch1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, epoch1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
nano::pending_info info;
ASSERT_FALSE (store->pending.get (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ()), info));
ASSERT_EQ (nano::dev::genesis->account (), info.source);
ASSERT_EQ (nano::Gxrb_ratio, info.amount.number ());
ASSERT_EQ (nano::epoch::epoch_1, info.epoch);
// Receiving pruned block
auto receive1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send2->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (2, ledger.cache.pruned_count);
ASSERT_EQ (5, ledger.cache.block_count);
// Rollback receive block
ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ()));
nano::pending_info info2;
ASSERT_FALSE (store->pending.get (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ()), info2));
ASSERT_NE (nano::dev::genesis->account (), info2.source); // Tradeoff to not store pruned blocks accounts
ASSERT_EQ (nano::Gxrb_ratio, info2.amount.number ());
ASSERT_EQ (nano::epoch::epoch_1, info2.epoch);
// Process receive block again
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (2, ledger.cache.pruned_count);
ASSERT_EQ (5, ledger.cache.block_count);
}
TEST (ledger, pruning_source_rollback_legacy)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
nano::keypair key1;
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (key1.pub, send2->hash ())));
auto send3 = builder
.send ()
.previous (send2->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 3 * nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send2->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send3).code);
ASSERT_TRUE (store->block.exists (transaction, send3->hash ()));
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send3->hash ())));
// Pruning action
ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1));
ASSERT_FALSE (store->block.exists (transaction, send2->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, send2->hash ()));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
nano::pending_info info1;
ASSERT_FALSE (store->pending.get (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ()), info1));
ASSERT_EQ (nano::dev::genesis->account (), info1.source);
ASSERT_EQ (nano::Gxrb_ratio, info1.amount.number ());
ASSERT_EQ (nano::epoch::epoch_0, info1.epoch);
nano::pending_info info2;
ASSERT_FALSE (store->pending.get (transaction, nano::pending_key (key1.pub, send2->hash ()), info2));
ASSERT_EQ (nano::dev::genesis->account (), info2.source);
ASSERT_EQ (nano::Gxrb_ratio, info2.amount.number ());
ASSERT_EQ (nano::epoch::epoch_0, info2.epoch);
// Receiving pruned block
auto receive1 = builder
.receive ()
.previous (send3->hash ())
.source (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send3->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (2, ledger.cache.pruned_count);
ASSERT_EQ (5, ledger.cache.block_count);
// Rollback receive block
ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ()));
nano::pending_info info3;
ASSERT_FALSE (store->pending.get (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ()), info3));
ASSERT_NE (nano::dev::genesis->account (), info3.source); // Tradeoff to not store pruned blocks accounts
ASSERT_EQ (nano::Gxrb_ratio, info3.amount.number ());
ASSERT_EQ (nano::epoch::epoch_0, info3.epoch);
// Process receive block again
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
ASSERT_EQ (2, ledger.cache.pruned_count);
ASSERT_EQ (5, ledger.cache.block_count);
// Receiving pruned block (open)
auto open1 = builder
.open ()
.source (send2->hash ())
.representative (nano::dev::genesis->account ())
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (key1.pub, send2->hash ())));
ASSERT_EQ (2, ledger.cache.pruned_count);
ASSERT_EQ (6, ledger.cache.block_count);
// Rollback open block
ASSERT_FALSE (ledger.rollback (transaction, open1->hash ()));
nano::pending_info info4;
ASSERT_FALSE (store->pending.get (transaction, nano::pending_key (key1.pub, send2->hash ()), info4));
ASSERT_NE (nano::dev::genesis->account (), info4.source); // Tradeoff to not store pruned blocks accounts
ASSERT_EQ (nano::Gxrb_ratio, info4.amount.number ());
ASSERT_EQ (nano::epoch::epoch_0, info4.epoch);
// Process open block again
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (key1.pub, send2->hash ())));
ASSERT_EQ (2, ledger.cache.pruned_count);
ASSERT_EQ (6, ledger.cache.block_count);
}
TEST (ledger, pruning_process_error)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_EQ (0, ledger.cache.pruned_count);
ASSERT_EQ (2, ledger.cache.block_count);
// Pruning action for latest block (not valid action)
ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
// Attempt to process pruned block again
ASSERT_EQ (nano::process_result::old, ledger.process (transaction, *send1).code);
// Attept to process new block after pruned
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::gap_previous, ledger.process (transaction, *send2).code);
ASSERT_EQ (1, ledger.cache.pruned_count);
ASSERT_EQ (2, ledger.cache.block_count);
}
TEST (ledger, pruning_legacy_blocks)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
nano::keypair key1;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis->account (), send1->hash ())));
auto receive1 = builder
.receive ()
.previous (send1->hash ())
.source (send1->hash ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *receive1).code);
auto change1 = builder
.change ()
.previous (receive1->hash ())
.representative (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (receive1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *change1).code);
auto send2 = builder
.send ()
.previous (change1->hash ())
.destination (key1.pub)
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (change1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
auto open1 = builder
.open ()
.source (send2->hash ())
.representative (nano::dev::genesis->account ())
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *open1).code);
auto send3 = builder
.send ()
.previous (open1->hash ())
.destination (nano::dev::genesis->account ())
.balance (0)
.sign (key1.prv, key1.pub)
.work (*pool.generate (open1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send3).code);
// Pruning action
ASSERT_EQ (3, ledger.pruning_action (transaction, change1->hash (), 2));
ASSERT_EQ (1, ledger.pruning_action (transaction, open1->hash (), 1));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
ASSERT_FALSE (store->block.exists (transaction, receive1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, receive1->hash ()));
ASSERT_FALSE (store->block.exists (transaction, change1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, change1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
ASSERT_FALSE (store->block.exists (transaction, open1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, open1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, send3->hash ()));
ASSERT_EQ (4, ledger.cache.pruned_count);
ASSERT_EQ (7, ledger.cache.block_count);
ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count);
ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count);
}
TEST (ledger, pruning_safe_functions)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store->block.exists (transaction, send1->hash ()));
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Pruning action
ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (ledger.block_or_pruned_exists (transaction, send1->hash ())); // true for pruned
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Safe ledger actions
bool error (false);
ASSERT_EQ (0, ledger.balance_safe (transaction, send1->hash (), error));
ASSERT_TRUE (error);
error = false;
ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2, ledger.balance_safe (transaction, send2->hash (), error));
ASSERT_FALSE (error);
error = false;
ASSERT_EQ (0, ledger.amount_safe (transaction, send2->hash (), error));
ASSERT_TRUE (error);
error = false;
ASSERT_TRUE (ledger.account_safe (transaction, send1->hash (), error).is_zero ());
ASSERT_TRUE (error);
error = false;
ASSERT_EQ (nano::dev::genesis->account (), ledger.account_safe (transaction, send2->hash (), error));
ASSERT_FALSE (error);
}
TEST (ledger, hash_root_random)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants);
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats, nano::dev::constants);
ledger.pruning = true;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, ledger.cache, ledger.constants);
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
nano::block_builder builder;
auto send1 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send1).code);
ASSERT_TRUE (store->block.exists (transaction, send1->hash ()));
auto send2 = builder
.state ()
.account (nano::dev::genesis->account ())
.previous (send1->hash ())
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2)
.link (nano::dev::genesis->account ())
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, *send2).code);
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Pruning action
ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1));
ASSERT_FALSE (store->block.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ()));
ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ()));
ASSERT_TRUE (store->block.exists (transaction, send2->hash ()));
// Test random block including pruned
bool done (false);
auto iteration (0);
while (!done)
{
++iteration;
auto root_hash (ledger.hash_root_random (transaction));
done = (root_hash.first == send1->hash ()) && (root_hash.second.is_zero ());
ASSERT_LE (iteration, 1000);
}
done = false;
while (!done)
{
++iteration;
auto root_hash (ledger.hash_root_random (transaction));
done = (root_hash.first == send2->hash ()) && (root_hash.second == send2->root ().as_block_hash ());
ASSERT_LE (iteration, 1000);
}
}
TEST (ledger, migrate_lmdb_to_rocksdb)
{
auto path = nano::unique_path ();
nano::logger_mt logger{};
boost::asio::ip::address_v6 address (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"));
uint16_t port = 100;
nano::lmdb::store store{ logger, path / "data.ldb", nano::dev::constants };
nano::unchecked_map unchecked{ store, false };
nano::stat stats{};
nano::ledger ledger{ store, stats, nano::dev::constants };
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
std::shared_ptr<nano::block> send = nano::state_block_builder ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (0)
.link (nano::account (10))
.balance (nano::dev::constants.genesis_amount - 100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build_shared ();
nano::endpoint_key endpoint_key (address.to_bytes (), port);
auto version = 99;
{
auto transaction = store.tx_begin_write ();
store.initialize (transaction, ledger.cache, ledger.constants);
ASSERT_FALSE (store.init_error ());
// Lower the database to the max version unsupported for upgrades
store.confirmation_height.put (transaction, nano::dev::genesis->account (), { 2, send->hash () });
store.online_weight.put (transaction, 100, nano::amount (2));
store.frontier.put (transaction, nano::block_hash (2), nano::account (5));
store.peer.put (transaction, endpoint_key);
store.pending.put (transaction, nano::pending_key (nano::dev::genesis->account (), send->hash ()), nano::pending_info (nano::dev::genesis->account (), 100, nano::epoch::epoch_0));
store.pruned.put (transaction, send->hash ());
store.version.put (transaction, version);
send->sideband_set ({});
store.block.put (transaction, send->hash (), *send);
store.final_vote.put (transaction, send->qualified_root (), nano::block_hash (2));
}
auto error = ledger.migrate_lmdb_to_rocksdb (path);
ASSERT_FALSE (error);
nano::rocksdb::store rocksdb_store{ logger, path / "rocksdb", nano::dev::constants };
nano::unchecked_map rocksdb_unchecked{ rocksdb_store, false };
auto rocksdb_transaction (rocksdb_store.tx_begin_read ());
nano::pending_info pending_info{};
ASSERT_FALSE (rocksdb_store.pending.get (rocksdb_transaction, nano::pending_key (nano::dev::genesis->account (), send->hash ()), pending_info));
for (auto i = rocksdb_store.online_weight.begin (rocksdb_transaction); i != rocksdb_store.online_weight.end (); ++i)
{
ASSERT_EQ (i->first, 100);
ASSERT_EQ (i->second, 2);
}
ASSERT_EQ (rocksdb_store.online_weight.count (rocksdb_transaction), 1);
auto block1 = rocksdb_store.block.get (rocksdb_transaction, send->hash ());
ASSERT_EQ (*send, *block1);
ASSERT_TRUE (rocksdb_store.peer.exists (rocksdb_transaction, endpoint_key));
ASSERT_EQ (rocksdb_store.version.get (rocksdb_transaction), version);
ASSERT_EQ (rocksdb_store.frontier.get (rocksdb_transaction, 2), 5);
nano::confirmation_height_info confirmation_height_info;
ASSERT_FALSE (rocksdb_store.confirmation_height.get (rocksdb_transaction, nano::dev::genesis->account (), confirmation_height_info));
ASSERT_EQ (confirmation_height_info.height, 2);
ASSERT_EQ (confirmation_height_info.frontier, send->hash ());
ASSERT_TRUE (rocksdb_store.final_vote.get (rocksdb_transaction, nano::root (send->previous ())).size () == 1);
ASSERT_EQ (rocksdb_store.final_vote.get (rocksdb_transaction, nano::root (send->previous ()))[0], nano::block_hash (2));
}
TEST (ledger, unconfirmed_frontiers)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits<unsigned>::max () };
auto unconfirmed_frontiers = ledger.unconfirmed_frontiers ();
ASSERT_TRUE (unconfirmed_frontiers.empty ());
nano::state_block_builder builder;
nano::keypair key;
auto const latest = ledger.latest (store.tx_begin_read (), nano::dev::genesis->account ());
auto send = builder.make_block ()
.account (nano::dev::genesis->account ())
.previous (latest)
.representative (nano::dev::genesis->account ())
.balance (nano::dev::constants.genesis_amount - 100)
.link (key.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (latest))
.build ();
ASSERT_EQ (nano::process_result::progress, ledger.process (store.tx_begin_write (), *send).code);
unconfirmed_frontiers = ledger.unconfirmed_frontiers ();
ASSERT_EQ (unconfirmed_frontiers.size (), 1);
ASSERT_EQ (unconfirmed_frontiers.begin ()->first, 1);
nano::uncemented_info uncemented_info1{ latest, send->hash (), nano::dev::genesis->account () };
auto uncemented_info2 = unconfirmed_frontiers.begin ()->second;
ASSERT_EQ (uncemented_info1.account, uncemented_info2.account);
ASSERT_EQ (uncemented_info1.cemented_frontier, uncemented_info2.cemented_frontier);
ASSERT_EQ (uncemented_info1.frontier, uncemented_info2.frontier);
}
TEST (ledger, is_send_genesis)
{
auto ctx = nano::test::context::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto tx = store.tx_begin_read ();
ASSERT_FALSE (ledger.is_send (tx, *nano::dev::genesis));
}
TEST (ledger, is_send_state)
{
auto ctx = nano::test::context::ledger_send_receive ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto tx = store.tx_begin_read ();
ASSERT_TRUE (ledger.is_send (tx, *ctx.blocks ()[0]));
ASSERT_FALSE (ledger.is_send (tx, *ctx.blocks ()[1]));
}
TEST (ledger, is_send_legacy)
{
auto ctx = nano::test::context::ledger_send_receive_legacy ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto tx = store.tx_begin_read ();
ASSERT_TRUE (ledger.is_send (tx, *ctx.blocks ()[0]));
ASSERT_FALSE (ledger.is_send (tx, *ctx.blocks ()[1]));
}