Block builder (#1451)

This commit is contained in:
cryptocode 2018-12-28 18:04:37 +01:00 committed by Roy Keene
commit 5fb4617b45
11 changed files with 1264 additions and 0 deletions

View file

@ -3,6 +3,7 @@
#include <fstream>
#include <gtest/gtest.h>
#include <rai/core_test/testutil.hpp>
#include <rai/lib/interface.h>
#include <rai/node/common.hpp>
@ -452,3 +453,239 @@ TEST (block_uniquer, cleanup)
ASSERT_LT (iterations++, 200);
}
}
TEST (block_builder, zeroed_state_block)
{
std::error_code ec;
rai::block_builder builder;
rai::keypair key;
// Make sure manually- and builder constructed all-zero blocks have equal hashes, and check signature.
auto zero_block_manual (std::make_shared<rai::state_block> (0, 0, 0, 0, 0, key.prv, key.pub, 0));
auto zero_block_build = builder.state ().zero ().sign (key.prv, key.pub).build ();
ASSERT_TRUE (zero_block_manual->hash () == zero_block_build->hash ());
ASSERT_FALSE (rai::validate_message (key.pub, zero_block_build->hash (), zero_block_build->signature));
}
TEST (block_builder, state)
{
// Test against a random hash from the live network
std::error_code ec;
rai::block_builder builder;
auto block = builder
.state ()
.account_address ("xrb_15nhh1kzw3x8ohez6s75wy3jr6dqgq65oaede1fzk5hqxk4j8ehz7iqtb3to")
.previous_hex ("FEFBCE274E75148AB31FF63EFB3082EF1126BF72BF3FA9C76A97FD5A9F0EBEC5")
.balance_dec ("2251569974100400000000000000000000")
.representative_address ("xrb_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou")
.link_hex ("E16DD58C1EFA8B521545B0A74375AA994D9FC43828A4266D75ECF57F07A7EE86")
.build (ec);
ASSERT_EQ (block->hash ().to_string (), "2D243F8F92CDD0AD94A1D456A6B15F3BE7A6FCBD98D4C5831D06D15C818CD81F");
}
TEST (block_builder, state_missing_rep)
{
// Test against a random hash from the live network
std::error_code ec;
rai::block_builder builder;
auto block = builder
.state ()
.account_address ("xrb_15nhh1kzw3x8ohez6s75wy3jr6dqgq65oaede1fzk5hqxk4j8ehz7iqtb3to")
.previous_hex ("FEFBCE274E75148AB31FF63EFB3082EF1126BF72BF3FA9C76A97FD5A9F0EBEC5")
.balance_dec ("2251569974100400000000000000000000")
.link_hex ("E16DD58C1EFA8B521545B0A74375AA994D9FC43828A4266D75ECF57F07A7EE86")
.sign_zero ()
.work (0)
.build (ec);
ASSERT_EQ (ec, nano::error_common::missing_representative);
}
TEST (block_builder, state_equality)
{
std::error_code ec;
rai::block_builder builder;
// With constructor
rai::keypair key1, key2;
rai::state_block block1 (key1.pub, 1, key2.pub, 2, 4, key1.prv, key1.pub, 5);
// With builder
auto block2 = builder
.state ()
.account (key1.pub)
.previous (1)
.representative (key2.pub)
.balance (2)
.link (4)
.sign (key1.prv, key1.pub)
.work (5)
.build (ec);
ASSERT_NO_ERROR (ec);
ASSERT_EQ (block1.hash (), block2->hash ());
ASSERT_EQ (block1.work, block2->work);
}
TEST (block_builder, state_errors)
{
std::error_code ec;
rai::block_builder builder;
// Make sure we assert when building a block without an std::error_code
EXPECT_DEATH (builder
.state ()
.account_hex ("xyz")
.build (),
".*");
// Ensure the proper error is generated
builder.state ().account_hex ("xrb_bad").build (ec);
ASSERT_EQ (ec, nano::error_common::bad_account_number);
builder.state ().zero ().account_address ("xrb_1111111111111111111111111111111111111111111111111111hifc8npp").build (ec);
ASSERT_NO_ERROR (ec);
}
TEST (block_builder, open)
{
// Test built block's hash against the Genesis open block from the live network
std::error_code ec;
rai::block_builder builder;
auto block = builder
.open ()
.account_address ("xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3")
.representative_address ("xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3")
.source_hex ("E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA")
.build (ec);
ASSERT_EQ (block->hash ().to_string (), "991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948");
}
TEST (block_builder, open_equality)
{
std::error_code ec;
rai::block_builder builder;
// With constructor
rai::keypair key1, key2;
rai::open_block block1 (1, key1.pub, key2.pub, key1.prv, key1.pub, 5);
// With builder
auto block2 = builder
.open ()
.source (1)
.account (key2.pub)
.representative (key1.pub)
.sign (key1.prv, key1.pub)
.work (5)
.build (ec);
ASSERT_NO_ERROR (ec);
ASSERT_EQ (block1.hash (), block2->hash ());
ASSERT_EQ (block1.work, block2->work);
}
TEST (block_builder, change)
{
std::error_code ec;
rai::block_builder builder;
auto block = builder
.change ()
.representative_address ("xrb_3rropjiqfxpmrrkooej4qtmm1pueu36f9ghinpho4esfdor8785a455d16nf")
.previous_hex ("088EE46429CA936F76C4EAA20B97F6D33E5D872971433EE0C1311BCB98764456")
.build (ec);
ASSERT_EQ (block->hash ().to_string (), "13552AC3928E93B5C6C215F61879358E248D4A5246B8B3D1EEC5A566EDCEE077");
}
TEST (block_builder, change_equality)
{
std::error_code ec;
rai::block_builder builder;
// With constructor
rai::keypair key1, key2;
rai::change_block block1 (1, key1.pub, key1.prv, key1.pub, 5);
// With builder
auto block2 = builder
.change ()
.previous (1)
.representative (key1.pub)
.sign (key1.prv, key1.pub)
.work (5)
.build (ec);
ASSERT_NO_ERROR (ec);
ASSERT_EQ (block1.hash (), block2->hash ());
ASSERT_EQ (block1.work, block2->work);
}
TEST (block_builder, send)
{
std::error_code ec;
rai::block_builder builder;
auto block = builder
.send ()
.destination_address ("xrb_1gys8r4crpxhp94n4uho5cshaho81na6454qni5gu9n53gksoyy1wcd4udyb")
.previous_hex ("F685856D73A488894F7F3A62BC3A88E17E985F9969629FF3FDD4A0D4FD823F24")
.balance_hex ("00F035A9C7D818E7C34148C524FFFFEE")
.build (ec);
ASSERT_EQ (block->hash ().to_string (), "4560E7B1F3735D082700CFC2852F5D1F378F7418FD24CEF1AD45AB69316F15CD");
}
TEST (block_builder, send_equality)
{
std::error_code ec;
rai::block_builder builder;
// With constructor
rai::keypair key1, key2;
rai::send_block block1 (1, key1.pub, 2, key1.prv, key1.pub, 5);
// With builder
auto block2 = builder
.send ()
.previous (1)
.destination (key1.pub)
.balance (2)
.sign (key1.prv, key1.pub)
.work (5)
.build (ec);
ASSERT_NO_ERROR (ec);
ASSERT_EQ (block1.hash (), block2->hash ());
ASSERT_EQ (block1.work, block2->work);
}
TEST (block_builder, receive_equality)
{
std::error_code ec;
rai::block_builder builder;
// With constructor
rai::keypair key1;
rai::receive_block block1 (1, 2, key1.prv, key1.pub, 5);
// With builder
auto block2 = builder
.receive ()
.previous (1)
.source (2)
.sign (key1.prv, key1.pub)
.work (5)
.build (ec);
ASSERT_NO_ERROR (ec);
ASSERT_EQ (block1.hash (), block2->hash ());
ASSERT_EQ (block1.work, block2->work);
}
TEST (block_builder, receive)
{
std::error_code ec;
rai::block_builder builder;
auto block = builder
.receive ()
.previous_hex ("59660153194CAC5DAC08509D87970BF86F6AEA943025E2A7ED7460930594950E")
.source_hex ("7B2B0A29C1B235FDF9B4DEF2984BB3573BD1A52D28246396FBB3E4C5FE662135")
.build (ec);
ASSERT_EQ (block->hash ().to_string (), "6C004BF911D9CF2ED75CF6EC45E795122AD5D093FF5A83EDFBA43EC4A3EDC722");
}

View file

@ -13,3 +13,8 @@
#define ASSERT_NO_ERROR(condition) \
GTEST_TEST_ERROR_CODE (!(condition), #condition, condition.message ().c_str (), "", \
GTEST_FATAL_FAILURE_)
/** Extends gtest with a std::error_code assert that expects an error */
#define ASSERT_IS_ERROR(condition) \
GTEST_TEST_ERROR_CODE ((condition.value () > 0), #condition, "An error was expected", "", \
GTEST_FATAL_FAILURE_)

View file

@ -15,6 +15,8 @@ add_library (rai_lib
errors.hpp
errors.cpp
expected.hpp
blockbuilders.cpp
blockbuilders.hpp
blocks.cpp
blocks.hpp
config.hpp

632
rai/lib/blockbuilders.cpp Normal file
View file

@ -0,0 +1,632 @@
#include <cryptopp/osrng.h>
#include <rai/lib/blockbuilders.hpp>
#include <rai/secure/common.hpp>
#include <unordered_map>
namespace
{
template <typename BLOCKTYPE>
void previous_hex_impl (std::string const & previous_hex, std::error_code & ec, BLOCKTYPE & block)
{
rai::block_hash previous;
if (!previous.decode_hex (previous_hex))
{
block->hashables.previous = previous;
}
else
{
ec = nano::error_common::bad_previous;
}
}
template <typename BLOCKTYPE>
void account_hex_impl (std::string const & account_hex, std::error_code & ec, BLOCKTYPE & block)
{
rai::account account;
if (!account.decode_hex (account_hex))
{
block->hashables.account = account;
}
else
{
ec = nano::error_common::bad_account_number;
}
}
template <typename BLOCKTYPE>
void account_address_impl (std::string const & address, std::error_code & ec, BLOCKTYPE & block)
{
rai::account account;
if (!account.decode_account (address))
{
block->hashables.account = account;
}
else
{
ec = nano::error_common::bad_account_number;
}
}
template <typename BLOCKTYPE>
void representative_hex_impl (std::string const & account_hex, std::error_code & ec, BLOCKTYPE & block)
{
rai::account account;
if (!account.decode_hex (account_hex))
{
block->hashables.representative = account;
}
else
{
ec = nano::error_common::bad_representative_number;
}
}
template <typename BLOCKTYPE>
void representative_address_impl (std::string const & address, std::error_code & ec, BLOCKTYPE & block)
{
rai::account account;
if (!account.decode_account (address))
{
block->hashables.representative = account;
}
else
{
ec = nano::error_common::bad_representative_number;
}
}
template <typename BLOCKTYPE>
void destination_hex_impl (std::string const & account_hex, std::error_code & ec, BLOCKTYPE & block)
{
rai::account account;
if (!account.decode_hex (account_hex))
{
block->hashables.destination = account;
}
else
{
ec = nano::error_common::bad_account_number;
}
}
template <typename BLOCKTYPE>
void destination_address_impl (std::string const & address, std::error_code & ec, BLOCKTYPE & block)
{
rai::account account;
if (!account.decode_account (address))
{
block->hashables.destination = account;
}
else
{
ec = nano::error_common::bad_account_number;
}
}
template <typename BLOCKTYPE>
void source_hex_impl (std::string const & source_hex, std::error_code & ec, BLOCKTYPE & block)
{
rai::block_hash source;
if (!source.decode_hex (source_hex))
{
block->hashables.source = source;
}
else
{
ec = nano::error_common::bad_source;
}
}
template <typename BLOCKTYPE>
void balance_dec_impl (std::string const & balance_decimal, std::error_code & ec, BLOCKTYPE & block)
{
rai::amount balance;
if (!balance.decode_dec (balance_decimal))
{
block->hashables.balance = balance;
}
else
{
ec = nano::error_common::bad_balance;
}
}
template <typename BLOCKTYPE>
void balance_hex_impl (std::string const & balance_hex, std::error_code & ec, BLOCKTYPE & block)
{
rai::amount balance;
if (!balance.decode_hex (balance_hex))
{
block->hashables.balance = balance;
}
else
{
ec = nano::error_common::bad_balance;
}
}
/* The cost of looking up the error_code map is only taken if field-presence checks fail */
std::unordered_map<uint8_t, std::error_code> ec_map = {
{ static_cast<uint8_t> (rai::build_flags::account_present), nano::error_common::missing_account },
{ static_cast<uint8_t> (rai::build_flags::balance_present), nano::error_common::missing_balance },
{ static_cast<uint8_t> (rai::build_flags::link_present), nano::error_common::missing_link },
{ static_cast<uint8_t> (rai::build_flags::previous_present), nano::error_common::missing_previous },
{ static_cast<uint8_t> (rai::build_flags::representative_present), nano::error_common::missing_representative },
{ static_cast<uint8_t> (rai::build_flags::signature_present), nano::error_common::missing_signature },
{ static_cast<uint8_t> (rai::build_flags::work_present), nano::error_common::missing_work }
};
/** Find first set bit as a mask, e.g. 10101000 => 0x08. Returns -1 if no bit is set. */
inline signed ffs_mask (uint8_t num)
{
for (signed i = 0; i < 8; i++)
{
if ((num >> i) & 1)
{
return 1 << i;
}
}
return -1;
}
/**
* Check if \p build_state contains all the flags in \p block_all_flags.
* If not, return the corresponding nano::error_common::missing_<...> value.
*/
std::error_code check_fields_set (uint8_t block_all_flags, uint8_t build_state)
{
std::error_code ec;
// Figure out which fields are not set. Note that static typing ensures we cannot set values
// not applicable to a given block type, we can only forget to set fields. This will be zero
// if all fields are set and thus succeed.
uint8_t res = block_all_flags ^ build_state;
if (res)
{
// Convert the first bit set to a field mask and look up the error code.
auto build_flags_mask = ffs_mask (res);
assert (ec_map.find (build_flags_mask) != ec_map.end ());
ec = ec_map[build_flags_mask];
}
return ec;
}
} // anonymous namespace
rai::state_block_builder::state_block_builder ()
{
make_block ();
}
rai::state_block_builder & rai::state_block_builder::make_block ()
{
construct_block ();
return *this;
}
void rai::state_block_builder::validate ()
{
if (!ec)
{
ec = check_fields_set (required_fields, build_state);
}
}
rai::state_block_builder & rai::state_block_builder::zero ()
{
block->work = uint64_t (0);
block->signature.clear ();
block->hashables.account.clear ();
block->hashables.balance.clear ();
block->hashables.link.clear ();
block->hashables.previous.clear ();
block->hashables.representative.clear ();
build_state = required_fields;
return *this;
}
rai::state_block_builder & rai::state_block_builder::account (rai::account account)
{
block->hashables.account = account;
build_state |= build_flags::account_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::account_hex (std::string account_hex)
{
account_hex_impl (account_hex, ec, block);
build_state |= build_flags::account_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::account_address (std::string address)
{
account_address_impl (address, ec, block);
build_state |= build_flags::account_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::representative (rai::account account)
{
block->hashables.representative = account;
build_state |= build_flags::representative_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::representative_hex (std::string account_hex)
{
representative_hex_impl (account_hex, ec, block);
build_state |= build_flags::representative_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::representative_address (std::string address)
{
representative_address_impl (address, ec, block);
build_state |= build_flags::representative_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::previous (rai::block_hash previous)
{
block->hashables.previous = previous;
build_state |= build_flags::previous_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::previous_hex (std::string previous_hex)
{
previous_hex_impl (previous_hex, ec, block);
build_state |= build_flags::previous_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::balance (rai::amount balance)
{
block->hashables.balance = balance;
build_state |= build_flags::balance_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::balance_dec (std::string balance_decimal)
{
balance_dec_impl (balance_decimal, ec, block);
build_state |= build_flags::balance_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::balance_hex (std::string balance_hex)
{
balance_hex_impl (balance_hex, ec, block);
build_state |= build_flags::balance_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::link (rai::uint256_union link)
{
block->hashables.link = link;
build_state |= build_flags::link_present;
return *this;
}
rai::state_block_builder & rai::state_block_builder::link_hex (std::string link_hex)
{
rai::uint256_union link;
if (!link.decode_hex (link_hex))
{
block->hashables.link = link;
build_state |= build_flags::link_present;
}
else
{
ec = nano::error_common::bad_link;
}
return *this;
}
rai::state_block_builder & rai::state_block_builder::link_address (std::string link_address)
{
rai::account link;
if (!link.decode_account (link_address))
{
block->hashables.link = link;
build_state |= build_flags::link_present;
}
else
{
ec = nano::error_common::bad_link;
}
return *this;
}
rai::open_block_builder::open_block_builder ()
{
make_block ();
}
rai::open_block_builder & rai::open_block_builder::make_block ()
{
construct_block ();
return *this;
}
void rai::open_block_builder::validate ()
{
if (!ec)
{
ec = check_fields_set (required_fields, build_state);
}
}
rai::open_block_builder & rai::open_block_builder::zero ()
{
block->work = uint64_t (0);
block->signature.clear ();
block->hashables.account.clear ();
block->hashables.representative.clear ();
block->hashables.source.clear ();
build_state = required_fields;
return *this;
}
rai::open_block_builder & rai::open_block_builder::account (rai::account account)
{
block->hashables.account = account;
build_state |= build_flags::account_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::account_hex (std::string account_hex)
{
account_hex_impl (account_hex, ec, block);
build_state |= build_flags::account_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::account_address (std::string address)
{
account_address_impl (address, ec, block);
build_state |= build_flags::account_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::representative (rai::account account)
{
block->hashables.representative = account;
build_state |= build_flags::representative_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::representative_hex (std::string account_hex)
{
representative_hex_impl (account_hex, ec, block);
build_state |= build_flags::representative_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::representative_address (std::string address)
{
representative_address_impl (address, ec, block);
build_state |= build_flags::representative_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::source (rai::block_hash source)
{
block->hashables.source = source;
build_state |= build_flags::link_present;
return *this;
}
rai::open_block_builder & rai::open_block_builder::source_hex (std::string source_hex)
{
source_hex_impl (source_hex, ec, block);
build_state |= build_flags::link_present;
return *this;
}
rai::change_block_builder::change_block_builder ()
{
make_block ();
}
rai::change_block_builder & rai::change_block_builder::make_block ()
{
construct_block ();
return *this;
}
void rai::change_block_builder::validate ()
{
if (!ec)
{
ec = check_fields_set (required_fields, build_state);
}
}
rai::change_block_builder & rai::change_block_builder::zero ()
{
block->work = uint64_t (0);
block->signature.clear ();
block->hashables.previous.clear ();
block->hashables.representative.clear ();
build_state = required_fields;
return *this;
}
rai::change_block_builder & rai::change_block_builder::representative (rai::account account)
{
block->hashables.representative = account;
build_state |= build_flags::representative_present;
return *this;
}
rai::change_block_builder & rai::change_block_builder::representative_hex (std::string account_hex)
{
representative_hex_impl (account_hex, ec, block);
build_state |= build_flags::representative_present;
return *this;
}
rai::change_block_builder & rai::change_block_builder::representative_address (std::string address)
{
representative_address_impl (address, ec, block);
build_state |= build_flags::representative_present;
return *this;
}
rai::change_block_builder & rai::change_block_builder::previous (rai::block_hash previous)
{
block->hashables.previous = previous;
build_state |= build_flags::previous_present;
return *this;
}
rai::change_block_builder & rai::change_block_builder::previous_hex (std::string previous_hex)
{
previous_hex_impl (previous_hex, ec, block);
build_state |= build_flags::previous_present;
return *this;
}
rai::send_block_builder::send_block_builder ()
{
make_block ();
}
rai::send_block_builder & rai::send_block_builder::make_block ()
{
construct_block ();
return *this;
}
void rai::send_block_builder::validate ()
{
if (!ec)
{
ec = check_fields_set (required_fields, build_state);
}
}
rai::send_block_builder & rai::send_block_builder::zero ()
{
block->work = uint64_t (0);
block->signature.clear ();
block->hashables.previous.clear ();
block->hashables.destination.clear ();
block->hashables.balance.clear ();
build_state = required_fields;
return *this;
}
rai::send_block_builder & rai::send_block_builder::destination (rai::account account)
{
block->hashables.destination = account;
build_state |= build_flags::link_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::destination_hex (std::string account_hex)
{
destination_hex_impl (account_hex, ec, block);
build_state |= build_flags::link_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::destination_address (std::string address)
{
destination_address_impl (address, ec, block);
build_state |= build_flags::link_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::previous (rai::block_hash previous)
{
block->hashables.previous = previous;
build_state |= build_flags::previous_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::previous_hex (std::string previous_hex)
{
previous_hex_impl (previous_hex, ec, block);
build_state |= build_flags::previous_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::balance (rai::amount balance)
{
block->hashables.balance = balance;
build_state |= build_flags::balance_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::balance_dec (std::string balance_decimal)
{
balance_dec_impl (balance_decimal, ec, block);
build_state |= build_flags::balance_present;
return *this;
}
rai::send_block_builder & rai::send_block_builder::balance_hex (std::string balance_hex)
{
balance_hex_impl (balance_hex, ec, block);
build_state |= build_flags::balance_present;
return *this;
}
rai::receive_block_builder::receive_block_builder ()
{
make_block ();
}
rai::receive_block_builder & rai::receive_block_builder::make_block ()
{
construct_block ();
return *this;
}
void rai::receive_block_builder::validate ()
{
if (!ec)
{
ec = check_fields_set (required_fields, build_state);
}
}
rai::receive_block_builder & rai::receive_block_builder::zero ()
{
block->work = uint64_t (0);
block->signature.clear ();
block->hashables.previous.clear ();
block->hashables.source.clear ();
build_state = required_fields;
return *this;
}
rai::receive_block_builder & rai::receive_block_builder::previous (rai::block_hash previous)
{
block->hashables.previous = previous;
build_state |= build_flags::previous_present;
return *this;
}
rai::receive_block_builder & rai::receive_block_builder::previous_hex (std::string previous_hex)
{
previous_hex_impl (previous_hex, ec, block);
build_state |= build_flags::previous_present;
return *this;
}
rai::receive_block_builder & rai::receive_block_builder::source (rai::block_hash source)
{
block->hashables.source = source;
build_state |= build_flags::link_present;
return *this;
}
rai::receive_block_builder & rai::receive_block_builder::source_hex (std::string source_hex)
{
source_hex_impl (source_hex, ec, block);
build_state |= build_flags::link_present;
return *this;
}

331
rai/lib/blockbuilders.hpp Normal file
View file

@ -0,0 +1,331 @@
#pragma once
#include <memory>
#include <rai/lib/blocks.hpp>
#include <rai/lib/errors.hpp>
namespace rai
{
/** Flags to track builder state */
enum class build_flags : uint8_t
{
signature_present = 1,
work_present = 2,
account_present = 4,
balance_present = 8,
/* link also covers source and destination for legacy blocks */
link_present = 16,
previous_present = 32,
representative_present = 64
};
inline rai::build_flags operator| (rai::build_flags a, rai::build_flags b)
{
return static_cast<rai::build_flags> (static_cast<uint8_t> (a) | static_cast<uint8_t> (b));
}
inline uint8_t operator| (uint8_t a, rai::build_flags b)
{
return static_cast<uint8_t> (a | static_cast<uint8_t> (b));
}
inline uint8_t operator& (uint8_t a, rai::build_flags b)
{
return static_cast<uint8_t> (a & static_cast<uint8_t> (b));
}
inline uint8_t operator|= (uint8_t & a, rai::build_flags b)
{
return a = static_cast<uint8_t> (a | static_cast<uint8_t> (b));
}
/**
* Base type for block builder implementations. We employ static polymorphism
* to pass validation through subtypes without incurring the vtable cost.
*/
template <typename BLOCKTYPE, typename BUILDER>
class abstract_builder
{
public:
/** Returns the built block as a unique_ptr */
inline std::unique_ptr<BLOCKTYPE> build ()
{
if (!ec)
{
static_cast<BUILDER *> (this)->validate ();
}
assert (!ec);
return std::move (block);
}
/** Returns the built block as a unique_ptr. Any errors are placed in \p ec */
inline std::unique_ptr<BLOCKTYPE> build (std::error_code & ec)
{
if (!this->ec)
{
static_cast<BUILDER *> (this)->validate ();
}
ec = this->ec;
return std::move (block);
}
/** Set work value */
inline abstract_builder & work (uint64_t work)
{
block->work = work;
build_state |= build_flags::work_present;
return *this;
}
/** Sign the block using the \p private_key and \p public_key */
inline abstract_builder & sign (rai::raw_key const & private_key, rai::public_key const & public_key)
{
block->signature = rai::sign_message (private_key, public_key, block->hash ());
build_state |= build_flags::signature_present;
return *this;
}
/** Set signature to zero to pass build() validation, allowing block to be signed at a later point. This is mostly useful for tests. */
inline abstract_builder & sign_zero ()
{
block->signature.clear ();
build_state |= build_flags::signature_present;
return *this;
}
protected:
abstract_builder ()
{
}
/** Create a new block and resets the internal builder state */
inline void construct_block ()
{
block = std::make_unique<BLOCKTYPE> ();
ec.clear ();
build_state = 0;
}
/** The block we're building. Clients can convert this to shared_ptr as needed. */
std::unique_ptr<BLOCKTYPE> block;
/**
* Set if any builder functions fail. This will be output via the build(std::error_code) function,
* or cause an assert for the parameter-less overload.
*/
std::error_code ec;
/** Bitset to track build state */
uint8_t build_state{ 0 };
/** Required field shared by all block types*/
uint8_t base_fields = static_cast<uint8_t> (rai::build_flags::work_present | rai::build_flags::signature_present);
};
/** Builder for state blocks */
class state_block_builder : public abstract_builder<rai::state_block, state_block_builder>
{
public:
/** Creates a state block builder by calling make_block() */
state_block_builder ();
/** Creates a new block with fields, signature and work set to sentinel values. All fields must be set or zeroed for build() to succeed. */
state_block_builder & make_block ();
/** Sets all hashables, signature and work to zero. */
state_block_builder & zero ();
/** Set account */
state_block_builder & account (rai::account account);
/** Set account from hex representation of public key */
state_block_builder & account_hex (std::string account_hex);
/** Set account from an xrb_ or nano_ address */
state_block_builder & account_address (std::string account_address);
/** Set representative */
state_block_builder & representative (rai::account account);
/** Set representative from hex representation of public key */
state_block_builder & representative_hex (std::string account_hex);
/** Set representative from an xrb_ or nano_ address */
state_block_builder & representative_address (std::string account_address);
/** Set previous block hash */
state_block_builder & previous (rai::block_hash previous);
/** Set previous block hash from hex representation */
state_block_builder & previous_hex (std::string previous_hex);
/** Set balance */
state_block_builder & balance (rai::amount balance);
/** Set balance from decimal string */
state_block_builder & balance_dec (std::string balance_decimal);
/** Set balance from hex string */
state_block_builder & balance_hex (std::string balance_hex);
/** Set link */
state_block_builder & link (rai::uint256_union link);
/** Set link from hex representation */
state_block_builder & link_hex (std::string link_hex);
/** Set link from an xrb_ or nano_ address */
state_block_builder & link_address (std::string link_address);
/** Provides validation for build() */
void validate ();
private:
uint8_t required_fields = base_fields | static_cast<uint8_t> (rai::build_flags::account_present | rai::build_flags::balance_present | rai::build_flags::link_present | rai::build_flags::previous_present | rai::build_flags::representative_present);
};
/** Builder for open blocks */
class open_block_builder : public abstract_builder<rai::open_block, open_block_builder>
{
public:
/** Creates an open block builder by calling make_block() */
open_block_builder ();
/** Creates a new block with fields, signature and work set to sentinel values. All fields must be set or zeroed for build() to succeed. */
open_block_builder & make_block ();
/** Sets all hashables, signature and work to zero. */
open_block_builder & zero ();
/** Set account */
open_block_builder & account (rai::account account);
/** Set account from hex representation of public key */
open_block_builder & account_hex (std::string account_hex);
/** Set account from an xrb_ or nano_ address */
open_block_builder & account_address (std::string account_address);
/** Set representative */
open_block_builder & representative (rai::account account);
/** Set representative from hex representation of public key */
open_block_builder & representative_hex (std::string account_hex);
/** Set representative from an xrb_ or nano_ address */
open_block_builder & representative_address (std::string account_address);
/** Set source block hash */
open_block_builder & source (rai::block_hash source);
/** Set source block hash from hex representation */
open_block_builder & source_hex (std::string source_hex);
/** Provides validation for build() */
void validate ();
private:
uint8_t required_fields = base_fields | static_cast<uint8_t> (rai::build_flags::account_present | rai::build_flags::representative_present | rai::build_flags::link_present);
};
/** Builder for change blocks */
class change_block_builder : public abstract_builder<rai::change_block, change_block_builder>
{
public:
/** Create a change block builder by calling make_block() */
change_block_builder ();
/** Creates a new block with fields, signature and work set to sentinel values. All fields must be set or zeroed for build() to succeed. */
change_block_builder & make_block ();
/** Sets all hashables, signature and work to zero. */
change_block_builder & zero ();
/** Set representative */
change_block_builder & representative (rai::account account);
/** Set representative from hex representation of public key */
change_block_builder & representative_hex (std::string account_hex);
/** Set representative from an xrb_ or nano_ address */
change_block_builder & representative_address (std::string account_address);
/** Set previous block hash */
change_block_builder & previous (rai::block_hash previous);
/** Set previous block hash from hex representation */
change_block_builder & previous_hex (std::string previous_hex);
/** Provides validation for build() */
void validate ();
private:
uint8_t required_fields = base_fields | static_cast<uint8_t> (rai::build_flags::previous_present | rai::build_flags::representative_present);
};
/** Builder for send blocks */
class send_block_builder : public abstract_builder<rai::send_block, send_block_builder>
{
public:
/** Creates a send block builder by calling make_block() */
send_block_builder ();
/** Creates a new block with fields, signature and work set to sentinel values. All fields must be set or zeroed for build() to succeed. */
send_block_builder & make_block ();
/** Sets all hashables, signature and work to zero. */
send_block_builder & zero ();
/** Set destination */
send_block_builder & destination (rai::account account);
/** Set destination from hex representation of public key */
send_block_builder & destination_hex (std::string account_hex);
/** Set destination from an xrb_ or nano_ address */
send_block_builder & destination_address (std::string account_address);
/** Set previous block hash */
send_block_builder & previous (rai::block_hash previous);
/** Set previous block hash from hex representation */
send_block_builder & previous_hex (std::string previous_hex);
/** Set balance */
send_block_builder & balance (rai::amount balance);
/** Set balance from decimal string */
send_block_builder & balance_dec (std::string balance_decimal);
/** Set balance from hex string */
send_block_builder & balance_hex (std::string balance_hex);
/** Provides validation for build() */
void validate ();
private:
uint8_t required_fields = base_fields | static_cast<uint8_t> (build_flags::previous_present | build_flags::link_present | build_flags::balance_present);
};
/** Builder for receive blocks */
class receive_block_builder : public abstract_builder<rai::receive_block, receive_block_builder>
{
public:
/** Creates a receive block by calling make_block() */
receive_block_builder ();
/** Creates a new block with fields, signature and work set to sentinel values. All fields must be set or zeroed for build() to succeed. */
receive_block_builder & make_block ();
/** Sets all hashables, signature and work to zero. */
receive_block_builder & zero ();
/** Set previous block hash */
receive_block_builder & previous (rai::block_hash previous);
/** Set previous block hash from hex representation */
receive_block_builder & previous_hex (std::string previous_hex);
/** Set source block hash */
receive_block_builder & source (rai::block_hash source);
/** Set source block hash from hex representation */
receive_block_builder & source_hex (std::string source_hex);
/** Provides validation for build() */
void validate ();
private:
uint8_t required_fields = base_fields | static_cast<uint8_t> (build_flags::previous_present | build_flags::link_present);
};
/** Block builder to simplify construction of the various block types */
class block_builder
{
public:
/** Prepares a new state block and returns a block builder */
inline rai::state_block_builder & state ()
{
state_builder.make_block ();
return state_builder;
}
/** Prepares a new open block and returns a block builder */
inline rai::open_block_builder & open ()
{
open_builder.make_block ();
return open_builder;
}
/** Prepares a new change block and returns a block builder */
inline rai::change_block_builder & change ()
{
change_builder.make_block ();
return change_builder;
}
/** Prepares a new send block and returns a block builder */
inline rai::send_block_builder & send ()
{
send_builder.make_block ();
return send_builder;
}
/** Prepares a new receive block and returns a block builder */
inline rai::receive_block_builder & receive ()
{
receive_builder.make_block ();
return receive_builder;
}
private:
rai::state_block_builder state_builder;
rai::open_block_builder open_builder;
rai::change_block_builder change_builder;
rai::send_block_builder send_builder;
rai::receive_block_builder receive_builder;
};
}

View file

@ -73,6 +73,7 @@ public:
class send_hashables
{
public:
send_hashables () = default;
send_hashables (rai::account const &, rai::block_hash const &, rai::amount const &);
send_hashables (bool &, rai::stream &);
send_hashables (bool &, boost::property_tree::ptree const &);
@ -84,6 +85,7 @@ public:
class send_block : public rai::block
{
public:
send_block () = default;
send_block (rai::block_hash const &, rai::account const &, rai::amount const &, rai::raw_key const &, rai::public_key const &, uint64_t);
send_block (bool &, rai::stream &);
send_block (bool &, boost::property_tree::ptree const &);
@ -116,6 +118,7 @@ public:
class receive_hashables
{
public:
receive_hashables () = default;
receive_hashables (rai::block_hash const &, rai::block_hash const &);
receive_hashables (bool &, rai::stream &);
receive_hashables (bool &, boost::property_tree::ptree const &);
@ -126,6 +129,7 @@ public:
class receive_block : public rai::block
{
public:
receive_block () = default;
receive_block (rai::block_hash const &, rai::block_hash const &, rai::raw_key const &, rai::public_key const &, uint64_t);
receive_block (bool &, rai::stream &);
receive_block (bool &, boost::property_tree::ptree const &);
@ -158,6 +162,7 @@ public:
class open_hashables
{
public:
open_hashables () = default;
open_hashables (rai::block_hash const &, rai::account const &, rai::account const &);
open_hashables (bool &, rai::stream &);
open_hashables (bool &, boost::property_tree::ptree const &);
@ -169,6 +174,7 @@ public:
class open_block : public rai::block
{
public:
open_block () = default;
open_block (rai::block_hash const &, rai::account const &, rai::account const &, rai::raw_key const &, rai::public_key const &, uint64_t);
open_block (rai::block_hash const &, rai::account const &, rai::account const &, std::nullptr_t);
open_block (bool &, rai::stream &);
@ -202,6 +208,7 @@ public:
class change_hashables
{
public:
change_hashables () = default;
change_hashables (rai::block_hash const &, rai::account const &);
change_hashables (bool &, rai::stream &);
change_hashables (bool &, boost::property_tree::ptree const &);
@ -212,6 +219,7 @@ public:
class change_block : public rai::block
{
public:
change_block () = default;
change_block (rai::block_hash const &, rai::account const &, rai::raw_key const &, rai::public_key const &, uint64_t);
change_block (bool &, rai::stream &);
change_block (bool &, boost::property_tree::ptree const &);
@ -244,6 +252,7 @@ public:
class state_hashables
{
public:
state_hashables () = default;
state_hashables (rai::account const &, rai::block_hash const &, rai::account const &, rai::amount const &, rai::uint256_union const &);
state_hashables (bool &, rai::stream &);
state_hashables (bool &, boost::property_tree::ptree const &);
@ -266,6 +275,7 @@ public:
class state_block : public rai::block
{
public:
state_block () = default;
state_block (rai::account const &, rai::block_hash const &, rai::account const &, rai::amount const &, rai::uint256_union const &, rai::raw_key const &, rai::public_key const &, uint64_t);
state_block (bool &, rai::stream &);
state_block (bool &, boost::property_tree::ptree const &);

View file

@ -6,6 +6,20 @@ std::string nano::error_common_messages::message (int ev) const
{
case nano::error_common::generic:
return "Unknown error";
case nano::error_common::missing_account:
return "Missing account";
case nano::error_common::missing_balance:
return "Missing balance";
case nano::error_common::missing_link:
return "Missing link, source or destination";
case nano::error_common::missing_previous:
return "Missing previous";
case nano::error_common::missing_representative:
return "Missing representative";
case nano::error_common::missing_signature:
return "Missing signature";
case nano::error_common::missing_work:
return "Missing work";
case nano::error_common::account_exists:
return "Account already exists";
case nano::error_common::account_not_found:
@ -14,6 +28,18 @@ std::string nano::error_common_messages::message (int ev) const
return "Account not found in wallet";
case nano::error_common::bad_account_number:
return "Bad account number";
case nano::error_common::bad_balance:
return "Bad balance";
case nano::error_common::bad_link:
return "Bad link value";
case nano::error_common::bad_previous:
return "Bad previous hash";
case nano::error_common::bad_representative_number:
return "Bad representative";
case nano::error_common::bad_source:
return "Bad source";
case nano::error_common::bad_signature:
return "Bad signature";
case nano::error_common::bad_private_key:
return "Bad private key";
case nano::error_common::bad_public_key:

View file

@ -32,12 +32,25 @@ enum class error_common
account_not_found_wallet,
account_exists,
bad_account_number,
bad_balance,
bad_link,
bad_previous,
bad_representative_number,
bad_source,
bad_signature,
bad_private_key,
bad_public_key,
bad_seed,
bad_threshold,
bad_wallet_number,
bad_work_format,
missing_account,
missing_balance,
missing_link,
missing_previous,
missing_representative,
missing_signature,
missing_work,
invalid_amount,
invalid_amount_big,
invalid_count,

View file

@ -310,6 +310,12 @@ rai::uint512_union::uint512_union (rai::uint512_t const & number_a)
}
}
bool rai::uint512_union::is_zero () const
{
return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0
&& qwords[4] == 0 && qwords[5] == 0 && qwords[6] == 0 && qwords[7] == 0;
}
void rai::uint512_union::clear ()
{
bytes.fill (0);

View file

@ -119,6 +119,7 @@ union uint512_union
std::array<uint64_t, 8> qwords;
std::array<uint256_union, 2> uint256s;
void clear ();
bool is_zero () const;
rai::uint512_t number () const;
std::string to_string () const;
};

View file

@ -1,5 +1,6 @@
#pragma once
#include <rai/lib/blockbuilders.hpp>
#include <rai/lib/blocks.hpp>
#include <rai/secure/utility.hpp>