Skip to content

Commit

Permalink
Test ledger priority
Browse files Browse the repository at this point in the history
  • Loading branch information
pwojcikdev committed Nov 25, 2024
1 parent ca22bdc commit 49331f1
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 14 deletions.
1 change: 1 addition & 0 deletions nano/core_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_executable(
ipc.cpp
ledger.cpp
ledger_confirm.cpp
ledger_priority.cpp
locks.cpp
logging.cpp
message.cpp
Expand Down
324 changes: 324 additions & 0 deletions nano/core_test/ledger_priority.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
#include <nano/lib/blocks.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/secure/ledger_set_any.hpp>
#include <nano/secure/ledger_set_confirmed.hpp>
#include <nano/test_common/ledger_context.hpp>
#include <nano/test_common/make_store.hpp>
#include <nano/test_common/testutil.hpp>

#include <gtest/gtest.h>

// Test priority of genesis block
TEST (ledger_priority, genesis_priority)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & store = ctx.store ();
auto transaction = ledger.tx_begin_write ();

auto const [priority_balance, priority_timestamp] = ledger.block_priority (transaction, *nano::dev::genesis);
ASSERT_EQ (nano::dev::constants.genesis_amount, priority_balance);
ASSERT_EQ (0, priority_timestamp);
}

// Test priority of legacy blocks
TEST (ledger_priority, legacy_blocks_priority)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & pool = ctx.pool ();
auto transaction = ledger.tx_begin_write ();

// Create legacy send block
nano::keypair key1;
nano::block_builder builder;
auto send = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (100)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send));

// Check send priority - should use max of current and previous balance
auto const [send_balance, send_timestamp] = ledger.block_priority (transaction, *send);
ASSERT_EQ (nano::dev::constants.genesis_amount, send_balance);
ASSERT_EQ (nano::dev::genesis->sideband ().timestamp, send_timestamp);

// Create legacy open/receive block
auto open = builder
.open ()
.source (send->hash ())
.representative (key1.pub)
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open));

// Check open priority - should use current balance
auto const [open_balance, open_timestamp] = ledger.block_priority (transaction, *open);
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, open_balance);
ASSERT_EQ (open->sideband ().timestamp, open_timestamp);
}

// Test priority of a send state block
TEST (ledger_priority, send_priority)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & pool = ctx.pool ();
auto transaction = ledger.tx_begin_write ();

nano::keypair key1;
nano::block_builder builder;

// Create state send block
auto send = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.balance (100)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send));

// Check priority
auto const [priority_balance, priority_timestamp] = ledger.block_priority (transaction, *send);
ASSERT_EQ (nano::dev::constants.genesis_amount, priority_balance);
ASSERT_EQ (nano::dev::genesis->sideband ().timestamp, priority_timestamp);
}

// Test priority of a full balance state send
TEST (ledger_priority, full_balance_send)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & pool = ctx.pool ();
auto transaction = ledger.tx_begin_write ();

nano::keypair key1;
nano::block_builder builder;

// Create state send block that sends full balance
auto send = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.balance (0)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send));

// Check priority - should use previous balance since current is 0
auto const [priority_balance, priority_timestamp] = ledger.block_priority (transaction, *send);
ASSERT_EQ (nano::dev::constants.genesis_amount, priority_balance);
ASSERT_EQ (nano::dev::genesis->sideband ().timestamp, priority_timestamp);
}

// Test priority of state blocks with multiple operations
TEST (ledger_priority, sequential_blocks)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & pool = ctx.pool ();
auto transaction = ledger.tx_begin_write ();

nano::keypair key1;
nano::block_builder builder;

// First state send
auto send1 = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.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 ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1));

// State open
auto open = builder
.state ()
.account (key1.pub)
.previous (0)
.representative (key1.pub)
.balance (100)
.link (send1->hash ())
.sign (key1.prv, key1.pub)
.work (*pool.generate (key1.pub))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open));

// Second state send
auto send2 = builder
.state ()
.account (key1.pub)
.previous (open->hash ())
.representative (key1.pub)
.balance (50)
.link (nano::dev::genesis_key.pub)
.sign (key1.prv, key1.pub)
.work (*pool.generate (open->hash ()))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2));

// Check priorities follow the chain
auto const [priority_balance1, timestamp1] = ledger.block_priority (transaction, *send1);
auto const [priority_balance2, timestamp2] = ledger.block_priority (transaction, *open);
auto const [priority_balance3, timestamp3] = ledger.block_priority (transaction, *send2);

ASSERT_EQ (nano::dev::constants.genesis_amount, priority_balance1);
ASSERT_EQ (100, priority_balance2);
ASSERT_EQ (100, priority_balance3); // Max of current (50) and previous (100)

ASSERT_EQ (nano::dev::genesis->sideband ().timestamp, timestamp1);
ASSERT_EQ (send1->sideband ().timestamp, timestamp2);
ASSERT_EQ (open->sideband ().timestamp, timestamp3);
}

// Test priority after rolling back state blocks
TEST (ledger_priority, block_rollback)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & pool = ctx.pool ();
auto transaction = ledger.tx_begin_write ();

nano::keypair key1;
nano::block_builder builder;

// First state send
auto send1 = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.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 ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1));

// Get priority before rollback
auto const [priority_before, timestamp_before] = ledger.block_priority (transaction, *send1);

// Rollback the send
ASSERT_FALSE (ledger.rollback (transaction, send1->hash ()));

// Create new state send with different amount
auto send2 = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.balance (nano::dev::constants.genesis_amount - 200)
.link (key1.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (nano::dev::genesis->hash ()))
.build ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2));

// Check priority after rollback and new block
auto const [priority_after, timestamp_after] = ledger.block_priority (transaction, *send2);

// Priorities should be the same since both use genesis as previous
ASSERT_EQ (priority_before, priority_after);
ASSERT_EQ (timestamp_before, timestamp_after);
}

// Test priority with state block fork
TEST (ledger_priority, block_fork)
{
auto ctx = nano::test::ledger_empty ();
auto & ledger = ctx.ledger ();
auto & pool = ctx.pool ();
auto transaction = ledger.tx_begin_write ();

nano::keypair key1, key2;
nano::block_builder builder;

// First state send
auto send1 = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.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 ();

ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1));

// Create two competing state sends
auto send2a = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (send1->hash ())
.representative (nano::dev::genesis_key.pub)
.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 ();

auto send2b = builder
.state ()
.account (nano::dev::genesis_key.pub)
.previous (send1->hash ())
.representative (nano::dev::genesis_key.pub)
.balance (nano::dev::constants.genesis_amount - 150)
.link (key2.pub)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*pool.generate (send1->hash ()))
.build ();

// Process first fork
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2a));

// Try to process second fork
ASSERT_EQ (nano::block_status::fork, ledger.process (transaction, send2b));

// Get priority of first fork
auto const [priority_a, timestamp_a] = ledger.block_priority (transaction, *send2a);

// Rollback first fork
ASSERT_FALSE (ledger.rollback (transaction, send2a->hash ()));

// Process second fork
ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2b));

// Check priority of second fork
auto const [priority_b, timestamp_b] = ledger.block_priority (transaction, *send2b);

// Both should use send1's balance as previous
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, priority_a);
ASSERT_EQ (nano::dev::constants.genesis_amount - 100, priority_b);
// Both should use send1's timestamp
ASSERT_EQ (send1->sideband ().timestamp, timestamp_a);
ASSERT_EQ (send1->sideband ().timestamp, timestamp_b);
}
1 change: 1 addition & 0 deletions nano/secure/fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace nano
{
class account_info;
class keypair;
class ledger;
class ledger_cache;
class ledger_constants;
Expand Down
15 changes: 1 addition & 14 deletions nano/test_common/testutil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <nano/lib/locks.hpp>
#include <nano/lib/timer.hpp>
#include <nano/node/fwd.hpp>
#include <nano/node/transport/fwd.hpp>
#include <nano/secure/account_info.hpp>
#include <nano/store/fwd.hpp>
Expand Down Expand Up @@ -159,20 +160,6 @@ class stop_guard
/* Convenience globals for gtest projects */
namespace nano
{
class node;
using uint128_t = boost::multiprecision::uint128_t;
class keypair;
class public_key;
class block_hash;
class telemetry_data;
class network_params;
class vote;
class block;
class election;
class ledger;

extern nano::uint128_t const & genesis_amount;

namespace test
{
class system;
Expand Down

0 comments on commit 49331f1

Please sign in to comment.