diff --git a/CMakeLists.txt b/CMakeLists.txt index 56ce77fb..0100f416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -230,8 +230,18 @@ else () error ("Unknown platform: ${CMAKE_SYSTEM_NAME}") endif () +# Embed bootstrap representative weights in executable +file (READ rep_weights.bin filedata HEX) +string (REGEX REPLACE "(..)" "0x\\1," filedata ${filedata}) +file (WRITE ${CMAKE_BINARY_DIR}/bootstrap_weights.cpp "#include \n\ + namespace rai {\n\ + unsigned char rai_bootstrap_weights[] = {${filedata} 0x00};\n\ + size_t rai_bootstrap_weights_size = sizeof(rai_bootstrap_weights) - 1;\n\ + }\n") + add_library (secure ${PLATFORM_SECURE_SOURCE} + ${CMAKE_BINARY_DIR}/bootstrap_weights.cpp rai/config.hpp rai/secure.cpp rai/secure.hpp diff --git a/ci/record_rep_weights.py b/ci/record_rep_weights.py new file mode 100644 index 00000000..996b262b --- /dev/null +++ b/ci/record_rep_weights.py @@ -0,0 +1,63 @@ +import requests +import argparse +import string +from binascii import unhexlify +from base64 import b32decode +from binascii import hexlify, unhexlify + + +parser = argparse.ArgumentParser(description='Generate bootstrap representative weight file.') +parser.add_argument("output", type=str, help="output weight file") +parser.add_argument("--rpc", help="node rpc host:port", default="http://[::1]:7076") +parser.add_argument("--limit", help="percentage of the active supply represented", default=0.99) +parser.add_argument("--cutoff", help="stop using bootstrap reps this many blocks before the current block height", default=250000) +args = parser.parse_args() + +r = requests.post(args.rpc, data='{"action":"representatives"}') +p = r.json() + +reps = [ ] +tbl = string.maketrans('13456789abcdefghijkmnopqrstuwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') +for acc in p["representatives"]: + reps.append({ + 'account': acc, + 'weight': long(p["representatives"][acc]) + }) + +r = requests.post(args.rpc, data='{"action":"block_count"}') +p = r.json() +block_height = max(0, int(p["count"]) - args.cutoff) + +print "cutoff block height is %d" % block_height + +reps.sort(key=lambda x: x["weight"], reverse=True) + +supplymax = long(0) +for rep in reps: + supplymax += rep["weight"] + +supplymax /= long('1000000000000000000000000000000') +supplymax = long(supplymax * args.limit) +supplymax *= long('1000000000000000000000000000000') + +with open(args.output, 'wb') as of: + of.write(unhexlify("%032X" % block_height)) + + total = long(0) + count = 0 + for rep in reps: + if rep["weight"] == 0: + break + acc_val = long(hexlify(b32decode(rep["account"].encode("utf-8").replace("xrb_", "").translate(tbl) + "====")), 16) + acc_bytes = unhexlify("%064X" % (((acc_val >> 36) & ((1 << 256) - 1)))) + weight_bytes = unhexlify("%032X" % rep["weight"]) + of.write(acc_bytes) + of.write(weight_bytes) + total += rep["weight"] + count += 1 + if total >= supplymax: + break + + print "wrote %d rep weights" % count + + of.close() diff --git a/rai/core_test/ledger.cpp b/rai/core_test/ledger.cpp index 3451a858..967ba2c3 100644 --- a/rai/core_test/ledger.cpp +++ b/rai/core_test/ledger.cpp @@ -1431,3 +1431,38 @@ TEST (ledger, send_open_receive_rollback) ASSERT_EQ (0, ledger.weight (transaction, key3.pub)); ASSERT_EQ (rai::genesis_amount - 0, ledger.weight (transaction, rai::test_genesis_key.pub)); } + +TEST (ledger, bootstrap_rep_weight) +{ + bool init (false); + rai::block_store store (init, rai::unique_path ()); + ASSERT_TRUE (!init); + rai::ledger ledger (store, 40); + rai::account_info info1; + rai::keypair key2; + rai::genesis genesis; + { + rai::transaction transaction (store.environment, nullptr, true); + genesis.initialize (transaction, store); + ASSERT_FALSE (store.account_get (transaction, rai::test_genesis_key.pub, info1)); + rai::send_block send (info1.head, key2.pub, std::numeric_limits::max () - 50, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ledger.process (transaction, send); + } + { + rai::transaction transaction (store.environment, nullptr, false); + ledger.bootstrap_weight_max_blocks = 3; + ledger.bootstrap_weights[key2.pub] = 1000; + ASSERT_EQ (1000, ledger.weight (transaction, key2.pub)); + } + { + rai::transaction transaction (store.environment, nullptr, true); + genesis.initialize (transaction, store); + ASSERT_FALSE (store.account_get (transaction, rai::test_genesis_key.pub, info1)); + rai::send_block send (info1.head, key2.pub, std::numeric_limits::max () - 100, rai::test_genesis_key.prv, rai::test_genesis_key.pub, 0); + ledger.process (transaction, send); + } + { + rai::transaction transaction (store.environment, nullptr, false); + ASSERT_EQ (0, ledger.weight (transaction, key2.pub)); + } +} diff --git a/rai/node/node.cpp b/rai/node/node.cpp index f341bdb9..b51c0f3a 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -1525,6 +1525,37 @@ block_processor_thread ([this]() { this->block_processor.process_blocks (); }) genesis.initialize (transaction, store); } } + if (rai::rai_network == rai::rai_networks::rai_live_network) + { + extern const char rai_bootstrap_weights[]; + extern const size_t rai_bootstrap_weights_size; + rai::bufferstream weight_stream ((const uint8_t *)rai_bootstrap_weights, rai_bootstrap_weights_size); + rai::uint128_union block_height; + if (!rai::read (weight_stream, block_height)) + { + auto max_blocks = (uint64_t)block_height.number (); + rai::transaction transaction (store.environment, nullptr, false); + if (ledger.store.block_count (transaction).sum () < max_blocks) + { + ledger.bootstrap_weight_max_blocks = max_blocks; + while (true) + { + rai::account account; + if (rai::read (weight_stream, account.bytes)) + { + break; + } + rai::amount weight; + if (rai::read (weight_stream, weight.bytes)) + { + break; + } + BOOST_LOG (log) << "Using bootstrap rep weight: " << account.to_account () << " -> " << weight.format_balance (Mxrb_ratio, 0, true) << " XRB"; + ledger.bootstrap_weights[account] = weight.number (); + } + } + } + } } rai::node::~node () diff --git a/rai/secure.cpp b/rai/secure.cpp index af390937..216846a5 100644 --- a/rai/secure.cpp +++ b/rai/secure.cpp @@ -219,7 +219,8 @@ rai::keypair::keypair (std::string const & prv_a) rai::ledger::ledger (rai::block_store & store_a, rai::uint128_t const & inactive_supply_a) : store (store_a), -inactive_supply (inactive_supply_a) +inactive_supply (inactive_supply_a), +check_bootstrap_weights (true) { } @@ -2424,6 +2425,22 @@ std::string rai::ledger::block_text (rai::block_hash const & hash_a) // Vote weight of an account rai::uint128_t rai::ledger::weight (MDB_txn * transaction_a, rai::account const & account_a) { + if (check_bootstrap_weights.load ()) + { + auto blocks = store.block_count (transaction_a); + if (blocks.sum () < bootstrap_weight_max_blocks) + { + auto weight = bootstrap_weights.find (account_a); + if (weight != bootstrap_weights.end ()) + { + return weight->second; + } + } + else + { + check_bootstrap_weights = false; + } + } return store.representation_get (transaction_a, account_a); } diff --git a/rai/secure.hpp b/rai/secure.hpp index a37264ae..ca96e716 100644 --- a/rai/secure.hpp +++ b/rai/secure.hpp @@ -390,6 +390,9 @@ public: static rai::uint128_t const unit; rai::block_store & store; rai::uint128_t inactive_supply; + std::unordered_map bootstrap_weights; + uint64_t bootstrap_weight_max_blocks; + std::atomic check_bootstrap_weights; }; extern rai::keypair const & zero_key; extern rai::keypair const & test_genesis_key; diff --git a/rep_weights.bin b/rep_weights.bin new file mode 100644 index 00000000..daa9206e Binary files /dev/null and b/rep_weights.bin differ