From 1057d50428401d21aca70cd33404fcecebcf3bb0 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 Jan 2024 12:47:59 -0600 Subject: [PATCH 1/5] GH-2033 Port over some reference contract tests that provide coverage not found in existing unittests. Most of these are likely redundant, but change_inflation, votepay_share_invariant, and votepay_transition2 triggered a producer_double-confirming error no other tests found. --- unittests/eosio.system_tests.cpp | 2847 +++++++++++++++++++++++++++++ unittests/eosio_system_tester.hpp | 92 + 2 files changed, 2939 insertions(+) create mode 100644 unittests/eosio.system_tests.cpp diff --git a/unittests/eosio.system_tests.cpp b/unittests/eosio.system_tests.cpp new file mode 100644 index 0000000000..a5d5f0ebb0 --- /dev/null +++ b/unittests/eosio.system_tests.cpp @@ -0,0 +1,2847 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eosio_system_tester.hpp" + +struct _abi_hash { + eosio::chain::name owner; + fc::sha256 hash; +}; +FC_REFLECT( _abi_hash, (owner)(hash) ); + +struct connector { + asset balance; + double weight = .5; +}; +FC_REFLECT( connector, (balance)(weight) ); + +using namespace eosio_system; + +BOOST_AUTO_TEST_SUITE(eosio_system_tests) + +bool within_error(int64_t a, int64_t b, int64_t err) { return std::abs(a - b) <= err; }; +bool within_one(int64_t a, int64_t b) { return within_error(a, b, 1); } + +BOOST_FIXTURE_TEST_CASE( buysell, eosio_system_tester ) try { + using namespace eosio::chain; + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "eosio"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + auto total = get_total_stake( "alice1111111"_n ); + auto init_bytes = total["ram_bytes"].as_uint64(); + + const asset initial_ram_balance = get_balance("eosio.ram"_n); + const asset initial_ramfee_balance = get_balance("eosio.ramfee"_n); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("800.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( initial_ram_balance + core_from_string("199.0000"), get_balance("eosio.ram"_n) ); + BOOST_REQUIRE_EQUAL( initial_ramfee_balance + core_from_string("1.0000"), get_balance("eosio.ramfee"_n) ); + + total = get_total_stake( "alice1111111"_n ); + auto bytes = total["ram_bytes"].as_uint64(); + auto bought_bytes = bytes - init_bytes; + wdump((init_bytes)(bought_bytes)(bytes) ); + + BOOST_REQUIRE_EQUAL( true, 0 < bought_bytes ); + + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, bought_bytes ) ); + BOOST_REQUIRE_EQUAL( core_from_string("998.0049"), get_balance( "alice1111111"_n ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( true, total["ram_bytes"].as_uint64() == init_bytes ); + + transfer( "eosio"_n, "alice1111111"_n, core_from_string("100000000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("100000998.0049"), get_balance( "alice1111111"_n ) ); + // alice buys ram for 10000000.0000, 0.5% = 50000.0000 go to ramfee + // after fee 9950000.0000 go to bought bytes + // when selling back bought bytes, pay 0.5% fee and get back 99.5% of 9950000.0000 = 9900250.0000 + // expected account after that is 90000998.0049 + 9900250.0000 = 99901248.0049 with a difference + // of order 0.0001 due to rounding errors + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("90000998.0049"), get_balance( "alice1111111"_n ) ); + + total = get_total_stake( "alice1111111"_n ); + bytes = total["ram_bytes"].as_uint64(); + bought_bytes = bytes - init_bytes; + wdump((init_bytes)(bought_bytes)(bytes) ); + + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, bought_bytes ) ); + total = get_total_stake( "alice1111111"_n ); + + bytes = total["ram_bytes"].as_uint64(); + bought_bytes = bytes - init_bytes; + wdump((init_bytes)(bought_bytes)(bytes) ); + + BOOST_REQUIRE_EQUAL( true, total["ram_bytes"].as_uint64() == init_bytes ); + BOOST_REQUIRE_EQUAL( core_from_string("99901248.0048"), get_balance( "alice1111111"_n ) ); + + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("30.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("99900688.0048"), get_balance( "alice1111111"_n ) ); + + auto newtotal = get_total_stake( "alice1111111"_n ); + + auto newbytes = newtotal["ram_bytes"].as_uint64(); + bought_bytes = newbytes - bytes; + wdump((newbytes)(bytes)(bought_bytes) ); + + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, bought_bytes ) ); + BOOST_REQUIRE_EQUAL( core_from_string("99901242.4187"), get_balance( "alice1111111"_n ) ); + + newtotal = get_total_stake( "alice1111111"_n ); + auto startbytes = newtotal["ram_bytes"].as_uint64(); + + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("10000000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("300000.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("49301242.4187"), get_balance( "alice1111111"_n ) ); + + auto finaltotal = get_total_stake( "alice1111111"_n ); + auto endbytes = finaltotal["ram_bytes"].as_uint64(); + + bought_bytes = endbytes - startbytes; + wdump((startbytes)(endbytes)(bought_bytes) ); + + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, bought_bytes ) ); + + BOOST_REQUIRE_EQUAL( false, get_row_by_account( config::system_account_name, config::system_account_name, + "rammarket"_n, account_name(symbol{SY(4,RAMCORE)}.value()) ).empty() ); + + auto get_ram_market = [this]() -> fc::variant { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, + "rammarket"_n, account_name(symbol{SY(4,RAMCORE)}.value()) ); + BOOST_REQUIRE( !data.empty() ); + return abi_ser.binary_to_variant("exchange_state", data, abi_serializer::create_yield_function(abi_serializer_max_time)); + }; + + { + transfer( config::system_account_name, "alice1111111"_n, core_from_string("10000000.0000"), config::system_account_name ); + uint64_t bytes0 = get_total_stake( "alice1111111"_n )["ram_bytes"].as_uint64(); + + auto market = get_ram_market(); + const asset r0 = market["base"].as().balance; + const asset e0 = market["quote"].as().balance; + BOOST_REQUIRE_EQUAL( asset::from_string("0 RAM").get_symbol(), r0.get_symbol() ); + BOOST_REQUIRE_EQUAL( core_from_string("0.0000").get_symbol(), e0.get_symbol() ); + + const asset payment = core_from_string("10000000.0000"); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, payment ) ); + uint64_t bytes1 = get_total_stake( "alice1111111"_n )["ram_bytes"].as_uint64(); + + const int64_t fee = (payment.get_amount() + 199) / 200; + const double net_payment = payment.get_amount() - fee; + const int64_t expected_delta = net_payment * r0.get_amount() / ( net_payment + e0.get_amount() ); + + BOOST_REQUIRE_EQUAL( expected_delta, bytes1 - bytes0 ); + } + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( stake_unstake, eosio_system_tester ) try { + cross_15_percent_threshold(); + + produce_blocks( 10 ); + produce_block( fc::hours(3*24) ); + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( success(), stake( "eosio"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + auto total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + + const auto init_eosio_stake_balance = get_balance( "eosio.stake"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( init_eosio_stake_balance + core_from_string("300.0000"), get_balance( "eosio.stake"_n ) ); + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + + produce_block( fc::hours(3*24-1) ); + produce_blocks(1); + // testing balance still the same + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( init_eosio_stake_balance + core_from_string("300.0000"), get_balance( "eosio.stake"_n ) ); + // call refund expected to fail too early + BOOST_REQUIRE_EQUAL( wasm_assert_msg("refund is not available yet"), + push_action( "alice1111111"_n, "refund"_n, mvo()("owner", "alice1111111"_n) ) ); + + // after 1 hour refund ready + produce_block( fc::hours(1) ); + produce_blocks(1); + // now we can do the refund + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "refund"_n, mvo()("owner", "alice1111111"_n) ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( init_eosio_stake_balance, get_balance( "eosio.stake"_n ) ); + + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + total = get_total_stake("bob111111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000").get_amount(), total["net_weight"].as().get_amount() ); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000").get_amount(), total["cpu_weight"].as().get_amount() ); + + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000")), get_voter_info( "alice1111111"_n ) ); + + auto bytes = total["ram_bytes"].as_uint64(); + BOOST_REQUIRE_EQUAL( true, 0 < bytes ); + + //unstake from bob111111111 + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + total = get_total_stake("bob111111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["cpu_weight"].as()); + produce_block( fc::hours(3*24-1) ); + produce_blocks(1); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + //after 3 days funds should be released + produce_block( fc::hours(1) ); + produce_blocks(1); + + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("0.0000") ), get_voter_info( "alice1111111"_n ) ); + produce_blocks(1); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "refund"_n, mvo()("owner", "alice1111111"_n) ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance( "alice1111111"_n ) ); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( stake_unstake_with_transfer, eosio_system_tester ) try { + cross_15_percent_threshold(); + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + + //eosio stakes for alice with transfer flag + + transfer( "eosio"_n, "bob111111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake_with_transfer( "bob111111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + //check that alice has both bandwidth and voting power + auto total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000")), get_voter_info( "alice1111111"_n ) ); + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + + //alice stakes for herself + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + //now alice's stake should be equal to transferred from eosio + own stake + total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("410.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("600.0000")), get_voter_info( "alice1111111"_n ) ); + + //alice can unstake everything (including what was transferred) + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "alice1111111"_n, core_from_string("400.0000"), core_from_string("200.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + + produce_block( fc::hours(3*24-1) ); + produce_blocks(1); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + //after 3 days funds should be released + + produce_block( fc::hours(1) ); + produce_blocks(1); + + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "refund"_n, mvo()("owner", "alice1111111"_n) ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1300.0000"), get_balance( "alice1111111"_n ) ); + + //stake should be equal to what was staked in constructor, voting power should be 0 + total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("0.0000")), get_voter_info( "alice1111111"_n ) ); + + // Now alice stakes to bob with transfer flag + BOOST_REQUIRE_EQUAL( success(), stake_with_transfer( "alice1111111"_n, "bob111111111"_n, core_from_string("100.0000"), core_from_string("100.0000") ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( stake_to_self_with_transfer, eosio_system_tester ) try { + cross_15_percent_threshold(); + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("cannot use transfer flag if delegating to self"), + stake_with_transfer( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) + ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( stake_while_pending_refund, eosio_system_tester ) try { + cross_15_percent_threshold(); + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + + //eosio stakes for alice with transfer flag + transfer( "eosio"_n, "bob111111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake_with_transfer( "bob111111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + //check that alice has both bandwidth and voting power + auto total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000")), get_voter_info( "alice1111111"_n ) ); + + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + + //alice stakes for herself + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + //now alice's stake should be equal to transferred from eosio + own stake + total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("410.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("600.0000")), get_voter_info( "alice1111111"_n ) ); + + //alice can unstake everything (including what was transferred) + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "alice1111111"_n, core_from_string("400.0000"), core_from_string("200.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + + produce_block( fc::hours(3*24-1) ); + produce_blocks(1); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + //after 3 days funds should be released + + produce_block( fc::hours(1) ); + produce_blocks(1); + + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "refund"_n, mvo()("owner", "alice1111111"_n) ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1300.0000"), get_balance( "alice1111111"_n ) ); + + //stake should be equal to what was staked in constructor, voting power should be 0 + total = get_total_stake("alice1111111"_n); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("0.0000")), get_voter_info( "alice1111111"_n ) ); + + // Now alice stakes to bob with transfer flag + BOOST_REQUIRE_EQUAL( success(), stake_with_transfer( "alice1111111"_n, "bob111111111"_n, core_from_string("100.0000"), core_from_string("100.0000") ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( fail_without_auth, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + + BOOST_REQUIRE_EQUAL( success(), stake( "eosio"_n, "alice1111111"_n, core_from_string("2000.0000"), core_from_string("1000.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("10.0000"), core_from_string("10.0000") ) ); + + BOOST_REQUIRE_EQUAL( error("missing authority of alice1111111"), + push_action( "alice1111111"_n, "delegatebw"_n, mvo() + ("from", "alice1111111"_n) + ("receiver", "bob111111111"_n) + ("stake_net_quantity", core_from_string("10.0000")) + ("stake_cpu_quantity", core_from_string("10.0000")) + ("transfer", 0 ) + ,false + ) + ); + + BOOST_REQUIRE_EQUAL( error("missing authority of alice1111111"), + push_action("alice1111111"_n, "undelegatebw"_n, mvo() + ("from", "alice1111111"_n) + ("receiver", "bob111111111"_n) + ("unstake_net_quantity", core_from_string("200.0000")) + ("unstake_cpu_quantity", core_from_string("100.0000")) + ("transfer", 0 ) + ,false + ) + ); + //REQUIRE_MATCHING_OBJECT( , get_voter_info( "alice1111111"_n ) ); +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( stake_negative, eosio_system_tester ) try { + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must stake a positive amount"), + stake( "alice1111111"_n, core_from_string("-0.0001"), core_from_string("0.0000") ) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must stake a positive amount"), + stake( "alice1111111"_n, core_from_string("0.0000"), core_from_string("-0.0001") ) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must stake a positive amount"), + stake( "alice1111111"_n, core_from_string("00.0000"), core_from_string("00.0000") ) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must stake a positive amount"), + stake( "alice1111111"_n, core_from_string("0.0000"), core_from_string("00.0000") ) + + ); + + BOOST_REQUIRE_EQUAL( true, get_voter_info( "alice1111111"_n ).is_null() ); +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( unstake_negative, eosio_system_tester ) try { + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("200.0001"), core_from_string("100.0001") ) ); + + auto total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0001"), total["net_weight"].as()); + auto vinfo = get_voter_info("alice1111111"_n ); + wdump((vinfo)); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0002") ), get_voter_info( "alice1111111"_n ) ); + + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must unstake a positive amount"), + unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("-1.0000"), core_from_string("0.0000") ) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must unstake a positive amount"), + unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("0.0000"), core_from_string("-1.0000") ) + ); + + //unstake all zeros + BOOST_REQUIRE_EQUAL( wasm_assert_msg("must unstake a positive amount"), + unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("0.0000"), core_from_string("0.0000") ) + + ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( unstake_more_than_at_stake, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + auto total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + + //trying to unstake more net bandwidth than at stake + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient staked net bandwidth"), + unstake( "alice1111111"_n, core_from_string("200.0001"), core_from_string("0.0000") ) + ); + + //trying to unstake more cpu bandwidth than at stake + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient staked cpu bandwidth"), + unstake( "alice1111111"_n, core_from_string("0.0000"), core_from_string("100.0001") ) + + ); + + //check that nothing has changed + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( delegate_to_another_user, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + + BOOST_REQUIRE_EQUAL( success(), stake ( "alice1111111"_n, "bob111111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + auto total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + //all voting power goes to alice1111111 + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000") ), get_voter_info( "alice1111111"_n ) ); + //but not to bob111111111 + BOOST_REQUIRE_EQUAL( true, get_voter_info( "bob111111111"_n ).is_null() ); + + //bob111111111 should not be able to unstake what was staked by alice1111111 + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient staked cpu bandwidth"), + unstake( "bob111111111"_n, core_from_string("0.0000"), core_from_string("10.0000") ) + + ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient staked net bandwidth"), + unstake( "bob111111111"_n, core_from_string("10.0000"), core_from_string("0.0000") ) + ); + + issue_and_transfer( "carol1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, "bob111111111"_n, core_from_string("20.0000"), core_from_string("10.0000") ) ); + total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("230.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("120.0000"), total["cpu_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("970.0000"), get_balance( "carol1111111"_n ) ); + REQUIRE_MATCHING_OBJECT( voter( "carol1111111"_n, core_from_string("30.0000") ), get_voter_info( "carol1111111"_n ) ); + + //alice1111111 should not be able to unstake money staked by carol1111111 + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient staked net bandwidth"), + unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("2001.0000"), core_from_string("1.0000") ) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg("insufficient staked cpu bandwidth"), + unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("1.0000"), core_from_string("101.0000") ) + + ); + + total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("230.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("120.0000"), total["cpu_weight"].as()); + //balance should not change after unsuccessful attempts to unstake + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + //voting power too + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000") ), get_voter_info( "alice1111111"_n ) ); + REQUIRE_MATCHING_OBJECT( voter( "carol1111111"_n, core_from_string("30.0000") ), get_voter_info( "carol1111111"_n ) ); + BOOST_REQUIRE_EQUAL( true, get_voter_info( "bob111111111"_n ).is_null() ); +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( stake_unstake_separate, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance( "alice1111111"_n ) ); + + //everything at once + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("10.0000"), core_from_string("20.0000") ) ); + auto total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("20.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("30.0000"), total["cpu_weight"].as()); + + //cpu + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("100.0000"), core_from_string("0.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("120.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("30.0000"), total["cpu_weight"].as()); + + //net + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("0.0000"), core_from_string("200.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("120.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("230.0000"), total["cpu_weight"].as()); + + //unstake cpu + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, core_from_string("100.0000"), core_from_string("0.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("20.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("230.0000"), total["cpu_weight"].as()); + + //unstake net + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, core_from_string("0.0000"), core_from_string("200.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("20.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("30.0000"), total["cpu_weight"].as()); +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( adding_stake_partial_unstake, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000") ), get_voter_info( "alice1111111"_n ) ); + + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("100.0000"), core_from_string("50.0000") ) ); + + auto total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("310.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("160.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("450.0000") ), get_voter_info( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("550.0000"), get_balance( "alice1111111"_n ) ); + + //unstake a share + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("150.0000"), core_from_string("75.0000") ) ); + + total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("160.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("85.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("225.0000") ), get_voter_info( "alice1111111"_n ) ); + + //unstake more + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "bob111111111"_n, core_from_string("50.0000"), core_from_string("25.0000") ) ); + total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("60.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("150.0000") ), get_voter_info( "alice1111111"_n ) ); + + //combined amount should be available only in 3 days + produce_block( fc::days(2) ); + produce_blocks(1); + BOOST_REQUIRE_EQUAL( core_from_string("550.0000"), get_balance( "alice1111111"_n ) ); + produce_block( fc::days(1) ); + produce_blocks(1); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "refund"_n, mvo()("owner", "alice1111111"_n) ) ); + BOOST_REQUIRE_EQUAL( core_from_string("850.0000"), get_balance( "alice1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( stake_from_refund, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + auto total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("50.0000"), core_from_string("50.0000") ) ); + + total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("60.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("60.0000"), total["cpu_weight"].as()); + + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("400.0000") ), get_voter_info( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("600.0000"), get_balance( "alice1111111"_n ) ); + + //unstake a share + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000"), core_from_string("50.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("60.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("250.0000") ), get_voter_info( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("600.0000"), get_balance( "alice1111111"_n ) ); + auto refund = get_refund_request( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("100.0000"), refund["net_amount"].as() ); + BOOST_REQUIRE_EQUAL( core_from_string( "50.0000"), refund["cpu_amount"].as() ); + //XXX auto request_time = refund["request_time"].as_int64(); + + //alice delegates to bob, should pull from liquid balance not refund + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("50.0000"), core_from_string("50.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("60.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("350.0000") ), get_voter_info( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("500.0000"), get_balance( "alice1111111"_n ) ); + refund = get_refund_request( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("100.0000"), refund["net_amount"].as() ); + BOOST_REQUIRE_EQUAL( core_from_string( "50.0000"), refund["cpu_amount"].as() ); + + //stake less than pending refund, entire amount should be taken from refund + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("50.0000"), core_from_string("25.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("160.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("85.0000"), total["cpu_weight"].as()); + refund = get_refund_request( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("50.0000"), refund["net_amount"].as() ); + BOOST_REQUIRE_EQUAL( core_from_string("25.0000"), refund["cpu_amount"].as() ); + //request time should stay the same + //BOOST_REQUIRE_EQUAL( request_time, refund["request_time"].as_int64() ); + //balance should stay the same + BOOST_REQUIRE_EQUAL( core_from_string("500.0000"), get_balance( "alice1111111"_n ) ); + + //stake exactly pending refund amount + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("50.0000"), core_from_string("25.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + //pending refund should be removed + refund = get_refund_request( "alice1111111"_n ); + BOOST_TEST_REQUIRE( refund.is_null() ); + //balance should stay the same + BOOST_REQUIRE_EQUAL( core_from_string("500.0000"), get_balance( "alice1111111"_n ) ); + + //create pending refund again + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("10.0000"), total["cpu_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("500.0000"), get_balance( "alice1111111"_n ) ); + refund = get_refund_request( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("200.0000"), refund["net_amount"].as() ); + BOOST_REQUIRE_EQUAL( core_from_string("100.0000"), refund["cpu_amount"].as() ); + + //stake more than pending refund + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("300.0000"), core_from_string("200.0000") ) ); + total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("310.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["cpu_weight"].as()); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("700.0000") ), get_voter_info( "alice1111111"_n ) ); + refund = get_refund_request( "alice1111111"_n ); + BOOST_TEST_REQUIRE( refund.is_null() ); + //200 core tokens should be taken from alice's account + BOOST_REQUIRE_EQUAL( core_from_string("300.0000"), get_balance( "alice1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( stake_to_another_user_not_from_refund, eosio_system_tester ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + auto total = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n, core_from_string("300.0000") ), get_voter_info( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance( "alice1111111"_n ) ); + + //unstake + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + auto refund = get_refund_request( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("200.0000"), refund["net_amount"].as() ); + BOOST_REQUIRE_EQUAL( core_from_string("100.0000"), refund["cpu_amount"].as() ); + //auto orig_request_time = refund["request_time"].as_int64(); + + //stake to another user + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "bob111111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + total = get_total_stake( "bob111111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("210.0000"), total["net_weight"].as()); + BOOST_REQUIRE_EQUAL( core_from_string("110.0000"), total["cpu_weight"].as()); + //stake should be taken from alice's balance, and refund request should stay the same + BOOST_REQUIRE_EQUAL( core_from_string("400.0000"), get_balance( "alice1111111"_n ) ); + refund = get_refund_request( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( core_from_string("200.0000"), refund["net_amount"].as() ); + BOOST_REQUIRE_EQUAL( core_from_string("100.0000"), refund["cpu_amount"].as() ); + //BOOST_REQUIRE_EQUAL( orig_request_time, refund["request_time"].as_int64() ); + +} FC_LOG_AND_RETHROW() + +// Tests for voting +BOOST_FIXTURE_TEST_CASE( producer_register_unregister, eosio_system_tester ) try { + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + + //fc::variant params = producer_parameters_example(1); + auto key = fc::crypto::public_key( std::string("EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV") ); // cspell:disable-line + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", key ) + ("url", "http://block.one") + ("location", 1) + ) + ); + + auto info = get_producer_info( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() ); + BOOST_REQUIRE_EQUAL( 0, info["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( "http://block.one", info["url"].as_string() ); + + //change parameters one by one to check for things like #3783 + //fc::variant params2 = producer_parameters_example(2); + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", key ) + ("url", "http://block.two") + ("location", 1) + ) + ); + info = get_producer_info( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() ); + BOOST_REQUIRE_EQUAL( key, fc::crypto::public_key(info["producer_key"].as_string()) ); + BOOST_REQUIRE_EQUAL( "http://block.two", info["url"].as_string() ); + BOOST_REQUIRE_EQUAL( 1, info["location"].as_int64() ); + + auto key2 = fc::crypto::public_key( std::string("EOS5jnmSKrzdBHE9n8hw58y7yxFWBC8SNiG7m8S1crJH3KvAnf9o6") ); // cspell:disable-line + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", key2 ) + ("url", "http://block.two") + ("location", 2) + ) + ); + info = get_producer_info( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() ); + BOOST_REQUIRE_EQUAL( key2, fc::crypto::public_key(info["producer_key"].as_string()) ); + BOOST_REQUIRE_EQUAL( "http://block.two", info["url"].as_string() ); + BOOST_REQUIRE_EQUAL( 2, info["location"].as_int64() ); + + //unregister producer + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "unregprod"_n, mvo() + ("producer", "alice1111111"_n) + ) + ); + info = get_producer_info( "alice1111111"_n ); + //key should be empty + BOOST_REQUIRE_EQUAL( fc::crypto::public_key(), fc::crypto::public_key(info["producer_key"].as_string()) ); + //everything else should stay the same + BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() ); + BOOST_REQUIRE_EQUAL( 0, info["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( "http://block.two", info["url"].as_string() ); + + //unregister bob111111111 who is not a producer + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "producer not found" ), + push_action( "bob111111111"_n, "unregprod"_n, mvo() + ("producer", "bob111111111"_n) + ) + ); + +} FC_LOG_AND_RETHROW() + + + +BOOST_FIXTURE_TEST_CASE( vote_for_producer, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try { + cross_15_percent_threshold(); + + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + fc::variant params = producer_parameters_example(1); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url", "http://block.one") + ("location", 0 ) + ) + ); + auto prod = get_producer_info( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( "alice1111111", prod["owner"].as_string() ); + BOOST_REQUIRE_EQUAL( 0, prod["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( "http://block.one", prod["url"].as_string() ); + + issue_and_transfer( "bob111111111"_n, core_from_string("2000.0000"), config::system_account_name ); + issue_and_transfer( "carol1111111"_n, core_from_string("3000.0000"), config::system_account_name ); + + //bob111111111 makes stake + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("11.0000"), core_from_string("0.1111") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("1988.8889"), get_balance( "bob111111111"_n ) ); + REQUIRE_MATCHING_OBJECT( voter( "bob111111111"_n, core_from_string("11.1111") ), get_voter_info( "bob111111111"_n ) ); + + //bob111111111 votes for alice1111111 + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, { "alice1111111"_n } ) ); + + //check that producer parameters stay the same after voting + prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("11.1111")) == prod["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( "alice1111111", prod["owner"].as_string() ); + BOOST_REQUIRE_EQUAL( "http://block.one", prod["url"].as_string() ); + + //carol1111111 makes stake + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, core_from_string("22.0000"), core_from_string("0.2222") ) ); + REQUIRE_MATCHING_OBJECT( voter( "carol1111111"_n, core_from_string("22.2222") ), get_voter_info( "carol1111111"_n ) ); + BOOST_REQUIRE_EQUAL( core_from_string("2977.7778"), get_balance( "carol1111111"_n ) ); + //carol1111111 votes for alice1111111 + BOOST_REQUIRE_EQUAL( success(), vote( "carol1111111"_n, { "alice1111111"_n } ) ); + + //new stake votes be added to alice1111111's total_votes + prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("33.3333")) == prod["total_votes"].as_double() ); + + //bob111111111 increases his stake + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("33.0000"), core_from_string("0.3333") ) ); + //alice1111111 stake with transfer to bob111111111 + BOOST_REQUIRE_EQUAL( success(), stake_with_transfer( "alice1111111"_n, "bob111111111"_n, core_from_string("22.0000"), core_from_string("0.2222") ) ); + //should increase alice1111111's total_votes + prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("88.8888")) == prod["total_votes"].as_double() ); + + //carol1111111 unstakes part of the stake + BOOST_REQUIRE_EQUAL( success(), unstake( "carol1111111"_n, core_from_string("2.0000"), core_from_string("0.0002")/*"2.0000 EOS", "0.0002 EOS"*/ ) ); + + //should decrease alice1111111's total_votes + prod = get_producer_info( "alice1111111"_n ); + wdump((prod)); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("86.8886")) == prod["total_votes"].as_double() ); + + //bob111111111 revokes his vote + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, vector() ) ); + + //should decrease alice1111111's total_votes + prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("20.2220")) == prod["total_votes"].as_double() ); + //but eos should still be at stake + BOOST_REQUIRE_EQUAL( core_from_string("1955.5556"), get_balance( "bob111111111"_n ) ); + + //carol1111111 unstakes rest of eos + BOOST_REQUIRE_EQUAL( success(), unstake( "carol1111111"_n, core_from_string("20.0000"), core_from_string("0.2220") ) ); + //should decrease alice1111111's total_votes to zero + prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( 0.0 == prod["total_votes"].as_double() ); + + //carol1111111 should receive funds in 3 days + produce_block( fc::days(3) ); + produce_block(); + + // do a bid refund for carol + BOOST_REQUIRE_EQUAL( success(), push_action( "carol1111111"_n, "refund"_n, mvo()("owner", "carol1111111"_n) ) ); + BOOST_REQUIRE_EQUAL( core_from_string("3000.0000"), get_balance( "carol1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( unregistered_producer_voting, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try { + issue_and_transfer( "bob111111111"_n, core_from_string("2000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("13.0000"), core_from_string("0.5791") ) ); + REQUIRE_MATCHING_OBJECT( voter( "bob111111111"_n, core_from_string("13.5791") ), get_voter_info( "bob111111111"_n ) ); + + //bob111111111 should not be able to vote for alice1111111 who is not a producer + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "producer is not registered" ), + vote( "bob111111111"_n, { "alice1111111"_n } ) ); + + //alice1111111 registers as a producer + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + fc::variant params = producer_parameters_example(1); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url", "") + ("location", 0) + ) + ); + //and then unregisters + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "unregprod"_n, mvo() + ("producer", "alice1111111"_n) + ) + ); + //key should be empty + auto prod = get_producer_info( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( fc::crypto::public_key(), fc::crypto::public_key(prod["producer_key"].as_string()) ); + + //bob111111111 should not be able to vote for alice1111111 who is an unregistered producer + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "producer is not currently registered" ), + vote( "bob111111111"_n, { "alice1111111"_n } ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( more_than_30_producer_voting, eosio_system_tester ) try { + issue_and_transfer( "bob111111111"_n, core_from_string("2000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("13.0000"), core_from_string("0.5791") ) ); + REQUIRE_MATCHING_OBJECT( voter( "bob111111111"_n, core_from_string("13.5791") ), get_voter_info( "bob111111111"_n ) ); + + //bob111111111 should not be able to vote for alice1111111 who is not a producer + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "attempt to vote for too many producers" ), + vote( "bob111111111"_n, vector(31, "alice1111111"_n) ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( vote_same_producer_30_times, eosio_system_tester ) try { + issue_and_transfer( "bob111111111"_n, core_from_string("2000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("50.0000"), core_from_string("50.0000") ) ); + REQUIRE_MATCHING_OBJECT( voter( "bob111111111"_n, core_from_string("100.0000") ), get_voter_info( "bob111111111"_n ) ); + + //alice1111111 becomes a producer + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + fc::variant params = producer_parameters_example(1); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key("alice1111111"_n, "active") ) + ("url", "") + ("location", 0) + ) + ); + + //bob111111111 should not be able to vote for alice1111111 who is not a producer + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "producer votes must be unique and sorted" ), + vote( "bob111111111"_n, vector(30, "alice1111111"_n) ) ); + + auto prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( 0 == prod["total_votes"].as_double() ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( producer_keep_votes, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try { + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + fc::variant params = producer_parameters_example(1); + vector key = fc::raw::pack( get_public_key( "alice1111111"_n, "active" ) ); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url", "") + ("location", 0) + ) + ); + + //bob111111111 makes stake + issue_and_transfer( "bob111111111"_n, core_from_string("2000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("13.0000"), core_from_string("0.5791") ) ); + REQUIRE_MATCHING_OBJECT( voter( "bob111111111"_n, core_from_string("13.5791") ), get_voter_info( "bob111111111"_n ) ); + + //bob111111111 votes for alice1111111 + BOOST_REQUIRE_EQUAL( success(), vote("bob111111111"_n, { "alice1111111"_n } ) ); + + auto prod = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("13.5791")) == prod["total_votes"].as_double() ); + + //unregister producer + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "unregprod"_n, mvo() + ("producer", "alice1111111"_n) + ) + ); + prod = get_producer_info( "alice1111111"_n ); + //key should be empty + BOOST_REQUIRE_EQUAL( fc::crypto::public_key(), fc::crypto::public_key(prod["producer_key"].as_string()) ); + //check parameters just in case + //REQUIRE_MATCHING_OBJECT( params, prod["prefs"]); + //votes should stay the same + BOOST_TEST_REQUIRE( stake2votes(core_from_string("13.5791")), prod["total_votes"].as_double() ); + + //regtister the same producer again + params = producer_parameters_example(2); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url", "") + ("location", 0) + ) + ); + prod = get_producer_info( "alice1111111"_n ); + //votes should stay the same + BOOST_TEST_REQUIRE( stake2votes(core_from_string("13.5791")), prod["total_votes"].as_double() ); + + //change parameters + params = producer_parameters_example(3); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url","") + ("location", 0) + ) + ); + prod = get_producer_info( "alice1111111"_n ); + //votes should stay the same + BOOST_TEST_REQUIRE( stake2votes(core_from_string("13.5791")), prod["total_votes"].as_double() ); + //check parameters just in case + //REQUIRE_MATCHING_OBJECT( params, prod["prefs"]); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( vote_for_two_producers, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try { + //alice1111111 becomes a producer + fc::variant params = producer_parameters_example(1); + auto key = get_public_key( "alice1111111"_n, "active" ); + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproducer"_n, mvo() + ("producer", "alice1111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url","") + ("location", 0) + ) + ); + //bob111111111 becomes a producer + params = producer_parameters_example(2); + key = get_public_key( "bob111111111"_n, "active" ); + BOOST_REQUIRE_EQUAL( success(), push_action( "bob111111111"_n, "regproducer"_n, mvo() + ("producer", "bob111111111"_n) + ("producer_key", get_public_key( "alice1111111"_n, "active") ) + ("url","") + ("location", 0) + ) + ); + + //carol1111111 votes for alice1111111 and bob111111111 + issue_and_transfer( "carol1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, core_from_string("15.0005"), core_from_string("5.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "carol1111111"_n, { "alice1111111"_n, "bob111111111"_n } ) ); + + auto alice_info = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("20.0005")) == alice_info["total_votes"].as_double() ); + auto bob_info = get_producer_info( "bob111111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("20.0005")) == bob_info["total_votes"].as_double() ); + + //carol1111111 votes for alice1111111 (but revokes vote for bob111111111) + BOOST_REQUIRE_EQUAL( success(), vote( "carol1111111"_n, { "alice1111111"_n } ) ); + + alice_info = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("20.0005")) == alice_info["total_votes"].as_double() ); + bob_info = get_producer_info( "bob111111111"_n ); + BOOST_TEST_REQUIRE( 0 == bob_info["total_votes"].as_double() ); + + //alice1111111 votes for herself and bob111111111 + issue_and_transfer( "alice1111111"_n, core_from_string("2.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("1.0000"), core_from_string("1.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), vote("alice1111111"_n, { "alice1111111"_n, "bob111111111"_n } ) ); + + alice_info = get_producer_info( "alice1111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("22.0005")) == alice_info["total_votes"].as_double() ); + + bob_info = get_producer_info( "bob111111111"_n ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("2.0000")) == bob_info["total_votes"].as_double() ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( proxy_register_unregister_keeps_stake, eosio_system_tester ) try { + //register proxy by first action for this user ever + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", true ) + ) + ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n ), get_voter_info( "alice1111111"_n ) ); + + //unregister proxy + BOOST_REQUIRE_EQUAL( success(), push_action("alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", false) + ) + ); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n ), get_voter_info( "alice1111111"_n ) ); + + //stake and then register as a proxy + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("200.0002"), core_from_string("100.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), push_action( "bob111111111"_n, "regproxy"_n, mvo() + ("proxy", "bob111111111"_n) + ("isproxy", true) + ) + ); + REQUIRE_MATCHING_OBJECT( proxy( "bob111111111"_n )( "staked", 3000003 ), get_voter_info( "bob111111111"_n ) ); + //unrgister and check that stake is still in place + BOOST_REQUIRE_EQUAL( success(), push_action( "bob111111111"_n, "regproxy"_n, mvo() + ("proxy", "bob111111111"_n) + ("isproxy", false) + ) + ); + REQUIRE_MATCHING_OBJECT( voter( "bob111111111"_n, core_from_string("300.0003") ), get_voter_info( "bob111111111"_n ) ); + + //register as a proxy and then stake + BOOST_REQUIRE_EQUAL( success(), push_action( "carol1111111"_n, "regproxy"_n, mvo() + ("proxy", "carol1111111"_n) + ("isproxy", true) + ) + ); + issue_and_transfer( "carol1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, core_from_string("246.0002"), core_from_string("531.0001") ) ); + //check that both proxy flag and stake a correct + REQUIRE_MATCHING_OBJECT( proxy( "carol1111111"_n )( "staked", 7770003 ), get_voter_info( "carol1111111"_n ) ); + + //unregister + BOOST_REQUIRE_EQUAL( success(), push_action( "carol1111111"_n, "regproxy"_n, mvo() + ("proxy", "carol1111111"_n) + ("isproxy", false) + ) + ); + REQUIRE_MATCHING_OBJECT( voter( "carol1111111"_n, core_from_string("777.0003") ), get_voter_info( "carol1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( proxy_stake_unstake_keeps_proxy_flag, eosio_system_tester ) try { + cross_15_percent_threshold(); + + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", true) + ) + ); + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n ), get_voter_info( "alice1111111"_n ) ); + + //stake + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("100.0000"), core_from_string("50.0000") ) ); + //check that account is still a proxy + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )( "staked", 1500000 ), get_voter_info( "alice1111111"_n ) ); + + //stake more + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("30.0000"), core_from_string("20.0000") ) ); + //check that account is still a proxy + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )("staked", 2000000 ), get_voter_info( "alice1111111"_n ) ); + + //unstake more + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, core_from_string("65.0000"), core_from_string("35.0000") ) ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )("staked", 1000000 ), get_voter_info( "alice1111111"_n ) ); + + //unstake the rest + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, core_from_string("65.0000"), core_from_string("35.0000") ) ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )( "staked", 0 ), get_voter_info( "alice1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( proxy_actions_affect_producers, eosio_system_tester, * boost::unit_test::tolerance(1e+5) ) try { + cross_15_percent_threshold(); + + create_accounts_with_resources( { "defproducer1"_n, "defproducer2"_n, "defproducer3"_n } ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer1"_n, 1) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer2"_n, 2) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer3"_n, 3) ); + + //register as a proxy + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", true) + ) + ); + + //accumulate proxied votes + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), vote("bob111111111"_n, vector(), "alice1111111"_n ) ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )( "proxied_vote_weight", stake2votes(core_from_string("150.0003")) ), get_voter_info( "alice1111111"_n ) ); + + //vote for producers + BOOST_REQUIRE_EQUAL( success(), vote("alice1111111"_n, { "defproducer1"_n, "defproducer2"_n } ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( 0 == get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //vote for another producers + BOOST_REQUIRE_EQUAL( success(), vote( "alice1111111"_n, { "defproducer1"_n, "defproducer3"_n } ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //unregister proxy + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", false) + ) + ); + //REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n )( "proxied_vote_weight", stake2votes(core_from_string("150.0003")) ), get_voter_info( "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //register proxy again + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", true) + ) + ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //stake increase by proxy itself affects producers + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("30.0001"), core_from_string("20.0001") ) ); + BOOST_REQUIRE_EQUAL( stake2votes(core_from_string("200.0005")), get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( stake2votes(core_from_string("200.0005")), get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //stake decrease by proxy itself affects producers + BOOST_REQUIRE_EQUAL( success(), unstake( "alice1111111"_n, core_from_string("10.0001"), core_from_string("10.0001") ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("180.0003")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("180.0003")) == get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(multiple_producer_votepay_share, eosio_system_tester, * boost::unit_test::tolerance(1e-10)) try { + + const asset net = core_from_string("80.0000"); + const asset cpu = core_from_string("80.0000"); + const std::vector voters = { "producvotera"_n, "producvoterb"_n, "producvoterc"_n, "producvoterd"_n }; + for (const auto& v: voters) { + create_account_with_resources( v, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + transfer( config::system_account_name, v, core_from_string("100000000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL(success(), stake(v, core_from_string("30000000.0000"), core_from_string("30000000.0000")) ); + } + + // create accounts {defproducera, defproducerb, ..., defproducerz, abcproducera, ..., defproducern} and register as producers + std::vector producer_names; + { + producer_names.reserve('z' - 'a' + 1); + { + const std::string root("defproducer"); + for ( char c = 'a'; c <= 'z'; ++c ) { + producer_names.emplace_back(root + std::string(1, c)); + } + } + { + const std::string root("abcproducer"); + for ( char c = 'a'; c <= 'n'; ++c ) { + producer_names.emplace_back(root + std::string(1, c)); + } + } + setup_producer_accounts(producer_names); + for (const auto& p: producer_names) { + BOOST_REQUIRE_EQUAL( success(), regproducer(p) ); + produce_blocks(1); + ilog( "------ get pro----------" ); + wdump((p)); + BOOST_TEST_REQUIRE(0 == get_producer_info(p)["total_votes"].as_double()); + BOOST_TEST_REQUIRE(0 == get_producer_info2(p)["votepay_share"].as_double()); + BOOST_REQUIRE(0 < microseconds_since_epoch_of_iso_string( get_producer_info2(p)["last_votepay_share_update"] )); + } + } + + produce_block( fc::hours(24) ); + + // producvotera votes for defproducera ... defproducerj + // producvoterb votes for defproducera ... defproduceru + // producvoterc votes for defproducera ... defproducerz + // producvoterd votes for abcproducera ... abcproducern + { + BOOST_TEST_REQUIRE( 0 == get_global_state3()["total_vpay_share_change_rate"].as_double() ); + BOOST_REQUIRE_EQUAL( success(), vote("producvotera"_n, vector(producer_names.begin(), producer_names.begin()+10)) ); + produce_block( fc::hours(10) ); + BOOST_TEST_REQUIRE( 0 == get_global_state2()["total_producer_votepay_share"].as_double() ); + const auto& init_info = get_producer_info(producer_names[0]); + const auto& init_info2 = get_producer_info2(producer_names[0]); + uint64_t init_update = microseconds_since_epoch_of_iso_string( init_info2["last_votepay_share_update"] ); + double init_votes = init_info["total_votes"].as_double(); + BOOST_REQUIRE_EQUAL( success(), vote("producvoterb"_n, vector(producer_names.begin(), producer_names.begin()+21)) ); + const auto& info = get_producer_info(producer_names[0]); + const auto& info2 = get_producer_info2(producer_names[0]); + BOOST_TEST_REQUIRE( ((microseconds_since_epoch_of_iso_string( info2["last_votepay_share_update"] ) - init_update)/double(1E6)) * init_votes == info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( info2["votepay_share"].as_double() * 10 == get_global_state2()["total_producer_votepay_share"].as_double() ); + + BOOST_TEST_REQUIRE( 0 == get_producer_info2(producer_names[11])["votepay_share"].as_double() ); + produce_block( fc::hours(13) ); + BOOST_REQUIRE_EQUAL( success(), vote("producvoterc"_n, vector(producer_names.begin(), producer_names.begin()+26)) ); + BOOST_REQUIRE( 0 < get_producer_info2(producer_names[11])["votepay_share"].as_double() ); + produce_block( fc::hours(1) ); + BOOST_REQUIRE_EQUAL( success(), vote("producvoterd"_n, vector(producer_names.begin()+26, producer_names.end())) ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(producer_names[26])["votepay_share"].as_double() ); + } + + { + auto proda = get_producer_info( "defproducera"_n ); + auto prodj = get_producer_info( "defproducerj"_n ); + auto prodk = get_producer_info( "defproducerk"_n ); + auto produ = get_producer_info( "defproduceru"_n ); + auto prodv = get_producer_info( "defproducerv"_n ); + auto prodz = get_producer_info( "defproducerz"_n ); + + BOOST_REQUIRE (0 == proda["unpaid_blocks"].as() && 0 == prodz["unpaid_blocks"].as()); + + // check vote ratios + BOOST_REQUIRE ( 0 < proda["total_votes"].as_double() && 0 < prodz["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( proda["total_votes"].as_double() == prodj["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( prodk["total_votes"].as_double() == produ["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( prodv["total_votes"].as_double() == prodz["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( 2 * proda["total_votes"].as_double() == 3 * produ["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( proda["total_votes"].as_double() == 3 * prodz["total_votes"].as_double() ); + } + + std::vector vote_shares(producer_names.size()); + { + double total_votes = 0; + for (uint32_t i = 0; i < producer_names.size(); ++i) { + vote_shares[i] = get_producer_info(producer_names[i])["total_votes"].as_double(); + total_votes += vote_shares[i]; + } + BOOST_TEST_REQUIRE( total_votes == get_global_state()["total_producer_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( total_votes == get_global_state3()["total_vpay_share_change_rate"].as_double() ); + BOOST_REQUIRE_EQUAL( microseconds_since_epoch_of_iso_string( get_producer_info2(producer_names.back())["last_votepay_share_update"] ), + microseconds_since_epoch_of_iso_string( get_global_state3()["last_vpay_state_update"] ) ); + + std::for_each( vote_shares.begin(), vote_shares.end(), [total_votes](double& x) { x /= total_votes; } ); + BOOST_TEST_REQUIRE( double(1) == std::accumulate(vote_shares.begin(), vote_shares.end(), double(0)) ); + BOOST_TEST_REQUIRE( double(3./71.) == vote_shares.front() ); + BOOST_TEST_REQUIRE( double(1./71.) == vote_shares.back() ); + } + + std::vector votepay_shares(producer_names.size()); + { + const auto& gs3 = get_global_state3(); + double total_votepay_shares = 0; + double expected_total_votepay_shares = 0; + for (uint32_t i = 0; i < producer_names.size() ; ++i) { + const auto& info = get_producer_info(producer_names[i]); + const auto& info2 = get_producer_info2(producer_names[i]); + votepay_shares[i] = info2["votepay_share"].as_double(); + total_votepay_shares += votepay_shares[i]; + expected_total_votepay_shares += votepay_shares[i]; + expected_total_votepay_shares += info["total_votes"].as_double() + * double( ( microseconds_since_epoch_of_iso_string( gs3["last_vpay_state_update"] ) + - microseconds_since_epoch_of_iso_string( info2["last_votepay_share_update"] ) + ) / 1E6 ); + } + BOOST_TEST( expected_total_votepay_shares > total_votepay_shares ); + BOOST_TEST_REQUIRE( expected_total_votepay_shares == get_global_state2()["total_producer_votepay_share"].as_double() ); + } + + { + const uint32_t prod_index = 15; + const account_name prod_name = producer_names[prod_index]; + const auto& init_info = get_producer_info(prod_name); + const auto& init_info2 = get_producer_info2(prod_name); + BOOST_REQUIRE( 0 < init_info2["votepay_share"].as_double() ); + BOOST_REQUIRE( 0 < microseconds_since_epoch_of_iso_string( init_info2["last_votepay_share_update"] ) ); + + BOOST_REQUIRE_EQUAL( success(), push_action(prod_name, "claimrewards"_n, mvo()("owner", prod_name)) ); + + BOOST_TEST_REQUIRE( 0 == get_producer_info2(prod_name)["votepay_share"].as_double() ); + BOOST_REQUIRE_EQUAL( get_producer_info(prod_name)["last_claim_time"].as_string(), + get_producer_info2(prod_name)["last_votepay_share_update"].as_string() ); + BOOST_REQUIRE_EQUAL( get_producer_info(prod_name)["last_claim_time"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + const auto& gs3 = get_global_state3(); + double expected_total_votepay_shares = 0; + for (uint32_t i = 0; i < producer_names.size(); ++i) { + const auto& info = get_producer_info(producer_names[i]); + const auto& info2 = get_producer_info2(producer_names[i]); + expected_total_votepay_shares += info2["votepay_share"].as_double(); + expected_total_votepay_shares += info["total_votes"].as_double() + * double( ( microseconds_since_epoch_of_iso_string( gs3["last_vpay_state_update"] ) + - microseconds_since_epoch_of_iso_string( info2["last_votepay_share_update"] ) + ) / 1E6 ); + } + BOOST_TEST_REQUIRE( expected_total_votepay_shares == get_global_state2()["total_producer_votepay_share"].as_double() ); + } + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(votepay_share_invariant, eosio_system_tester, * boost::unit_test::tolerance(1e-10)) try { + + cross_15_percent_threshold(); + + const asset net = core_from_string("80.0000"); + const asset cpu = core_from_string("80.0000"); + const std::vector accounts = { "aliceaccount"_n, "bobbyaccount"_n, "carolaccount"_n, "emilyaccount"_n }; + for (const auto& a: accounts) { + create_account_with_resources( a, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + transfer( config::system_account_name, a, core_from_string("1000.0000"), config::system_account_name ); + } + const auto vota = accounts[0]; + const auto votb = accounts[1]; + const auto proda = accounts[2]; + const auto prodb = accounts[3]; + + BOOST_REQUIRE_EQUAL( success(), stake( vota, core_from_string("100.0000"), core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), stake( votb, core_from_string("100.0000"), core_from_string("100.0000") ) ); + + BOOST_REQUIRE_EQUAL( success(), regproducer( proda ) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( prodb ) ); + + BOOST_REQUIRE_EQUAL( success(), vote( vota, { proda } ) ); + BOOST_REQUIRE_EQUAL( success(), vote( votb, { prodb } ) ); + + produce_block( fc::hours(25) ); + + BOOST_REQUIRE_EQUAL( success(), vote( vota, { proda } ) ); + BOOST_REQUIRE_EQUAL( success(), vote( votb, { prodb } ) ); + + produce_block( fc::hours(1) ); + + BOOST_REQUIRE_EQUAL( success(), push_action(proda, "claimrewards"_n, mvo()("owner", proda)) ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(proda)["votepay_share"].as_double() ); + + produce_block( fc::hours(24) ); + + BOOST_REQUIRE_EQUAL( success(), vote( vota, { proda } ) ); + + produce_block( fc::hours(24) ); + + BOOST_REQUIRE_EQUAL( success(), push_action(prodb, "claimrewards"_n, mvo()("owner", prodb)) ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(prodb)["votepay_share"].as_double() ); + + produce_block( fc::hours(10) ); + + BOOST_REQUIRE_EQUAL( success(), vote( votb, { prodb } ) ); + + produce_block( fc::hours(16) ); + + BOOST_REQUIRE_EQUAL( success(), vote( votb, { prodb } ) ); + produce_block( fc::hours(2) ); + BOOST_REQUIRE_EQUAL( success(), vote( vota, { proda } ) ); + + const auto& info = get_producer_info(prodb); + const auto& info2 = get_producer_info2(prodb); + const auto& gs2 = get_global_state2(); + const auto& gs3 = get_global_state3(); + + double expected_total_vpay_share = info2["votepay_share"].as_double() + + info["total_votes"].as_double() + * ( microseconds_since_epoch_of_iso_string( gs3["last_vpay_state_update"] ) + - microseconds_since_epoch_of_iso_string( info2["last_votepay_share_update"] ) ) / 1E6; + + BOOST_TEST_REQUIRE( expected_total_vpay_share == gs2["total_producer_votepay_share"].as_double() ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(votepay_share_proxy, eosio_system_tester, * boost::unit_test::tolerance(1e-5)) try { + + cross_15_percent_threshold(); + + const asset net = core_from_string("80.0000"); + const asset cpu = core_from_string("80.0000"); + const std::vector accounts = { "aliceaccount"_n, "bobbyaccount"_n, "carolaccount"_n, "emilyaccount"_n }; + for (const auto& a: accounts) { + create_account_with_resources( a, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + transfer( config::system_account_name, a, core_from_string("1000.0000"), config::system_account_name ); + } + const auto alice = accounts[0]; + const auto bob = accounts[1]; + const auto carol = accounts[2]; + const auto emily = accounts[3]; + + // alice becomes a proxy + BOOST_REQUIRE_EQUAL( success(), push_action( alice, "regproxy"_n, mvo()("proxy", alice)("isproxy", true) ) ); + REQUIRE_MATCHING_OBJECT( proxy( alice ), get_voter_info( alice ) ); + + // carol and emily become producers + BOOST_REQUIRE_EQUAL( success(), regproducer( carol, 1) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( emily, 1) ); + + // bob chooses alice as proxy + BOOST_REQUIRE_EQUAL( success(), stake( bob, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), stake( alice, core_from_string("150.0000"), core_from_string("150.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( bob, { }, alice ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_voter_info(alice)["proxied_vote_weight"].as_double() ); + + // alice (proxy) votes for carol + BOOST_REQUIRE_EQUAL( success(), vote( alice, { carol } ) ); + double total_votes = get_producer_info(carol)["total_votes"].as_double(); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("450.0003")) == total_votes ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(carol)["votepay_share"].as_double() ); + uint64_t last_update_time = microseconds_since_epoch_of_iso_string( get_producer_info2(carol)["last_votepay_share_update"] ); + + produce_block( fc::hours(15) ); + + // alice (proxy) votes again for carol + BOOST_REQUIRE_EQUAL( success(), vote( alice, { carol } ) ); + auto cur_info2 = get_producer_info2(carol); + double expected_votepay_share = double( (microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ) - last_update_time) / 1E6 ) * total_votes; + BOOST_TEST_REQUIRE( stake2votes(core_from_string("450.0003")) == get_producer_info(carol)["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( expected_votepay_share == cur_info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( expected_votepay_share == get_global_state2()["total_producer_votepay_share"].as_double() ); + last_update_time = microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ); + total_votes = get_producer_info(carol)["total_votes"].as_double(); + + produce_block( fc::hours(40) ); + + // bob unstakes + BOOST_REQUIRE_EQUAL( success(), unstake( bob, core_from_string("10.0002"), core_from_string("10.0001") ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("430.0000")), get_producer_info(carol)["total_votes"].as_double() ); + + cur_info2 = get_producer_info2(carol); + expected_votepay_share += double( (microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ) - last_update_time) / 1E6 ) * total_votes; + BOOST_TEST_REQUIRE( expected_votepay_share == cur_info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( expected_votepay_share == get_global_state2()["total_producer_votepay_share"].as_double() ); + last_update_time = microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ); + total_votes = get_producer_info(carol)["total_votes"].as_double(); + + // carol claims rewards + BOOST_REQUIRE_EQUAL( success(), push_action(carol, "claimrewards"_n, mvo()("owner", carol)) ); + + produce_block( fc::hours(20) ); + + // bob votes for carol + BOOST_REQUIRE_EQUAL( success(), vote( bob, { carol } ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("430.0000")), get_producer_info(carol)["total_votes"].as_double() ); + cur_info2 = get_producer_info2(carol); + expected_votepay_share = double( (microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ) - last_update_time) / 1E6 ) * total_votes; + BOOST_TEST_REQUIRE( expected_votepay_share == cur_info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( expected_votepay_share == get_global_state2()["total_producer_votepay_share"].as_double() ); + + produce_block( fc::hours(54) ); + + // bob votes for carol again + // carol hasn't claimed rewards in over 3 days + total_votes = get_producer_info(carol)["total_votes"].as_double(); + BOOST_REQUIRE_EQUAL( success(), vote( bob, { carol } ) ); + BOOST_REQUIRE_EQUAL( get_producer_info2(carol)["last_votepay_share_update"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(carol)["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == get_global_state2()["total_producer_votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == get_global_state3()["total_vpay_share_change_rate"].as_double() ); + + produce_block( fc::hours(20) ); + + // bob votes for carol again + // carol still hasn't claimed rewards + BOOST_REQUIRE_EQUAL( success(), vote( bob, { carol } ) ); + BOOST_REQUIRE_EQUAL(get_producer_info2(carol)["last_votepay_share_update"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(carol)["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == get_global_state2()["total_producer_votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == get_global_state3()["total_vpay_share_change_rate"].as_double() ); + + produce_block( fc::hours(24) ); + + // carol finally claims rewards + BOOST_REQUIRE_EQUAL( success(), push_action( carol, "claimrewards"_n, mvo()("owner", carol) ) ); + BOOST_TEST_REQUIRE( 0 == get_producer_info2(carol)["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == get_global_state2()["total_producer_votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( total_votes == get_global_state3()["total_vpay_share_change_rate"].as_double() ); + + produce_block( fc::hours(5) ); + + // alice votes for carol and emily + // emily hasn't claimed rewards in over 3 days + last_update_time = microseconds_since_epoch_of_iso_string( get_producer_info2(carol)["last_votepay_share_update"] ); + BOOST_REQUIRE_EQUAL( success(), vote( alice, { carol, emily } ) ); + cur_info2 = get_producer_info2(carol); + auto cur_info2_emily = get_producer_info2(emily); + + expected_votepay_share = double( (microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ) - last_update_time) / 1E6 ) * total_votes; + BOOST_TEST_REQUIRE( expected_votepay_share == cur_info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == cur_info2_emily["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( expected_votepay_share == get_global_state2()["total_producer_votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( get_producer_info(carol)["total_votes"].as_double() == + get_global_state3()["total_vpay_share_change_rate"].as_double() ); + BOOST_REQUIRE_EQUAL( cur_info2["last_votepay_share_update"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + BOOST_REQUIRE_EQUAL( cur_info2_emily["last_votepay_share_update"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + + produce_block( fc::hours(10) ); + + // bob chooses alice as proxy + // emily still hasn't claimed rewards + last_update_time = microseconds_since_epoch_of_iso_string( get_producer_info2(carol)["last_votepay_share_update"] ); + BOOST_REQUIRE_EQUAL( success(), vote( bob, { }, alice ) ); + cur_info2 = get_producer_info2(carol); + cur_info2_emily = get_producer_info2(emily); + + expected_votepay_share += double( (microseconds_since_epoch_of_iso_string( cur_info2["last_votepay_share_update"] ) - last_update_time) / 1E6 ) * total_votes; + BOOST_TEST_REQUIRE( expected_votepay_share == cur_info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == cur_info2_emily["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( expected_votepay_share == get_global_state2()["total_producer_votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( get_producer_info(carol)["total_votes"].as_double() == + get_global_state3()["total_vpay_share_change_rate"].as_double() ); + BOOST_REQUIRE_EQUAL( cur_info2["last_votepay_share_update"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + BOOST_REQUIRE_EQUAL( cur_info2_emily["last_votepay_share_update"].as_string(), + get_global_state3()["last_vpay_state_update"].as_string() ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(votepay_share_update_order, eosio_system_tester, * boost::unit_test::tolerance(1e-10)) try { + + cross_15_percent_threshold(); + + const asset net = core_from_string("80.0000"); + const asset cpu = core_from_string("80.0000"); + const std::vector accounts = { "aliceaccount"_n, "bobbyaccount"_n, "carolaccount"_n, "emilyaccount"_n }; + for (const auto& a: accounts) { + create_account_with_resources( a, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + transfer( config::system_account_name, a, core_from_string("1000.0000"), config::system_account_name ); + } + const auto alice = accounts[0]; + const auto bob = accounts[1]; + const auto carol = accounts[2]; + const auto emily = accounts[3]; + + BOOST_REQUIRE_EQUAL( success(), regproducer( carol ) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( emily ) ); + + produce_block( fc::hours(24) ); + + BOOST_REQUIRE_EQUAL( success(), stake( alice, core_from_string("100.0000"), core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( success(), stake( bob, core_from_string("100.0000"), core_from_string("100.0000") ) ); + + BOOST_REQUIRE_EQUAL( success(), vote( alice, { carol, emily } ) ); + + + BOOST_REQUIRE_EQUAL( success(), push_action( carol, "claimrewards"_n, mvo()("owner", carol) ) ); + produce_block( fc::hours(1) ); + BOOST_REQUIRE_EQUAL( success(), push_action( emily, "claimrewards"_n, mvo()("owner", emily) ) ); + + produce_block( fc::hours(3 * 24 + 1) ); + + { + signed_transaction trx; + set_transaction_headers(trx); + + trx.actions.emplace_back( get_action( config::system_account_name, "claimrewards"_n, { {carol, config::active_name} }, + mvo()("owner", carol) ) ); + + std::vector prods = { carol, emily }; + trx.actions.emplace_back( get_action( config::system_account_name, "voteproducer"_n, { {alice, config::active_name} }, + mvo()("voter", alice)("proxy", name(0))("producers", prods) ) ); + + trx.actions.emplace_back( get_action( config::system_account_name, "claimrewards"_n, { {emily, config::active_name} }, + mvo()("owner", emily) ) ); + + trx.sign( get_private_key( carol, "active" ), control->get_chain_id() ); + trx.sign( get_private_key( alice, "active" ), control->get_chain_id() ); + trx.sign( get_private_key( emily, "active" ), control->get_chain_id() ); + + push_transaction( trx ); + } + + const auto& carol_info = get_producer_info(carol); + const auto& carol_info2 = get_producer_info2(carol); + const auto& emily_info = get_producer_info(emily); + const auto& emily_info2 = get_producer_info2(emily); + const auto& gs3 = get_global_state3(); + BOOST_REQUIRE_EQUAL( carol_info2["last_votepay_share_update"].as_string(), gs3["last_vpay_state_update"].as_string() ); + BOOST_REQUIRE_EQUAL( emily_info2["last_votepay_share_update"].as_string(), gs3["last_vpay_state_update"].as_string() ); + BOOST_TEST_REQUIRE( 0 == carol_info2["votepay_share"].as_double() ); + BOOST_TEST_REQUIRE( 0 == emily_info2["votepay_share"].as_double() ); + BOOST_REQUIRE( 0 < carol_info["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( carol_info["total_votes"].as_double() == emily_info["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( gs3["total_vpay_share_change_rate"].as_double() == 2 * carol_info["total_votes"].as_double() ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(votepay_transition, eosio_system_tester, * boost::unit_test::tolerance(1e-10)) try { + + const asset net = core_from_string("80.0000"); + const asset cpu = core_from_string("80.0000"); + const std::vector voters = { "producvotera"_n, "producvoterb"_n, "producvoterc"_n, "producvoterd"_n }; + for (const auto& v: voters) { + create_account_with_resources( v, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + transfer( config::system_account_name, v, core_from_string("100000000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL(success(), stake(v, core_from_string("30000000.0000"), core_from_string("30000000.0000")) ); + } + + // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers + std::vector producer_names; + { + producer_names.reserve('z' - 'a' + 1); + { + const std::string root("defproducer"); + for ( char c = 'a'; c <= 'd'; ++c ) { + producer_names.emplace_back(root + std::string(1, c)); + } + } + setup_producer_accounts(producer_names); + for (const auto& p: producer_names) { + BOOST_REQUIRE_EQUAL( success(), regproducer(p) ); + BOOST_TEST_REQUIRE(0 == get_producer_info(p)["total_votes"].as_double()); + BOOST_TEST_REQUIRE(0 == get_producer_info2(p)["votepay_share"].as_double()); + BOOST_REQUIRE(0 < microseconds_since_epoch_of_iso_string( get_producer_info2(p)["last_votepay_share_update"] )); + } + } + + BOOST_REQUIRE_EQUAL( success(), vote("producvotera"_n, vector(producer_names.begin(), producer_names.end())) ); + auto* tbl = control->db().find( + boost::make_tuple( config::system_account_name, + config::system_account_name, + "producers2"_n ) ); + BOOST_REQUIRE( tbl ); + BOOST_REQUIRE( 0 < microseconds_since_epoch_of_iso_string( get_producer_info2("defproducera"_n)["last_votepay_share_update"] ) ); + + // const_cast hack for now + const_cast(control->db()).remove( *tbl ); + tbl = control->db().find( + boost::make_tuple( config::system_account_name, + config::system_account_name, + "producers2"_n ) ); + BOOST_REQUIRE( !tbl ); + + BOOST_REQUIRE_EQUAL( success(), vote("producvoterb"_n, vector(producer_names.begin(), producer_names.end())) ); + tbl = control->db().find( + boost::make_tuple( config::system_account_name, + config::system_account_name, + "producers2"_n ) ); + BOOST_REQUIRE( !tbl ); + BOOST_REQUIRE_EQUAL( success(), regproducer("defproducera"_n) ); + BOOST_REQUIRE( microseconds_since_epoch_of_iso_string( get_producer_info("defproducera"_n)["last_claim_time"] ) < microseconds_since_epoch_of_iso_string( get_producer_info2("defproducera"_n)["last_votepay_share_update"] ) ); + + create_account_with_resources( "defproducer1"_n, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + BOOST_REQUIRE_EQUAL( success(), regproducer("defproducer1"_n) ); + BOOST_REQUIRE( 0 < microseconds_since_epoch_of_iso_string( get_producer_info("defproducer1"_n)["last_claim_time"] ) ); + BOOST_REQUIRE_EQUAL( get_producer_info("defproducer1"_n)["last_claim_time"].as_string(), + get_producer_info2("defproducer1"_n)["last_votepay_share_update"].as_string() ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE(producers_upgrade_system_contract, eosio_system_tester) try { + //install multisig contract + abi_serializer msig_abi_ser = initialize_multisig(); + auto producer_names = active_and_vote_producers(); + + //change `default_max_inline_action_size` to 512 KB + eosio::chain::chain_config params = control->get_global_properties().configuration; + params.max_inline_action_size = 512 * 1024; + base_tester::push_action( config::system_account_name, "setparams"_n, config::system_account_name, mutable_variant_object() + ("params", params) ); + + produce_blocks(); + + //helper function + auto push_action_msig = [&]( const account_name& signer, const action_name &name, const variant_object &data, bool auth = true ) -> action_result { + string action_type_name = msig_abi_ser.get_action_type(name); + + action act; + act.account = "eosio.msig"_n; + act.name = name; + act.data = msig_abi_ser.variant_to_binary( action_type_name, data, abi_serializer::create_yield_function(abi_serializer_max_time) ); + + return base_tester::push_action( std::move(act), (auth ? signer : signer == "bob111111111"_n ? "alice1111111"_n : "bob111111111"_n).to_uint64_t() ); + }; + // test begins + vector prod_perms; + for ( auto& x : producer_names ) { + prod_perms.push_back( { name(x), config::active_name } ); + } + + transaction trx; + { + //prepare system contract with different hash (contract differs in one byte) + auto code = test_contracts::eosio_system_wasm(); + string msg = "producer votes must be unique and sorted"; + auto it = std::search( code.begin(), code.end(), msg.begin(), msg.end() ); + BOOST_REQUIRE( it != code.end() ); + msg[0] = 'P'; + std::copy( msg.begin(), msg.end(), it ); + + fc::variant pretty_trx = fc::mutable_variant_object() + ("expiration", "2020-01-01T00:30") + ("ref_block_num", 2) + ("ref_block_prefix", 3) + ("net_usage_words", 0) + ("max_cpu_usage_ms", 0) + ("delay_sec", 0) + ("actions", fc::variants({ + fc::mutable_variant_object() + ("account", name(config::system_account_name)) + ("name", "setcode") + ("authorization", vector{ { config::system_account_name, config::active_name } }) + ("data", fc::mutable_variant_object() ("account", name(config::system_account_name)) + ("vmtype", 0) + ("vmversion", "0") + ("code", bytes( code.begin(), code.end() )) + ) + }) + ); + abi_serializer::from_variant(pretty_trx, trx, get_resolver(), abi_serializer::create_yield_function(abi_serializer_max_time)); + } + + BOOST_REQUIRE_EQUAL(success(), push_action_msig( "alice1111111"_n, "propose"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "upgrade1") + ("trx", trx) + ("requested", prod_perms) + ) + ); + + // get 15 approvals + for ( size_t i = 0; i < 14; ++i ) { + BOOST_REQUIRE_EQUAL(success(), push_action_msig( name(producer_names[i]), "approve"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "upgrade1") + ("level", permission_level{ name(producer_names[i]), config::active_name }) + ) + ); + } + + //should fail + BOOST_REQUIRE_EQUAL(wasm_assert_msg("transaction authorization failed"), + push_action_msig( "alice1111111"_n, "exec"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "upgrade1") + ("executer", "alice1111111"_n) + ) + ); + + // one more approval + BOOST_REQUIRE_EQUAL(success(), push_action_msig( name(producer_names[14]), "approve"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "upgrade1") + ("level", permission_level{ name(producer_names[14]), config::active_name }) + ) + ); + + transaction_trace_ptr trace; + control->applied_transaction.connect( + [&]( std::tuple p ) { + trace = std::get<0>(p); + } ); + + BOOST_REQUIRE_EQUAL(success(), push_action_msig( "alice1111111"_n, "exec"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "upgrade1") + ("executer", "alice1111111"_n) + ) + ); + + BOOST_REQUIRE( bool(trace) ); + BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status ); + + produce_blocks( 250 ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(producer_onblock_check, eosio_system_tester) try { + + const asset large_asset = core_from_string("80.0000"); + create_account_with_resources( "producvotera"_n, config::system_account_name, core_from_string("1.0000"), false, large_asset, large_asset ); + create_account_with_resources( "producvoterb"_n, config::system_account_name, core_from_string("1.0000"), false, large_asset, large_asset ); + create_account_with_resources( "producvoterc"_n, config::system_account_name, core_from_string("1.0000"), false, large_asset, large_asset ); + + // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers + std::vector producer_names; + producer_names.reserve('z' - 'a' + 1); + const std::string root("defproducer"); + for ( char c = 'a'; c <= 'z'; ++c ) { + producer_names.emplace_back(root + std::string(1, c)); + } + setup_producer_accounts(producer_names); + + for (auto a:producer_names) + regproducer(a); + + produce_block(fc::hours(24)); + + BOOST_REQUIRE_EQUAL(0, get_producer_info( producer_names.front() )["total_votes"].as()); + BOOST_REQUIRE_EQUAL(0, get_producer_info( producer_names.back() )["total_votes"].as()); + + + transfer(config::system_account_name, "producvotera"_n, core_from_string("200000000.0000"), config::system_account_name); + BOOST_REQUIRE_EQUAL(success(), stake("producvotera"_n, core_from_string("70000000.0000"), core_from_string("70000000.0000") )); + BOOST_REQUIRE_EQUAL(success(), vote( "producvotera"_n, vector(producer_names.begin(), producer_names.begin()+10))); + BOOST_CHECK_EQUAL( wasm_assert_msg( "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" ), + unstake( "producvotera"_n, core_from_string("50.0000"), core_from_string("50.0000") ) ); + + // give a chance for everyone to produce blocks + { + produce_blocks(21 * 12); + bool all_21_produced = true; + for (uint32_t i = 0; i < 21; ++i) { + if (0 == get_producer_info(producer_names[i])["unpaid_blocks"].as()) { + all_21_produced= false; + } + } + bool rest_didnt_produce = true; + for (uint32_t i = 21; i < producer_names.size(); ++i) { + if (0 < get_producer_info(producer_names[i])["unpaid_blocks"].as()) { + rest_didnt_produce = false; + } + } + BOOST_REQUIRE_EQUAL(false, all_21_produced); + BOOST_REQUIRE_EQUAL(true, rest_didnt_produce); + } + + { + const char* claimrewards_activation_error_message = "cannot claim rewards until the chain is activated (at least 15% of all tokens participate in voting)"; + BOOST_CHECK_EQUAL(0, get_global_state()["total_unpaid_blocks"].as()); + BOOST_REQUIRE_EQUAL(wasm_assert_msg( claimrewards_activation_error_message ), + push_action(producer_names.front(), "claimrewards"_n, mvo()("owner", producer_names.front()))); + BOOST_REQUIRE_EQUAL(0, get_balance(producer_names.front()).get_amount()); + BOOST_REQUIRE_EQUAL(wasm_assert_msg( claimrewards_activation_error_message ), + push_action(producer_names.back(), "claimrewards"_n, mvo()("owner", producer_names.back()))); + BOOST_REQUIRE_EQUAL(0, get_balance(producer_names.back()).get_amount()); + } + + // stake across 15% boundary + transfer(config::system_account_name, "producvoterb"_n, core_from_string("100000000.0000"), config::system_account_name); + BOOST_REQUIRE_EQUAL(success(), stake("producvoterb"_n, core_from_string("4000000.0000"), core_from_string("4000000.0000"))); + transfer(config::system_account_name, "producvoterc"_n, core_from_string("100000000.0000"), config::system_account_name); + BOOST_REQUIRE_EQUAL(success(), stake("producvoterc"_n, core_from_string("2000000.0000"), core_from_string("2000000.0000"))); + + BOOST_REQUIRE_EQUAL(success(), vote( "producvoterb"_n, vector(producer_names.begin(), producer_names.begin()+21))); + BOOST_REQUIRE_EQUAL(success(), vote( "producvoterc"_n, vector(producer_names.begin(), producer_names.end()))); + + // give a chance for everyone to produce blocks + { + produce_blocks(21 * 12); + bool all_21_produced = true; + for (uint32_t i = 0; i < 21; ++i) { + if (0 == get_producer_info(producer_names[i])["unpaid_blocks"].as()) { + all_21_produced= false; + } + } + bool rest_didnt_produce = true; + for (uint32_t i = 21; i < producer_names.size(); ++i) { + if (0 < get_producer_info(producer_names[i])["unpaid_blocks"].as()) { + rest_didnt_produce = false; + } + } + BOOST_REQUIRE_EQUAL(true, all_21_produced); + BOOST_REQUIRE_EQUAL(true, rest_didnt_produce); + BOOST_REQUIRE_EQUAL(success(), + push_action(producer_names.front(), "claimrewards"_n, mvo()("owner", producer_names.front()))); + BOOST_REQUIRE(0 < get_balance(producer_names.front()).get_amount()); + } + + BOOST_CHECK_EQUAL( success(), unstake( "producvotera"_n, core_from_string("50.0000"), core_from_string("50.0000") ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( voters_actions_affect_proxy_and_producers, eosio_system_tester, * boost::unit_test::tolerance(1e+6) ) try { + cross_15_percent_threshold(); + + create_accounts_with_resources( { "donald111111"_n, "defproducer1"_n, "defproducer2"_n, "defproducer3"_n } ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer1"_n, 1) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer2"_n, 2) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer3"_n, 3) ); + + //alice1111111 becomes a producer + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", true) + ) + ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n ), get_voter_info( "alice1111111"_n ) ); + + //alice1111111 makes stake and votes + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("30.0001"), core_from_string("20.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "alice1111111"_n, { "defproducer1"_n, "defproducer2"_n } ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("50.0002")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("50.0002")) == get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + BOOST_REQUIRE_EQUAL( success(), push_action( "donald111111"_n, "regproxy"_n, mvo() + ("proxy", "donald111111") + ("isproxy", true) + ) + ); + REQUIRE_MATCHING_OBJECT( proxy( "donald111111"_n ), get_voter_info( "donald111111"_n ) ); + + //bob111111111 chooses alice1111111 as a proxy + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, vector(), "alice1111111"_n ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("150.0003")) == get_voter_info( "alice1111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("200.0005")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("200.0005")) == get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //carol1111111 chooses alice1111111 as a proxy + issue_and_transfer( "carol1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, core_from_string("30.0001"), core_from_string("20.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "carol1111111"_n, vector(), "alice1111111"_n ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("200.0005")) == get_voter_info( "alice1111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("250.0007")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("250.0007")) == get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //proxied voter carol1111111 increases stake + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, core_from_string("50.0000"), core_from_string("70.0000") ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("320.0005")) == get_voter_info( "alice1111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("370.0007")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("370.0007")) == get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //proxied voter bob111111111 decreases stake + BOOST_REQUIRE_EQUAL( success(), unstake( "bob111111111"_n, core_from_string("50.0001"), core_from_string("50.0001") ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("220.0003")) == get_voter_info( "alice1111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("270.0005")) == get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("270.0005")) == get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //proxied voter carol1111111 chooses another proxy + BOOST_REQUIRE_EQUAL( success(), vote( "carol1111111"_n, vector(), "donald111111"_n ) ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("50.0001")), get_voter_info( "alice1111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("170.0002")), get_voter_info( "donald111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("100.0003")), get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("100.0003")), get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_REQUIRE_EQUAL( 0, get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + + //bob111111111 switches to direct voting and votes for one of the same producers, but not for another one + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, { "defproducer2"_n } ) ); + BOOST_TEST_REQUIRE( 0.0 == get_voter_info( "alice1111111"_n )["proxied_vote_weight"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("50.0002")), get_producer_info( "defproducer1"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( stake2votes(core_from_string("100.0003")), get_producer_info( "defproducer2"_n )["total_votes"].as_double() ); + BOOST_TEST_REQUIRE( 0.0 == get_producer_info( "defproducer3"_n )["total_votes"].as_double() ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( vote_both_proxy_and_producers, eosio_system_tester ) try { + //alice1111111 becomes a proxy + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", true) + ) + ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n ), get_voter_info( "alice1111111"_n ) ); + + //carol1111111 becomes a producer + BOOST_REQUIRE_EQUAL( success(), regproducer( "carol1111111"_n, 1) ); + + //bob111111111 chooses alice1111111 as a proxy + + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg("cannot vote for producers and proxy at same time"), + vote( "bob111111111"_n, { "carol1111111"_n }, "alice1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( select_invalid_proxy, eosio_system_tester ) try { + //accumulate proxied votes + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + + //selecting account not registered as a proxy + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "invalid proxy specified" ), + vote( "bob111111111"_n, vector(), "alice1111111"_n ) ); + + //selecting not existing account as a proxy + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "invalid proxy specified" ), + vote( "bob111111111"_n, vector(), "notexist"_n ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( double_register_unregister_proxy_keeps_votes, eosio_system_tester ) try { + //alice1111111 becomes a proxy + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", 1) + ) + ); + issue_and_transfer( "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, core_from_string("5.0000"), core_from_string("5.0000") ) ); + edump((get_voter_info("alice1111111"_n))); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )( "staked", 100000 ), get_voter_info( "alice1111111"_n ) ); + + //bob111111111 stakes and selects alice1111111 as a proxy + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, vector(), "alice1111111"_n ) ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )( "proxied_vote_weight", stake2votes( core_from_string("150.0003") ))( "staked", 100000 ), get_voter_info( "alice1111111"_n ) ); + + //double regestering should fail without affecting total votes and stake + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "action has no effect" ), + push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", 1) + ) + ); + REQUIRE_MATCHING_OBJECT( proxy( "alice1111111"_n )( "proxied_vote_weight", stake2votes(core_from_string("150.0003")) )( "staked", 100000 ), get_voter_info( "alice1111111"_n ) ); + + //uregister + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", 0) + ) + ); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n )( "proxied_vote_weight", stake2votes(core_from_string("150.0003")) )( "staked", 100000 ), get_voter_info( "alice1111111"_n ) ); + + //double unregistering should not affect proxied_votes and stake + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "action has no effect" ), + push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", 0) + ) + ); + REQUIRE_MATCHING_OBJECT( voter( "alice1111111"_n )( "proxied_vote_weight", stake2votes(core_from_string("150.0003")))( "staked", 100000 ), get_voter_info( "alice1111111"_n ) ); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( proxy_cannot_use_another_proxy, eosio_system_tester ) try { + //alice1111111 becomes a proxy + BOOST_REQUIRE_EQUAL( success(), push_action( "alice1111111"_n, "regproxy"_n, mvo() + ("proxy", "alice1111111"_n) + ("isproxy", 1) + ) + ); + + //bob111111111 becomes a proxy + BOOST_REQUIRE_EQUAL( success(), push_action( "bob111111111"_n, "regproxy"_n, mvo() + ("proxy", "bob111111111"_n) + ("isproxy", 1) + ) + ); + + //proxy should not be able to use a proxy + issue_and_transfer( "bob111111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "account registered as a proxy is not allowed to use a proxy" ), + vote( "bob111111111"_n, vector(), "alice1111111"_n ) ); + + //voter that uses a proxy should not be allowed to become a proxy + issue_and_transfer( "carol1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( "carol1111111"_n, core_from_string("100.0002"), core_from_string("50.0001") ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "carol1111111"_n, vector(), "alice1111111"_n ) ); + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "account that uses a proxy is not allowed to become a proxy" ), + push_action( "carol1111111"_n, "regproxy"_n, mvo() + ("proxy", "carol1111111"_n) + ("isproxy", 1) + ) + ); + + //proxy should not be able to use itself as a proxy + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "cannot proxy to self" ), + vote( "bob111111111"_n, vector(), "bob111111111"_n ) ); + +} FC_LOG_AND_RETHROW() + +fc::mutable_variant_object config_to_variant( const eosio::chain::chain_config& config ) { + return mutable_variant_object() + ( "max_block_net_usage", config.max_block_net_usage ) + ( "target_block_net_usage_pct", config.target_block_net_usage_pct ) + ( "max_transaction_net_usage", config.max_transaction_net_usage ) + ( "base_per_transaction_net_usage", config.base_per_transaction_net_usage ) + ( "context_free_discount_net_usage_num", config.context_free_discount_net_usage_num ) + ( "context_free_discount_net_usage_den", config.context_free_discount_net_usage_den ) + ( "max_block_cpu_usage", config.max_block_cpu_usage ) + ( "target_block_cpu_usage_pct", config.target_block_cpu_usage_pct ) + ( "max_transaction_cpu_usage", config.max_transaction_cpu_usage ) + ( "min_transaction_cpu_usage", config.min_transaction_cpu_usage ) + ( "max_transaction_lifetime", config.max_transaction_lifetime ) + ( "deferred_trx_expiration_window", config.deferred_trx_expiration_window ) + ( "max_transaction_delay", config.max_transaction_delay ) + ( "max_inline_action_size", config.max_inline_action_size ) + ( "max_inline_action_depth", config.max_inline_action_depth ) + ( "max_authority_depth", config.max_authority_depth ); +} + +BOOST_FIXTURE_TEST_CASE( elect_producers /*_and_parameters*/, eosio_system_tester ) try { + create_accounts_with_resources( { "defproducer1"_n, "defproducer2"_n, "defproducer3"_n } ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer1"_n, 1) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer2"_n, 2) ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "defproducer3"_n, 3) ); + + //stake more than 15% of total EOS supply to activate chain + transfer( "eosio"_n, "alice1111111"_n, core_from_string("600000000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111"_n, "alice1111111"_n, core_from_string("300000000.0000"), core_from_string("300000000.0000") ) ); + //vote for producers + BOOST_REQUIRE_EQUAL( success(), vote( "alice1111111"_n, { "defproducer1"_n } ) ); + produce_blocks(250); + auto producer_keys = control->active_producers(); + BOOST_REQUIRE_EQUAL( 1, producer_keys.producers.size() ); + BOOST_REQUIRE_EQUAL( name("defproducer1"_n), producer_keys.producers[0].producer_name ); + + //auto config = config_to_variant( control->get_global_properties().configuration ); + //auto prod1_config = testing::filter_fields( config, producer_parameters_example( 1 ) ); + //REQUIRE_EQUAL_OBJECTS(prod1_config, config); + + // elect 2 producers + issue_and_transfer( "bob111111111"_n, core_from_string("80000.0000"), config::system_account_name ); + ilog("stake"); + BOOST_REQUIRE_EQUAL( success(), stake( "bob111111111"_n, core_from_string("40000.0000"), core_from_string("40000.0000") ) ); + ilog("start vote"); + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, { "defproducer2"_n } ) ); + ilog("."); + produce_blocks(250); + producer_keys = control->active_producers(); + BOOST_REQUIRE_EQUAL( 2, producer_keys.producers.size() ); + BOOST_REQUIRE_EQUAL( name("defproducer1"_n), producer_keys.producers[0].producer_name ); + BOOST_REQUIRE_EQUAL( name("defproducer2"_n), producer_keys.producers[1].producer_name ); + //config = config_to_variant( control->get_global_properties().configuration ); + //auto prod2_config = testing::filter_fields( config, producer_parameters_example( 2 ) ); + //REQUIRE_EQUAL_OBJECTS(prod2_config, config); + + // elect 3 producers + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, { "defproducer2"_n, "defproducer3"_n } ) ); + produce_blocks(250); + producer_keys = control->active_producers(); + BOOST_REQUIRE_EQUAL( 3, producer_keys.producers.size() ); + BOOST_REQUIRE_EQUAL( name("defproducer1"_n), producer_keys.producers[0].producer_name ); + BOOST_REQUIRE_EQUAL( name("defproducer2"_n), producer_keys.producers[1].producer_name ); + BOOST_REQUIRE_EQUAL( name("defproducer3"_n), producer_keys.producers[2].producer_name ); + //config = config_to_variant( control->get_global_properties().configuration ); + //REQUIRE_EQUAL_OBJECTS(prod2_config, config); + + // try to go back to 2 producers and fail + BOOST_REQUIRE_EQUAL( success(), vote( "bob111111111"_n, { "defproducer3"_n } ) ); + produce_blocks(250); + producer_keys = control->active_producers(); + BOOST_REQUIRE_EQUAL( 3, producer_keys.producers.size() ); + + // The test below is invalid now, producer schedule is not updated if there are + // fewer producers in the new schedule + /* + BOOST_REQUIRE_EQUAL( 2, producer_keys.size() ); + BOOST_REQUIRE_EQUAL( name("defproducer1"_n), producer_keys[0].producer_name ); + BOOST_REQUIRE_EQUAL( name("defproducer3"_n), producer_keys[1].producer_name ); + //config = config_to_variant( control->get_global_properties().configuration ); + //auto prod3_config = testing::filter_fields( config, producer_parameters_example( 3 ) ); + //REQUIRE_EQUAL_OBJECTS(prod3_config, config); + */ + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( buyname, eosio_system_tester ) try { + create_accounts_with_resources( { "dan"_n, "sam"_n } ); + transfer( config::system_account_name, "dan"_n, core_from_string( "10000.0000" ) ); + transfer( config::system_account_name, "sam"_n, core_from_string( "10000.0000" ) ); + stake_with_transfer( config::system_account_name, "sam"_n, core_from_string( "80000000.0000" ), core_from_string( "80000000.0000" ) ); + stake_with_transfer( config::system_account_name, "dan"_n, core_from_string( "80000000.0000" ), core_from_string( "80000000.0000" ) ); + + regproducer( config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), vote( "sam"_n, { config::system_account_name } ) ); + // wait 14 days after min required amount has been staked + produce_block( fc::days(7) ); + BOOST_REQUIRE_EQUAL( success(), vote( "dan"_n, { config::system_account_name } ) ); + produce_block( fc::days(7) ); + produce_block(); + + BOOST_REQUIRE_EXCEPTION( create_accounts_with_resources( { "fail"_n }, "dan"_n ), // dan shouldn't be able to create fail + eosio_assert_message_exception, eosio_assert_message_is( "no active bid for name" ) ); + bidname( "dan"_n, "nofail"_n, core_from_string( "1.0000" ) ); + BOOST_REQUIRE_EQUAL( "assertion failure with message: must increase bid by 10%", bidname( "sam"_n, "nofail"_n, core_from_string( "1.0000" ) )); // didn't increase bid by 10% + BOOST_REQUIRE_EQUAL( success(), bidname( "sam"_n, "nofail"_n, core_from_string( "2.0000" ) )); // didn't increase bid by 10% + produce_block( fc::days(1) ); + produce_block(); + + BOOST_REQUIRE_EXCEPTION( create_accounts_with_resources( { "nofail"_n }, "dan"_n ), // dan shoudn't be able to do this, sam won + eosio_assert_message_exception, eosio_assert_message_is( "only highest bidder can claim" ) ); + //wlog( "verify sam can create nofail" ); + create_accounts_with_resources( { "nofail"_n }, "sam"_n ); // sam should be able to do this, he won the bid + //wlog( "verify nofail can create test.nofail" ); + transfer( "eosio"_n, "nofail"_n, core_from_string( "1000.0000" ) ); + create_accounts_with_resources( { "test.nofail"_n }, "nofail"_n ); // only nofail can create test.nofail + //wlog( "verify dan cannot create test.fail" ); + BOOST_REQUIRE_EXCEPTION( create_accounts_with_resources( { "test.fail"_n }, "dan"_n ), // dan shouldn't be able to do this + eosio_assert_message_exception, eosio_assert_message_is( "only suffix may create this account" ) ); + + create_accounts_with_resources( { "goodgoodgood"_n }, "dan"_n ); /// 12 char names should succeed +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( bid_invalid_names, eosio_system_tester ) try { + create_accounts_with_resources( { "dan"_n } ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "you can only bid on top-level suffix" ), + bidname( "dan"_n, "abcdefg.12345"_n, core_from_string( "1.0000" ) ) ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "the empty name is not a valid account name to bid on" ), + bidname( "dan"_n, ""_n, core_from_string( "1.0000" ) ) ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "13 character names are not valid account names to bid on" ), + bidname( "dan"_n, "abcdefgh12345"_n, core_from_string( "1.0000" ) ) ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "accounts with 12 character names and no dots can be created without bidding required" ), + bidname( "dan"_n, "abcdefg12345"_n, core_from_string( "1.0000" ) ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( multiple_namebids, eosio_system_tester ) try { + + const std::string not_closed_message("auction for name is not closed yet"); + + std::vector accounts = { "alice"_n, "bob"_n, "carl"_n, "david"_n, "eve"_n }; + create_accounts_with_resources( accounts ); + for ( const auto& a: accounts ) { + transfer( config::system_account_name, a, core_from_string( "10000.0000" ) ); + BOOST_REQUIRE_EQUAL( core_from_string( "10000.0000" ), get_balance(a) ); + } + create_accounts_with_resources( { "producer"_n } ); + BOOST_REQUIRE_EQUAL( success(), regproducer( "producer"_n ) ); + + produce_block(); + // stake but not enough to go live + stake_with_transfer( config::system_account_name, "bob"_n, core_from_string( "35000000.0000" ), core_from_string( "35000000.0000" ) ); + stake_with_transfer( config::system_account_name, "carl"_n, core_from_string( "35000000.0000" ), core_from_string( "35000000.0000" ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "bob"_n, { "producer"_n } ) ); + BOOST_REQUIRE_EQUAL( success(), vote( "carl"_n, { "producer"_n } ) ); + + // start bids + bidname( "bob"_n, "prefa"_n, core_from_string("1.0003") ); + BOOST_REQUIRE_EQUAL( core_from_string( "9998.9997" ), get_balance("bob"_n) ); + bidname( "bob"_n, "prefb"_n, core_from_string("1.0000") ); + bidname( "bob"_n, "prefc"_n, core_from_string("1.0000") ); + BOOST_REQUIRE_EQUAL( core_from_string( "9996.9997" ), get_balance("bob"_n) ); + + bidname( "carl"_n, "prefd"_n, core_from_string("1.0000") ); + bidname( "carl"_n, "prefe"_n, core_from_string("1.0000") ); + BOOST_REQUIRE_EQUAL( core_from_string( "9998.0000" ), get_balance("carl"_n) ); + + BOOST_REQUIRE_EQUAL( error("assertion failure with message: account is already highest bidder"), + bidname( "bob"_n, "prefb"_n, core_from_string("1.1001") ) ); + BOOST_REQUIRE_EQUAL( error("assertion failure with message: must increase bid by 10%"), + bidname( "alice"_n, "prefb"_n, core_from_string("1.0999") ) ); + BOOST_REQUIRE_EQUAL( core_from_string( "9996.9997" ), get_balance("bob"_n) ); + BOOST_REQUIRE_EQUAL( core_from_string( "10000.0000" ), get_balance("alice"_n) ); + + + // alice outbids bob on prefb + { + const asset initial_names_balance = get_balance("eosio.names"_n); + BOOST_REQUIRE_EQUAL( success(), + bidname( "alice"_n, "prefb"_n, core_from_string("1.1001") ) ); + // refund bob's failed bid on prefb + BOOST_REQUIRE_EQUAL( success(), push_action( "bob"_n, "bidrefund"_n, mvo()("bidder","bob")("newname", "prefb") ) ); + BOOST_REQUIRE_EQUAL( core_from_string( "9997.9997" ), get_balance("bob"_n) ); + BOOST_REQUIRE_EQUAL( core_from_string( "9998.8999" ), get_balance("alice"_n) ); + BOOST_REQUIRE_EQUAL( initial_names_balance + core_from_string("0.1001"), get_balance("eosio.names"_n) ); + } + + // david outbids carl on prefd + { + BOOST_REQUIRE_EQUAL( core_from_string( "9998.0000" ), get_balance("carl"_n) ); + BOOST_REQUIRE_EQUAL( core_from_string( "10000.0000" ), get_balance("david"_n) ); + BOOST_REQUIRE_EQUAL( success(), + bidname( "david"_n, "prefd"_n, core_from_string("1.9900") ) ); + // refund carls's failed bid on prefd + BOOST_REQUIRE_EQUAL( success(), push_action( "carl"_n, "bidrefund"_n, mvo()("bidder","carl")("newname", "prefd") ) ); + BOOST_REQUIRE_EQUAL( core_from_string( "9999.0000" ), get_balance("carl"_n) ); + BOOST_REQUIRE_EQUAL( core_from_string( "9998.0100" ), get_balance("david"_n) ); + } + + // eve outbids carl on prefe + { + BOOST_REQUIRE_EQUAL( success(), + bidname( "eve"_n, "prefe"_n, core_from_string("1.7200") ) ); + } + + produce_block( fc::days(14) ); + produce_block(); + + // highest bid is from david for prefd but no bids can be closed yet + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefd"_n, "david"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + + // stake enough to go above the 15% threshold + stake_with_transfer( config::system_account_name, "alice"_n, core_from_string( "10000000.0000" ), core_from_string( "10000000.0000" ) ); + BOOST_REQUIRE_EQUAL(0, get_producer_info("producer"_n)["unpaid_blocks"].as()); + BOOST_REQUIRE_EQUAL( success(), vote( "alice"_n, { "producer"_n } ) ); + + // need to wait for 14 days after going live + produce_blocks(10); + produce_block( fc::days(2) ); + produce_blocks( 10 ); + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefd"_n, "david"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + // it's been 14 days, auction for prefd has been closed + produce_block( fc::days(12) ); + create_account_with_resources( "prefd"_n, "david"_n ); + produce_blocks(2); + produce_block( fc::hours(23) ); + // auctions for prefa, prefb, prefc, prefe haven't been closed + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefa"_n, "bob"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefb"_n, "alice"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefc"_n, "bob"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefe"_n, "eve"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + // attemp to create account with no bid + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefg"_n, "alice"_n ), + fc::exception, fc_assert_exception_message_is( "no active bid for name" ) ); + // changing highest bid pushes auction closing time by 24 hours + BOOST_REQUIRE_EQUAL( success(), + bidname( "eve"_n, "prefb"_n, core_from_string("2.1880") ) ); + + produce_block( fc::hours(22) ); + produce_blocks(2); + + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefb"_n, "eve"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + // but changing a bid that is not the highest does not push closing time + BOOST_REQUIRE_EQUAL( success(), + bidname( "carl"_n, "prefe"_n, core_from_string("2.0980") ) ); + produce_block( fc::hours(2) ); + produce_blocks(2); + // bid for prefb has closed, only highest bidder can claim + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefb"_n, "alice"_n ), + eosio_assert_message_exception, eosio_assert_message_is( "only highest bidder can claim" ) ); + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefb"_n, "carl"_n ), + eosio_assert_message_exception, eosio_assert_message_is( "only highest bidder can claim" ) ); + create_account_with_resources( "prefb"_n, "eve"_n ); + + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefe"_n, "carl"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + produce_block(); + produce_block( fc::hours(24) ); + // by now bid for prefe has closed + create_account_with_resources( "prefe"_n, "carl"_n ); + // prefe can now create *.prefe + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "xyz.prefe"_n, "carl"_n ), + fc::exception, fc_assert_exception_message_is("only suffix may create this account") ); + transfer( config::system_account_name, "prefe"_n, core_from_string("10000.0000") ); + create_account_with_resources( "xyz.prefe"_n, "prefe"_n ); + + // other auctions haven't closed + BOOST_REQUIRE_EXCEPTION( create_account_with_resources( "prefa"_n, "bob"_n ), + fc::exception, fc_assert_exception_message_is( not_closed_message ) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( vote_producers_in_and_out, eosio_system_tester ) try { + + const asset net = core_from_string("80.0000"); + const asset cpu = core_from_string("80.0000"); + std::vector voters = { "producvotera"_n, "producvoterb"_n, "producvoterc"_n, "producvoterd"_n }; + for (const auto& v: voters) { + create_account_with_resources(v, config::system_account_name, core_from_string("1.0000"), false, net, cpu); + } + + // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers + std::vector producer_names; + { + producer_names.reserve('z' - 'a' + 1); + const std::string root("defproducer"); + for ( char c = 'a'; c <= 'z'; ++c ) { + producer_names.emplace_back(root + std::string(1, c)); + } + setup_producer_accounts(producer_names); + for (const auto& p: producer_names) { + BOOST_REQUIRE_EQUAL( success(), regproducer(p) ); + produce_blocks(1); + ilog( "------ get pro----------" ); + wdump((p)); + BOOST_TEST(0 == get_producer_info(p)["total_votes"].as()); + } + } + + for (const auto& v: voters) { + transfer( config::system_account_name, v, core_from_string("200000000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL(success(), stake(v, core_from_string("30000000.0000"), core_from_string("30000000.0000")) ); + } + + { + BOOST_REQUIRE_EQUAL(success(), vote("producvotera"_n, vector(producer_names.begin(), producer_names.begin()+20))); + BOOST_REQUIRE_EQUAL(success(), vote("producvoterb"_n, vector(producer_names.begin(), producer_names.begin()+21))); + BOOST_REQUIRE_EQUAL(success(), vote("producvoterc"_n, vector(producer_names.begin(), producer_names.end()))); + } + + // give a chance for everyone to produce blocks + { + produce_blocks(23 * 12 + 20); + bool all_21_produced = true; + for (uint32_t i = 0; i < 21; ++i) { + if (0 == get_producer_info(producer_names[i])["unpaid_blocks"].as()) { + all_21_produced = false; + } + } + bool rest_didnt_produce = true; + for (uint32_t i = 21; i < producer_names.size(); ++i) { + if (0 < get_producer_info(producer_names[i])["unpaid_blocks"].as()) { + rest_didnt_produce = false; + } + } + BOOST_REQUIRE(all_21_produced && rest_didnt_produce); + } + + { + produce_block(fc::hours(7)); + const uint32_t voted_out_index = 20; + const uint32_t new_prod_index = 23; + BOOST_REQUIRE_EQUAL(success(), stake("producvoterd"_n, core_from_string("40000000.0000"), core_from_string("40000000.0000"))); + BOOST_REQUIRE_EQUAL(success(), vote("producvoterd"_n, { producer_names[new_prod_index] })); + BOOST_REQUIRE_EQUAL(0, get_producer_info(producer_names[new_prod_index])["unpaid_blocks"].as()); + produce_blocks(4 * 12 * 21); + BOOST_REQUIRE(0 < get_producer_info(producer_names[new_prod_index])["unpaid_blocks"].as()); + const uint32_t initial_unpaid_blocks = get_producer_info(producer_names[voted_out_index])["unpaid_blocks"].as(); + produce_blocks(2 * 12 * 21); + BOOST_REQUIRE_EQUAL(initial_unpaid_blocks, get_producer_info(producer_names[voted_out_index])["unpaid_blocks"].as()); + produce_block(fc::hours(24)); + BOOST_REQUIRE_EQUAL(success(), vote("producvoterd"_n, { producer_names[voted_out_index] })); + produce_blocks(2 * 12 * 21); + BOOST_REQUIRE(fc::crypto::public_key() != fc::crypto::public_key(get_producer_info(producer_names[voted_out_index])["producer_key"].as_string())); + BOOST_REQUIRE_EQUAL(success(), push_action(producer_names[voted_out_index], "claimrewards"_n, mvo()("owner", producer_names[voted_out_index]))); + } + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( setparams, eosio_system_tester ) try { + //install multisig contract + abi_serializer msig_abi_ser = initialize_multisig(); + auto producer_names = active_and_vote_producers(); + + //helper function + auto push_action_msig = [&]( const account_name& signer, const action_name &name, const variant_object &data, bool auth = true ) -> action_result { + string action_type_name = msig_abi_ser.get_action_type(name); + + action act; + act.account = "eosio.msig"_n; + act.name = name; + act.data = msig_abi_ser.variant_to_binary( action_type_name, data, abi_serializer::create_yield_function(abi_serializer_max_time) ); + + return base_tester::push_action( std::move(act), (auth ? signer : signer == "bob111111111"_n ? "alice1111111"_n : "bob111111111"_n).to_uint64_t() ); + }; + + // test begins + vector prod_perms; + for ( auto& x : producer_names ) { + prod_perms.push_back( { name(x), config::active_name } ); + } + + eosio::chain::chain_config params; + params = control->get_global_properties().configuration; + //change some values + params.max_block_net_usage += 10; + params.max_transaction_lifetime += 1; + + transaction trx; + { + fc::variant pretty_trx = fc::mutable_variant_object() + ("expiration", "2020-01-01T00:30") + ("ref_block_num", 2) + ("ref_block_prefix", 3) + ("net_usage_words", 0) + ("max_cpu_usage_ms", 0) + ("delay_sec", 0) + ("actions", fc::variants({ + fc::mutable_variant_object() + ("account", name(config::system_account_name)) + ("name", "setparams") + ("authorization", vector{ { config::system_account_name, config::active_name } }) + ("data", fc::mutable_variant_object() + ("params", params) + ) + }) + ); + abi_serializer::from_variant(pretty_trx, trx, get_resolver(), abi_serializer::create_yield_function(abi_serializer_max_time)); + } + + BOOST_REQUIRE_EQUAL(success(), push_action_msig( "alice1111111"_n, "propose"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "setparams1") + ("trx", trx) + ("requested", prod_perms) + ) + ); + + // get 16 approvals + for ( size_t i = 0; i < 15; ++i ) { + BOOST_REQUIRE_EQUAL(success(), push_action_msig( name(producer_names[i]), "approve"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "setparams1") + ("level", permission_level{ name(producer_names[i]), config::active_name }) + ) + ); + } + + transaction_trace_ptr trace; + control->applied_transaction.connect( + [&]( std::tuple p ) { + trace = std::get<0>(p); + } ); + + BOOST_REQUIRE_EQUAL(success(), push_action_msig( "alice1111111"_n, "exec"_n, mvo() + ("proposer", "alice1111111"_n) + ("proposal_name", "setparams1") + ("executer", "alice1111111"_n) + ) + ); + + BOOST_REQUIRE( bool(trace) ); + BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status ); + + produce_blocks( 250 ); + + // make sure that changed parameters were applied + auto active_params = control->get_global_properties().configuration; + BOOST_REQUIRE_EQUAL( params.max_block_net_usage, active_params.max_block_net_usage ); + BOOST_REQUIRE_EQUAL( params.max_transaction_lifetime, active_params.max_transaction_lifetime ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( setram_effect, eosio_system_tester ) try { + + const asset net = core_from_string("8.0000"); + const asset cpu = core_from_string("8.0000"); + std::vector accounts = { "aliceaccount"_n, "bobbyaccount"_n }; + for (const auto& a: accounts) { + create_account_with_resources(a, config::system_account_name, core_from_string("1.0000"), false, net, cpu); + } + + { + const auto name_a = accounts[0]; + transfer( config::system_account_name, name_a, core_from_string("1000.0000") ); + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance(name_a) ); + const uint64_t init_bytes_a = get_total_stake(name_a)["ram_bytes"].as_uint64(); + BOOST_REQUIRE_EQUAL( success(), buyram( name_a, name_a, core_from_string("300.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance(name_a) ); + const uint64_t bought_bytes_a = get_total_stake(name_a)["ram_bytes"].as_uint64() - init_bytes_a; + + // after buying and selling balance should be 700 + 300 * 0.995 * 0.995 = 997.0075 (actually 997.0074 due to rounding fees up) + BOOST_REQUIRE_EQUAL( success(), sellram(name_a, bought_bytes_a ) ); + BOOST_REQUIRE_EQUAL( core_from_string("997.0074"), get_balance(name_a) ); + } + + { + const auto name_b = accounts[1]; + transfer( config::system_account_name, name_b, core_from_string("1000.0000") ); + BOOST_REQUIRE_EQUAL( core_from_string("1000.0000"), get_balance(name_b) ); + const uint64_t init_bytes_b = get_total_stake(name_b)["ram_bytes"].as_uint64(); + // name_b buys ram at current price + BOOST_REQUIRE_EQUAL( success(), buyram( name_b, name_b, core_from_string("300.0000") ) ); + BOOST_REQUIRE_EQUAL( core_from_string("700.0000"), get_balance(name_b) ); + const uint64_t bought_bytes_b = get_total_stake(name_b)["ram_bytes"].as_uint64() - init_bytes_b; + + // increase max_ram_size, ram bought by name_b loses part of its value + BOOST_REQUIRE_EQUAL( wasm_assert_msg("ram may only be increased"), + push_action(config::system_account_name, "setram"_n, mvo()("max_ram_size", 64ll*1024 * 1024 * 1024)) ); + BOOST_REQUIRE_EQUAL( error("missing authority of eosio"), + push_action(name_b, "setram"_n, mvo()("max_ram_size", 80ll*1024 * 1024 * 1024)) ); + BOOST_REQUIRE_EQUAL( success(), + push_action(config::system_account_name, "setram"_n, mvo()("max_ram_size", 80ll*1024 * 1024 * 1024)) ); + + BOOST_REQUIRE_EQUAL( success(), sellram(name_b, bought_bytes_b ) ); + BOOST_REQUIRE( core_from_string("900.0000") < get_balance(name_b) ); + BOOST_REQUIRE( core_from_string("950.0000") > get_balance(name_b) ); + } + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( ram_inflation, eosio_system_tester ) try { + + const uint64_t init_max_ram_size = 64ll*1024 * 1024 * 1024; + + BOOST_REQUIRE_EQUAL( init_max_ram_size, get_global_state()["max_ram_size"].as_uint64() ); + produce_blocks(20); + BOOST_REQUIRE_EQUAL( init_max_ram_size, get_global_state()["max_ram_size"].as_uint64() ); + transfer( config::system_account_name, "alice1111111"_n, core_from_string("1000.0000"), config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + produce_blocks(3); + BOOST_REQUIRE_EQUAL( init_max_ram_size, get_global_state()["max_ram_size"].as_uint64() ); + uint16_t rate = 1000; + BOOST_REQUIRE_EQUAL( success(), push_action( config::system_account_name, "setramrate"_n, mvo()("bytes_per_block", rate) ) ); + BOOST_REQUIRE_EQUAL( rate, get_global_state2()["new_ram_per_block"].as() ); + // last time update_ram_supply called is in buyram, num of blocks since then to + // the block that includes the setramrate action is 1 + 3 = 4. + // However, those 4 blocks were accumulating at a rate of 0, so the max_ram_size should not have changed. + BOOST_REQUIRE_EQUAL( init_max_ram_size, get_global_state()["max_ram_size"].as_uint64() ); + // But with additional blocks, it should start accumulating at the new rate. + uint64_t cur_ram_size = get_global_state()["max_ram_size"].as_uint64(); + produce_blocks(10); + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("100.0000") ) ); + BOOST_REQUIRE_EQUAL( cur_ram_size + 11 * rate, get_global_state()["max_ram_size"].as_uint64() ); + cur_ram_size = get_global_state()["max_ram_size"].as_uint64(); + produce_blocks(5); + BOOST_REQUIRE_EQUAL( cur_ram_size, get_global_state()["max_ram_size"].as_uint64() ); + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, 100 ) ); + BOOST_REQUIRE_EQUAL( cur_ram_size + 6 * rate, get_global_state()["max_ram_size"].as_uint64() ); + cur_ram_size = get_global_state()["max_ram_size"].as_uint64(); + produce_blocks(); + BOOST_REQUIRE_EQUAL( success(), buyrambytes( "alice1111111"_n, "alice1111111"_n, 100 ) ); + BOOST_REQUIRE_EQUAL( cur_ram_size + 2 * rate, get_global_state()["max_ram_size"].as_uint64() ); + + BOOST_REQUIRE_EQUAL( error("missing authority of eosio"), + push_action( "alice1111111"_n, "setramrate"_n, mvo()("bytes_per_block", rate) ) ); + + cur_ram_size = get_global_state()["max_ram_size"].as_uint64(); + produce_blocks(10); + uint16_t old_rate = rate; + rate = 5000; + BOOST_REQUIRE_EQUAL( success(), push_action( config::system_account_name, "setramrate"_n, mvo()("bytes_per_block", rate) ) ); + BOOST_REQUIRE_EQUAL( cur_ram_size + 11 * old_rate, get_global_state()["max_ram_size"].as_uint64() ); + produce_blocks(5); + BOOST_REQUIRE_EQUAL( success(), buyrambytes( "alice1111111"_n, "alice1111111"_n, 100 ) ); + BOOST_REQUIRE_EQUAL( cur_ram_size + 11 * old_rate + 6 * rate, get_global_state()["max_ram_size"].as_uint64() ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( eosioram_ramusage, eosio_system_tester ) try { + BOOST_REQUIRE_EQUAL( core_from_string("0.0000"), get_balance( "alice1111111"_n ) ); + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "eosio"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("1000.0000") ) ); + + BOOST_REQUIRE_EQUAL( false, get_row_by_account( "eosio.token"_n, "alice1111111"_n, "accounts"_n, account_name(symbol{}.to_symbol_code()) ).empty() ); + + //remove row + base_tester::push_action( "eosio.token"_n, "close"_n, "alice1111111"_n, mvo() + ( "owner", "alice1111111"_n ) + ( "symbol", symbol{} ) + ); + BOOST_REQUIRE_EQUAL( true, get_row_by_account( "eosio.token"_n, "alice1111111"_n, "accounts"_n, account_name(symbol{}.to_symbol_code()) ).empty() ); + + auto rlm = control->get_resource_limits_manager(); + auto eosioram_ram_usage = rlm.get_account_ram_usage("eosio.ram"_n); + auto alice_ram_usage = rlm.get_account_ram_usage("alice1111111"_n); + + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, 2048 ) ); + + //make sure that ram was billed to alice, not to eosio.ram + BOOST_REQUIRE_EQUAL( true, alice_ram_usage < rlm.get_account_ram_usage("alice1111111"_n) ); + BOOST_REQUIRE_EQUAL( eosioram_ram_usage, rlm.get_account_ram_usage("eosio.ram"_n) ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( ram_gift, eosio_system_tester ) try { + active_and_vote_producers(); + + auto rlm = control->get_resource_limits_manager(); + int64_t ram_bytes_orig, net_weight, cpu_weight; + rlm.get_account_limits( "alice1111111"_n, ram_bytes_orig, net_weight, cpu_weight ); + + /* + * It seems impossible to write this test, because buyrambytes action doesn't give you exact amount of bytes requested + * + //check that it's possible to create account bying required_bytes(2724) + userres table(112) + userres row(160) - ram_gift_bytes(1400) + create_account_with_resources( "abcdefghklmn"_n, "alice1111111"_n, 2724 + 112 + 160 - 1400 ); + + //check that one byte less is not enough + BOOST_REQUIRE_THROW( create_account_with_resources( "abcdefghklmn"_n, "alice1111111"_n, 2724 + 112 + 160 - 1400 - 1 ), + ram_usage_exceeded ); + */ + + //check that stake/unstake keeps the gift + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1000.0000"), "eosio"_n ); + BOOST_REQUIRE_EQUAL( success(), stake( "eosio"_n, "alice1111111"_n, core_from_string("200.0000"), core_from_string("100.0000") ) ); + int64_t ram_bytes_after_stake; + rlm.get_account_limits( "alice1111111"_n, ram_bytes_after_stake, net_weight, cpu_weight ); + BOOST_REQUIRE_EQUAL( ram_bytes_orig, ram_bytes_after_stake ); + + BOOST_REQUIRE_EQUAL( success(), unstake( "eosio"_n, "alice1111111"_n, core_from_string("20.0000"), core_from_string("10.0000") ) ); + int64_t ram_bytes_after_unstake; + rlm.get_account_limits( "alice1111111"_n, ram_bytes_after_unstake, net_weight, cpu_weight ); + BOOST_REQUIRE_EQUAL( ram_bytes_orig, ram_bytes_after_unstake ); + + uint64_t ram_gift = 1400; + + int64_t ram_bytes; + BOOST_REQUIRE_EQUAL( success(), buyram( "alice1111111"_n, "alice1111111"_n, core_from_string("1000.0000") ) ); + rlm.get_account_limits( "alice1111111"_n, ram_bytes, net_weight, cpu_weight ); + auto userres = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( userres["ram_bytes"].as_uint64() + ram_gift, ram_bytes ); + + BOOST_REQUIRE_EQUAL( success(), sellram( "alice1111111"_n, 1024 ) ); + rlm.get_account_limits( "alice1111111"_n, ram_bytes, net_weight, cpu_weight ); + userres = get_total_stake( "alice1111111"_n ); + BOOST_REQUIRE_EQUAL( userres["ram_bytes"].as_uint64() + ram_gift, ram_bytes ); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE( change_limited_account_back_to_unlimited, eosio_system_tester ) try { + BOOST_REQUIRE( get_total_stake( "eosio"_n ).is_null() ); + + transfer( "eosio"_n, "alice1111111"_n, core_from_string("1.0000") ); + + auto error_msg = stake( "alice1111111"_n, "eosio"_n, core_from_string("0.0000"), core_from_string("1.0000") ); + auto semicolon_pos = error_msg.find(';'); + + BOOST_REQUIRE_EQUAL( error("account eosio has insufficient ram"), + error_msg.substr(0, semicolon_pos) ); + + int64_t ram_bytes_needed = 0; + { + std::istringstream s( error_msg ); + s.seekg( semicolon_pos + 7, std::ios_base::beg ); + s >> ram_bytes_needed; + ram_bytes_needed += 256; // enough room to cover total_resources_table + } + + push_action( "eosio"_n, "setalimits"_n, mvo() + ("account", "eosio"_n) + ("ram_bytes", ram_bytes_needed) + ("net_weight", -1) + ("cpu_weight", -1) + ); + + stake( "alice1111111"_n, "eosio"_n, core_from_string("0.0000"), core_from_string("1.0000") ); + + REQUIRE_MATCHING_OBJECT( get_total_stake( "eosio"_n ), mvo() + ("owner", "eosio"_n) + ("net_weight", core_from_string("0.0000")) + ("cpu_weight", core_from_string("1.0000")) + ("ram_bytes", 0) + ); + + BOOST_REQUIRE_EQUAL( wasm_assert_msg( "only supports unlimited accounts" ), + push_action( "eosio"_n, "setalimits"_n, mvo() + ("account", "eosio"_n) + ("ram_bytes", ram_bytes_needed) + ("net_weight", -1) + ("cpu_weight", -1) + ) + ); + + BOOST_REQUIRE_EQUAL( error( "transaction net usage is too high: 128 > 0" ), + push_action( "eosio"_n, "setalimits"_n, mvo() + ("account", "eosio.saving") + ("ram_bytes", -1) + ("net_weight", -1) + ("cpu_weight", -1) + ) + ); + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/eosio_system_tester.hpp b/unittests/eosio_system_tester.hpp index d793e938c4..d8988c21ed 100644 --- a/unittests/eosio_system_tester.hpp +++ b/unittests/eosio_system_tester.hpp @@ -280,6 +280,54 @@ class eosio_system_tester : public validating_tester { ); } + action_result deposit( const account_name& owner, const asset& amount ) { + return push_action( name(owner), "deposit"_n, mvo() + ("owner", owner) + ("amount", amount) + ); + } + + action_result withdraw( const account_name& owner, const asset& amount ) { + return push_action( name(owner), "withdraw"_n, mvo() + ("owner", owner) + ("amount", amount) + ); + } + + asset get_rex_balance( const account_name& act ) const { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, "rexbal"_n, act ); + return data.empty() ? asset(0, symbol(SY(4, REX))) : abi_ser.binary_to_variant("rex_balance", data, abi_serializer::create_yield_function(abi_serializer_max_time))["rex_balance"].as(); + } + + asset get_rex_fund( const account_name& act ) const { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, "rexfund"_n, act ); + return data.empty() ? asset(0, symbol{}) : abi_ser.binary_to_variant("rex_fund", data, abi_serializer::create_yield_function(abi_serializer_max_time))["balance"].as(); + } + + void setup_rex_accounts( const std::vector& accounts, + const asset& init_balance, + const asset& net = core_from_string("80.0000"), + const asset& cpu = core_from_string("80.0000"), + bool deposit_into_rex_fund = true ) { + const asset nstake = core_from_string("10.0000"); + const asset cstake = core_from_string("10.0000"); + create_account_with_resources( "proxyaccount"_n, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + BOOST_REQUIRE_EQUAL( success(), push_action( "proxyaccount"_n, "regproxy"_n, mvo()("proxy", "proxyaccount")("isproxy", true) ) ); + for (const auto& a: accounts) { + create_account_with_resources( a, config::system_account_name, core_from_string("1.0000"), false, net, cpu ); + transfer( config::system_account_name, a, init_balance + nstake + cstake, config::system_account_name ); + BOOST_REQUIRE_EQUAL( success(), stake( a, a, nstake, cstake) ); + BOOST_REQUIRE_EQUAL( success(), vote( a, { }, "proxyaccount"_n ) ); + BOOST_REQUIRE_EQUAL( init_balance, get_balance(a) ); + BOOST_REQUIRE_EQUAL( asset::from_string("0.0000 REX"), get_rex_balance(a) ); + if (deposit_into_rex_fund) { + BOOST_REQUIRE_EQUAL( success(), deposit( a, init_balance ) ); + BOOST_REQUIRE_EQUAL( init_balance, get_rex_fund( a ) ); + BOOST_REQUIRE_EQUAL( 0, get_balance( a ).get_amount() ); + } + } + } + static fc::variant_object producer_parameters_example( int n ) { return mutable_variant_object() ("max_block_net_usage", 10000000 + n ) @@ -345,6 +393,11 @@ class eosio_system_tester : public validating_tester { return abi_ser.binary_to_variant( "producer_info", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); } + fc::variant get_producer_info2( const account_name& act ) { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, "producers2"_n, act ); + return abi_ser.binary_to_variant( "producer_info2", data, abi_serializer::create_yield_function(abi_serializer_max_time) ); + } + void create_currency( name contract, name manager, asset maxsupply ) { auto act = mutable_variant_object() ("issuer", manager ) @@ -369,6 +422,32 @@ class eosio_system_tester : public validating_tester { ); } + void issue_and_transfer( const name& to, const asset& amount, const name& manager = config::system_account_name ) { + signed_transaction trx; + trx.actions.emplace_back( get_action( "eosio.token"_n, "issue"_n, + vector{{manager, config::active_name}}, + mutable_variant_object() + ("to", manager ) + ("quantity", amount ) + ("memo", "") + ) + ); + if ( to != manager ) { + trx.actions.emplace_back( get_action( "eosio.token"_n, "transfer"_n, + vector{{manager, config::active_name}}, + mutable_variant_object() + ("from", manager) + ("to", to ) + ("quantity", amount ) + ("memo", "") + ) + ); + } + set_transaction_headers( trx ); + trx.sign( get_private_key( manager, "active" ), control->get_chain_id() ); + push_transaction( trx ); + } + double stake2votes( asset stake ) { auto now = control->pending_block_time().time_since_epoch().count() / 1000000; return stake.get_amount() * pow(2, int64_t((now - (config::block_timestamp_epoch / 1000)) / (86400 * 7))/ double(52) ); // 52 week periods (i.e. ~years) @@ -389,11 +468,24 @@ class eosio_system_tester : public validating_tester { return get_stats("4," CORE_SYMBOL_NAME)["supply"].as(); } + uint64_t microseconds_since_epoch_of_iso_string( const fc::variant& v ) { + return static_cast( time_point::from_iso_string( v.as_string() ).time_since_epoch().count() ); + } + fc::variant get_global_state() { vector data = get_row_by_account( config::system_account_name, config::system_account_name, "global"_n, "global"_n ); if (data.empty()) std::cout << "\nData is empty\n" << std::endl; return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state", data, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + } + + fc::variant get_global_state2() { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, "global2"_n, "global2"_n ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state2", data, abi_serializer::create_yield_function(abi_serializer_max_time) ); + } + fc::variant get_global_state3() { + vector data = get_row_by_account( config::system_account_name, config::system_account_name, "global3"_n, "global3"_n ); + return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state3", data, abi_serializer::create_yield_function(abi_serializer_max_time) ); } fc::variant get_refund_request( name account ) { From 846c214781d9263f58868083f9cffcbe1f190075 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 Jan 2024 12:48:49 -0600 Subject: [PATCH 2/5] GH-2033 Revert to 5.0 implementation --- libraries/chain/block_header_state_legacy.cpp | 73 ++++++++----------- .../eosio/chain/block_header_state_legacy.hpp | 3 - 2 files changed, 30 insertions(+), 46 deletions(-) diff --git a/libraries/chain/block_header_state_legacy.cpp b/libraries/chain/block_header_state_legacy.cpp index 156efb4d24..49a4e3f509 100644 --- a/libraries/chain/block_header_state_legacy.cpp +++ b/libraries/chain/block_header_state_legacy.cpp @@ -4,8 +4,6 @@ namespace eosio { namespace chain { -#warning Add last_proposed_finalizer_policy_generation to snapshot_block_header_state_v3, see header file TODO - namespace detail { bool is_builtin_activated( const protocol_feature_activation_set_ptr& pfa, const protocol_feature_set& pfs, @@ -15,12 +13,6 @@ namespace eosio { namespace chain { const auto& protocol_features = pfa->protocol_features; return digest && protocol_features.find(*digest) != protocol_features.end(); } - - uint32_t get_next_next_round_block_num( block_timestamp_type t, uint32_t block_num ) { - auto index = t.slot % config::producer_repetitions; // current index in current round - // (increment to the end of this round ) + next round - return block_num + (config::producer_repetitions - index) + config::producer_repetitions; - } } producer_authority block_header_state_legacy::get_scheduled_producer( block_timestamp_type t )const { @@ -43,9 +35,6 @@ namespace eosio { namespace chain { return blocknums[ index ]; } - // create pending_block_header_state from this for `when` - // If hotstuff_activated then use new consensus values and simpler active schedule update. - // If notstuff is not activated then use previous pre-hotstuff consensus logic. pending_block_header_state_legacy block_header_state_legacy::next( block_timestamp_type when, uint16_t num_prev_blocks_to_confirm )const { @@ -57,40 +46,36 @@ namespace eosio { namespace chain { (when = header.timestamp).slot++; } + auto proauth = get_scheduled_producer(when); + + auto itr = producer_to_last_produced.find( proauth.producer_name ); + if( itr != producer_to_last_produced.end() ) { + EOS_ASSERT( itr->second < (block_num+1) - num_prev_blocks_to_confirm, producer_double_confirm, + "producer ${prod} double-confirming known range", + ("prod", proauth.producer_name)("num", block_num+1) + ("confirmed", num_prev_blocks_to_confirm)("last_produced", itr->second) ); + } + result.block_num = block_num + 1; result.previous = id; result.timestamp = when; + result.confirmed = num_prev_blocks_to_confirm; result.active_schedule_version = active_schedule.version; result.prev_activated_protocol_features = activated_protocol_features; - auto proauth = get_scheduled_producer(when); - result.valid_block_signing_authority = proauth.authority; result.producer = proauth.producer_name; - result.last_proposed_finalizer_policy_generation = last_proposed_finalizer_policy_generation; result.blockroot_merkle = blockroot_merkle; result.blockroot_merkle.append( id ); - result.prev_pending_schedule = pending_schedule; - - auto itr = producer_to_last_produced.find( proauth.producer_name ); - if( itr != producer_to_last_produced.end() ) { - EOS_ASSERT( itr->second < (block_num+1) - num_prev_blocks_to_confirm, producer_double_confirm, - "producer ${prod} double-confirming known range", - ("prod", proauth.producer_name)("num", block_num+1) - ("confirmed", num_prev_blocks_to_confirm)("last_produced", itr->second) ); - } - - result.confirmed = num_prev_blocks_to_confirm; - /// grow the confirmed count static_assert(std::numeric_limits::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations"); // This uses the previous block active_schedule because thats the "schedule" that signs and therefore confirms _this_ block auto num_active_producers = active_schedule.producers.size(); uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1; - + if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) { result.confirm_count.reserve( confirm_count.size() + 1 ); result.confirm_count = confirm_count; @@ -107,37 +92,39 @@ namespace eosio { namespace chain { int32_t i = (int32_t)(result.confirm_count.size() - 1); uint32_t blocks_to_confirm = num_prev_blocks_to_confirm + 1; /// confirm the head block too while( i >= 0 && blocks_to_confirm ) { - --result.confirm_count[i]; - //idump((confirm_count[i])); - if( result.confirm_count[i] == 0 ) - { + --result.confirm_count[i]; + //idump((confirm_count[i])); + if( result.confirm_count[i] == 0 ) + { uint32_t block_num_for_i = result.block_num - (uint32_t)(result.confirm_count.size() - 1 - i); new_dpos_proposed_irreversible_blocknum = block_num_for_i; //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum)); - + if (i == static_cast(result.confirm_count.size() - 1)) { result.confirm_count.resize(0); } else { memmove( &result.confirm_count[0], &result.confirm_count[i + 1], result.confirm_count.size() - i - 1); result.confirm_count.resize( result.confirm_count.size() - i - 1 ); } - + break; - } - --i; - --blocks_to_confirm; + } + --i; + --blocks_to_confirm; } - + result.dpos_proposed_irreversible_blocknum = new_dpos_proposed_irreversible_blocknum; result.dpos_irreversible_blocknum = calc_dpos_last_irreversible( proauth.producer_name ); - + + result.prev_pending_schedule = pending_schedule; + if( pending_schedule.schedule.producers.size() && result.dpos_irreversible_blocknum >= pending_schedule.schedule_lib_num ) { result.active_schedule = pending_schedule.schedule; - + flat_map new_producer_to_last_produced; - + for( const auto& pro : result.active_schedule.producers ) { if( pro.producer_name == proauth.producer_name ) { new_producer_to_last_produced[pro.producer_name] = result.block_num; @@ -151,7 +138,7 @@ namespace eosio { namespace chain { } } new_producer_to_last_produced[proauth.producer_name] = result.block_num; - + result.producer_to_last_produced = std::move( new_producer_to_last_produced ); flat_map new_producer_to_last_implied_irb; @@ -168,9 +155,9 @@ namespace eosio { namespace chain { } } } - + result.producer_to_last_implied_irb = std::move( new_producer_to_last_implied_irb ); - + result.was_pending_promoted = true; } else { result.active_schedule = active_schedule; diff --git a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp index 9ab9d6a1ad..eba898cf1b 100644 --- a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -58,7 +57,6 @@ namespace detail { uint32_t dpos_proposed_irreversible_blocknum = 0; uint32_t dpos_irreversible_blocknum = 0; producer_authority_schedule active_schedule; - uint32_t last_proposed_finalizer_policy_generation = 0; // TODO: Add to snapshot_block_header_state_v3 incremental_canonical_merkle_tree blockroot_merkle; flat_map producer_to_last_produced; flat_map producer_to_last_implied_irb; @@ -195,7 +193,6 @@ FC_REFLECT( eosio::chain::detail::block_header_state_legacy_common, (dpos_proposed_irreversible_blocknum) (dpos_irreversible_blocknum) (active_schedule) - (last_proposed_finalizer_policy_generation) (blockroot_merkle) (producer_to_last_produced) (producer_to_last_implied_irb) From 42513feb3699d185eb4fcf3d6f0f033bbe41d932 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 Jan 2024 12:49:35 -0600 Subject: [PATCH 3/5] GH-2033 Move get_next_next_round_block_num from legacy to where it is used. --- libraries/chain/block_header_state.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index c73b39a135..982d2adc4f 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -4,6 +4,16 @@ namespace eosio::chain { +#warning Add last_proposed_finalizer_policy_generation to snapshot_block_header_state_v3, see header file TODO + + namespace detail { + uint32_t get_next_next_round_block_num( block_timestamp_type t, uint32_t block_num ) { + auto index = t.slot % config::producer_repetitions; // current index in current round + // (increment to the end of this round ) + next round + return block_num + (config::producer_repetitions - index) + config::producer_repetitions; + } + } + block_header_state_core block_header_state_core::next(uint32_t last_qc_block_height, bool is_last_qc_strong) const { // no state change if last_qc_block_height is the same if (last_qc_block_height == this->last_qc_block_height) { From 2f7c1277a7218054a1ec4ea127f07729621bd2cd Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 Jan 2024 12:50:03 -0600 Subject: [PATCH 4/5] GH-2033 Add head_active_producers() needed by tester --- libraries/chain/controller.cpp | 4 ++++ libraries/chain/include/eosio/chain/controller.hpp | 1 + libraries/testing/tester.cpp | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 938c2d6438..805b0bf7cf 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3986,6 +3986,10 @@ const producer_authority_schedule& controller::active_producers()const { return my->pending->active_producers(); } +const producer_authority_schedule& controller::head_active_producers()const { + return my->block_data.head_active_schedule_auth(); +} + const producer_authority_schedule& controller::pending_producers()const { if( !(my->pending) ) return my->block_data.head_pending_schedule_auth(); // [greg todo] implement pending_producers correctly for IF mode diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 03de4f8e44..b563e4a5ca 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -249,6 +249,7 @@ namespace eosio::chain { uint32_t pending_block_num()const; const producer_authority_schedule& active_producers()const; + const producer_authority_schedule& head_active_producers()const; const producer_authority_schedule& pending_producers()const; std::optional proposed_producers()const; diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index e4abdace2b..e002c1507e 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -437,7 +437,7 @@ namespace eosio { namespace testing { void base_tester::_start_block(fc::time_point block_time) { auto head_block_number = control->head_block_num(); - auto producer = control->active_producers().get_scheduled_producer(block_time); + auto producer = control->head_active_producers().get_scheduled_producer(block_time); auto last_produced_block_num = control->last_irreversible_block_num(); auto itr = last_produced_block.find(producer.producer_name); From c878aee5a0750b12786ccd0d91c4ad3cd7780912 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 Jan 2024 12:50:22 -0600 Subject: [PATCH 5/5] GH-2033 Update deep-mind test --- unittests/deep-mind/deep-mind.log | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unittests/deep-mind/deep-mind.log b/unittests/deep-mind/deep-mind.log index 00d57243ba..9534635261 100644 --- a/unittests/deep-mind/deep-mind.log +++ b/unittests/deep-mind/deep-mind.log @@ -29,7 +29,7 @@ DMLOG TRX_OP CREATE onblock ef240e45433c433de4061120632aa06e32ec3e77048abf55c62e DMLOG APPLIED_TRANSACTION 2 ef240e45433c433de4061120632aa06e32ec3e77048abf55c62e0612c22548ed02000000013b3d4b010000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e801006400000000000000000000000000000000000000000001010000010000000000ea305506d4766d9dbedb630ad9546f583a9809539cf09d38fd1554b4216503113ff4e501000000000000000100000000000000010000000000ea3055010000000000000000000000000000ea30550000000000ea305500000000221acfa4010000000000ea305500000000a8ed323274003b3d4b000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044423079ed372a4dda0bf89c3a594df409eaa8c1535451b7d5ca6a3d7a37691200000000000000000000000000000000ef240e45433c433de4061120632aa06e32ec3e77048abf55c62e0612c22548ed02000000013b3d4b010000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e80000000000000000 DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":0,"value_ex":0,"consumed":0},"average_block_cpu_usage":{"last_ordinal":0,"value_ex":0,"consumed":0},"pending_net_usage":0,"pending_cpu_usage":100,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1048576,"virtual_cpu_limit":200000} DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":2,"value_ex":0,"consumed":0},"average_block_cpu_usage":{"last_ordinal":2,"value_ex":833334,"consumed":100},"pending_net_usage":0,"pending_cpu_usage":0,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1049625,"virtual_cpu_limit":200200} -DMLOG ACCEPTED_BLOCK 2 02000000020000000000000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add801000000000001000000015ab65a885a31e441ac485ebd2aeba87bf7ee6e7bcc40bf3a24506ba10100000000000000010000000000ea305502000000010000000000ea305500000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100000000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e8013b3d4b0000000000ea30550000000000015ab65a885a31e441ac485ebd2aeba87bf7ee6e7bcc40bf3a24506ba1000000000000000000000000000000000000000000000000000000000000000062267e8b11d7d8f28e1f991a4de2b08cf92500861af2795765bdc9263cd6f4cd000000000001000021010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0020701fd1d2d6fbca71ad1df5bd09a987d6863f301b93acfc1c34857e4b2f53821a0b4ca8483cf594f845f3f4fc155dbbc98009cb9c7b7b60d449f922dc00abcb0f0000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef26198000000000001010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0001013b3d4b0000000000ea30550000000000015ab65a885a31e441ac485ebd2aeba87bf7ee6e7bcc40bf3a24506ba1000000000000000000000000000000000000000000000000000000000000000062267e8b11d7d8f28e1f991a4de2b08cf92500861af2795765bdc9263cd6f4cd000000000001000021010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0020701fd1d2d6fbca71ad1df5bd09a987d6863f301b93acfc1c34857e4b2f53821a0b4ca8483cf594f845f3f4fc155dbbc98009cb9c7b7b60d449f922dc00abcb0f000001 +DMLOG ACCEPTED_BLOCK 2 02000000020000000000000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add8010001000000015ab65a885a31e441ac485ebd2aeba87bf7ee6e7bcc40bf3a24506ba10100000000000000010000000000ea305502000000010000000000ea305500000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100000000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e8013b3d4b0000000000ea30550000000000015ab65a885a31e441ac485ebd2aeba87bf7ee6e7bcc40bf3a24506ba1000000000000000000000000000000000000000000000000000000000000000062267e8b11d7d8f28e1f991a4de2b08cf92500861af2795765bdc9263cd6f4cd000000000001000021010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0020701fd1d2d6fbca71ad1df5bd09a987d6863f301b93acfc1c34857e4b2f53821a0b4ca8483cf594f845f3f4fc155dbbc98009cb9c7b7b60d449f922dc00abcb0f0000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef26198000000000001010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0001013b3d4b0000000000ea30550000000000015ab65a885a31e441ac485ebd2aeba87bf7ee6e7bcc40bf3a24506ba1000000000000000000000000000000000000000000000000000000000000000062267e8b11d7d8f28e1f991a4de2b08cf92500861af2795765bdc9263cd6f4cd000000000001000021010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0020701fd1d2d6fbca71ad1df5bd09a987d6863f301b93acfc1c34857e4b2f53821a0b4ca8483cf594f845f3f4fc155dbbc98009cb9c7b7b60d449f922dc00abcb0f000001 DMLOG START_BLOCK 3 DMLOG CREATION_OP ROOT 0 DMLOG RLIMIT_OP ACCOUNT_USAGE UPD {"owner":"eosio","net_usage":{"last_ordinal":1262304002,"value_ex":0,"consumed":0},"cpu_usage":{"last_ordinal":1262304002,"value_ex":1157,"consumed":101},"ram_usage":2724} @@ -137,7 +137,7 @@ DMLOG RLIMIT_OP ACCOUNT_USAGE UPD {"owner":"eosio","net_usage":{"last_ordinal":1 DMLOG APPLIED_TRANSACTION 3 4b44a1f39c39048a1fa53c7070cea6a57f0afbb982370bcaa03d4d735797778c03000000023b3d4b010000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb0100d007000010000000000000000080000000000000000001010000010000000000ea30551b7179e66f67158e50d547f27fb19f7660419a39880b1e596665bf44d4ce7fe21a000000000000001a00000000000000010000000000ea30551a0000000000000001010000000000ea30550000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232208cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7000000000000000000004b44a1f39c39048a1fa53c7070cea6a57f0afbb982370bcaa03d4d735797778c03000000023b3d4b010000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb0000000000000000 DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":2,"value_ex":0,"consumed":0},"average_block_cpu_usage":{"last_ordinal":2,"value_ex":833334,"consumed":100},"pending_net_usage":9952,"pending_cpu_usage":48100,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1049625,"virtual_cpu_limit":200200} DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":3,"value_ex":82933334,"consumed":9952},"average_block_cpu_usage":{"last_ordinal":3,"value_ex":401659723,"consumed":48101},"pending_net_usage":0,"pending_cpu_usage":0,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1050675,"virtual_cpu_limit":200400} -DMLOG ACCEPTED_BLOCK 3 03000000030000000200000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add8010000000000012d5b1b639d6ae94fcdd0536b224644931573d1ccb2a0c548613cd1feea18888b0200000000000000010000000000ea305503000000010000000000ea305502000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100000000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb023b3d4b0000000000ea305500000000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e82d8cdf5a48c796389dd8078c999af1b8542b7e1d56784775146ab9963566974d9426f419e11a481547c9e05fe24a17d138a7fcdcf5ff599a09e03630dec0c79900000000000000207b3e5f9a58ec8f603285898120bc8c58e80f603b37dddf6f00bf79d75d62f5024d1665fa7e7af18a5f639ba3d3bc6d6858c31658a0473ed2ce4c879daa5efe560000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef26198000000000001010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0001023b3d4b0000000000ea305500000000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e82d8cdf5a48c796389dd8078c999af1b8542b7e1d56784775146ab9963566974d9426f419e11a481547c9e05fe24a17d138a7fcdcf5ff599a09e03630dec0c79900000000000000207b3e5f9a58ec8f603285898120bc8c58e80f603b37dddf6f00bf79d75d62f5024d1665fa7e7af18a5f639ba3d3bc6d6858c31658a0473ed2ce4c879daa5efe561800d0070000fb05010100203b7de491b51d3d74624078bc2c5dc4420985f0350afb6923a5585b5621750c9f126d7cff0efeade2068c7b618fc754b2abb5bff8cdb9bd0ecb4432b72ae1ed380100a82f78daed5c7b8c5ce755ff1ef7357b67e3ebc6d94c3609f9e662d0b8a4659bb8eb2575dbbddbc476694b9cca2dfea3b0bbd99d647776bdbb9e1da70e0adead081045158a7894b6405524a4d21424545aa8cacb0d0815a94891fa20414284ff2a025511a245ad54737ee77cf7ceeccb71f09a87545b9e7be77b9cef7ce79cef3cbf71f44fe94f1bf5d03d9f1951f447e343fdf3d87be873f2879efef473830dea77fff59e7bbef7f440d3bfd197d9f57368d1bfa54767949ab11b9736d48cd9b8840f7a0b372ed11f35136cf0436fe80dfac0b80dbc2afa67f84d6306e6063201ad97a8ff9234d00880f033d54c84469e48cd68b03c8b3ea54dd0909531c1fc52d0b0ed95c70e2dae4f3fd29eed5de8b6a767e77a8b8fcdf6daf32a42d7cd6bdd76d9548e51317aeaedd5f5c5d5e9d9f5f576b7a72c9aa273ed73ebed9e4af025c3b4d595e9f9d9deecf4fae2cfb4558d9b09defcf4409f1a2aa7cead3d2e53ebddf6f90b8b40e6426f41a568ba89e04eaf75171f5b5c6e3f4ac8d519393476dbebab17ba73ede9e5c5738bbd75358c9e70f6e155c24ae17d44a6aeaeadaeb7e7f1327f61aedd5d5737a1d3a1f3e1e5d5b9a5b985d9c595e9b5d9eeecb9768ffae9756e8956e29db9475f6918efa23e77a1db6daff4a67b8be7daea00d316339982ed81b579743afff0f4238b2bf3d38be347558696da34d17361b9b778af3a88ef0707693c3db73adf56868958aed36dcfb5097257d61a2280580ef09890d1fac2ec3d6f1c57af61e4a877bdb74a6445ffcd681aa6a60b6bf3e02dda0ed993275414abb8369444511c0f0d594b9f517c8b1e31237624a07ff4371cd123d60e51efd0adb7da86ff63ab8f46725b10ea353d34145aad7434623774b17959a51baaf8d45f568fb8a6c3d9b5b5e5c7d5eb6a07b42a745a7bfdd83d47c727ee7bd39b87fe66539f0854767bbaa9b5dd3093f2d7a9078655417f5be683f4a5c81ecb752737e3f44d5a9f9cccad539d22ee1417cfe76a9c1a9c29b29e53ef1ad64e4faa62e3c4b0a9dbb45007e81ff5e90e663b4d2fe83d39aca9bdf8cdcb2a33ce1e489d4d8d4ac7b5def8415a6e29a755c64d9d66d262f59651832ba175dc6cd2f3ad0a40313352c533b4f3ffd03ada2854d3601718b7043ccf3b757258611fef0076d96d07d2ecce62649cc0127ae5968b8d4e1e38ddc96ecbb17da75c405b74f67c6e4ed034553cd1c92da19207457c3ed70f0c1b0c21ac685a71b19387d4d78c9c75da192c1c776901daf9131d02648088f62d173b2e62184ec68434c5f29bca465367881c84970c54f4d1c22c80549d0a2430a126fe9ede4b742b469a9637a28be0ed843e6191fd00d024d49de6bd366d0a5a6777d2dc74429b0dde36f5df9e6bec7a5859225a9339fce1c9dc60ae39a894d39e26292146a426345d7a93f272c2484b6b9e2e1154e1a0398c01a6a8778011febd839629d7b3d95d34d54c62415e4c31a2584ca6381a31acea26051d200bf4245168a23feb1ca6d5d2043cd2d9e1eda8f8f61f4e43950da9f42744a85e22fae9c3a08b2e5e0021137ecde82da8ded0adb2d78ef257a75be822622d65756a7949d1bae92fd774c0846b1104fa0872b354c43fcee7e5eb2cceaa08c0b2a62194695a9245a3dc961b6c411509c9112f456fcd80799088f838bb54d8415018cf5c23410b00c783082a10f50e84dded3abb44840118013088481f4a76fd881cda17441ad78fc81dfb8288bb7e440eef0b22adeb47e4ee7d4164ecfa1139ba2f884c5c3f22c7f70591cb6a174cf45e9898014c4c05e33982a10750d17ba2a2050223a0592d1118361ae9778cd51be612eb3957aa3975c4aadc4cb9a78eab14d660aa456f43fc36466f357e9ba03728426c01e32d8f870db33cdef01bc66b7ec378b62d9fc883fbd4017a0b8ae4b1fbd44dfc96d1db30bf35e8ad8e193c2eaec645d5b8b01a17f0fa0d5edf1c57b70aee99c7e5f60a97d10a97db2a5c1abc0b8cbbb9dae36baa3d1eacf69809ce8a9118e10581c42db234bd1d1264d57dea2e2107b5fd4035eece6adc1d6459c844b286602bf4adefd3fe7f92f6da533efd522076fd194daed5619535e0fa38f56e78155bff121a57aefcf1b77ee7d73ffde2d44f929380af57ae7cf6db5fc35720b9b9b9f9fca7fff04f3e72cf43c356be5efe95ef50ef43c3817cddfc230c7ef770e22c7c910f12ba05b9544fd1d3d923f6297dccb263414ecb8f8ed693d42f71e55b1f7e71ea3dbcc4339f7cf1c57ff8e047bef6f98d3ed0bfffbddfa0efef1e8e05ea3c3dc8c59e119833c76c4b409205c8de305a8f539ef639d94705e5437ffbf257805a244096e9419a6541802c1cb3ce03719decded17a94fab537bffde13e10c0fc28808402e4494c08c8c5f6fbdba4fd251e4ed2c9de385a0f531979861ee1b8392de34e1fb3137ed844273b365a0ffcb01e3da271b326c3d68ed9861fd6e8643f365ab77ed83be9118f9b5332ecd4313be98791a20538e3c73d013cc6cd451977f198cdfcb8ac931d1fad6b3fec7df4a88d9bb332ecec313be6878d75b2b78c52f891dd415f9ed190a6d7283eb3194e0bf99b27b324fdb2d131046c8ce4ab19389231e8eea0198a568f24ccc8823c7e4064cec5c507d8f58eb3db9a86d1a0a6039d62ed3cbbc37007e32c240f3f2848d65b2e98526010b5769ab010ae038f30f1b0e277b025f8f92fc012a09310635fd260540df077b6d2bce4647f5eea12572b34fae9bc53d4007b414c1f3719351cc2e45a47da98c714f14094031716fa8220d5eabc4ea926751db1ae09479bbacec3d7e6082462fb1461abca25c5157dde4507b51a2086c978c36344650a3d2378e671fa73468757a36d79743d753d30ed296b52d09ec5612f0283b22d4fd91dd44c795b25e102f218997a4c0750d45614c9842289d0ac0145dae9d3e6886dbd0245a283666f5a0cf7652e3b927edb50e84a24f9b8b911f2f6450ad6157d667654f6725c1e13781095c6095c40a756866653a3bc550e555cd032934211daf1045303a7069d09efb9ea4c8ed96760595ee05e97205a1662d29e4bb22a1c7fa6ae9359cfe89cb9c55d2f6881ee71268c99452f700b562d5b1a1523aec20199181db4bb70e1e346d870f3e0d1c79cac96feaa3511197562c7a6be91227a4a1e93f2382d8fb3c29aa3f218ab38045e819050a478bb8c2816e738036dbe496c7b2b734d58365171658c8f34c2d75d5846ebcdc8eced1c6b0d722c138e3564d24cae847bf4581304060ec559728fe871baa9f138454a891e93cda1abf069c8c125c2790976e1d4a6de7960ee4ebf6775c207e6867108142639236748b4227fcf8884fefb560ebe02cf66fa3cdbd4b229614a764ab856bb1ad78840bb706d53ced910b85613ae65c0d8d5ae81718cc54bb2c31a2ca4eaaf98418892b289d978cc2ec8db647f6dac54cd430309821d9c450e083949b2b45f31bbb673bbb9f7b9f5d2f05e4e35e586844ea48239adfc6095dd46019b2246227596a5a3900f24d5c897ec33dbed18927e2e14b3ff4db5b71e8e2b5d9c94ba38f1eb267d5d9c6c93aaa4b4fd7071f6949a44a4060a93c5252b46af76aa9f17f9a8ed38d5a72be161d1b986537d7a40386604cfb395626a99fbd91010518ab173cd9a77ad2db8572bbef6ec575ffbe030ab7ea44c3397c7d43ab6ec7d8b182e223fcef421e535c0d2a77032e9f85b56ebe8815339b682d93966a4d726348cef82e03b431009d0e9a53c06b221840833428f28fca9af13a231231a6e4174461ef38209a000d1b08f682888f2bc15993a2f324be42e6596e6cd88d6f1d0e22c4fa5fdf440fb99b23d19907119c6f957efacdd4fed792a6a1ab27f2015ce672d957a25426f3763619dfd083b3a2f3e074727ad952a33fd4598347de34ddae92d7af1ecdede06fb1ba52dfb22f46243ccbad8b2c957f040763767c99ee6ec2a0ec8cc80ffb1b6c5b5d8d59c5d456f95562cbc8a15bb8c8481bec479f2cb8a83576477103b2134297833766a03e859f16345c3e5014e2ce144f8fbe347e87338f7d17ff9cc37de40bccf5038390595c4d11069b50772d522cd826f2758303e7b993d600b7e247ed49492c8ee0436d4cac3615d2f87d4113d31a3127ecb3a651878d20f7e6058a7a20b8abb3b790492d3493b816202e9da850e1020c1715cd2e19ac0034c1412e8900b3329c7b818a4a038c326b5442e947a482ee11feb6eff967ecc4af4b0a93df57212ab2306e25629e6b054cca1e742d857cce136e90dbd62862e15511a70ca4eeda2a343d6d1c66ba3ad815acb1c45be8e75370825dac2727c717440afb364676ff3ca3de21e7a1b14e6ad2e40eca2bd1db718648f2a151f5d9be326fa1af179c04a964f23407ad373ff00fdbc66e20a9868a6e24b34d070054ab45329e15f30da6e38613b54129f42944b2cca25c1d2568a599fe40cc08a40086639cbca8bf9c04cb15c21c6dd3f90287bec23b44687a34186a6010df5a3dc6e83a6fb395d55ca871ec8e932b4f4dff50d2261b00709d51e2095b84c7b8084d0ecdfa6bf6e593346bcf1a069a6147c3bae9271dabb19d2f18e2ca7f470d0d4db7989efc2d471029d4b6e48579071e69a73cee2097b75459d7711f21379d4fbfd27096e54c49d664487980c1249ee79d2435ea9f20e12d9526d891c083a7af613b97950aaaa2e5ecadeeb7bcb8de5c949d699d0facebc0b03a983cc81613726c1eee85b728274a564f0835229d2eeb4f5cbd2495adaa14e7857b52a5bc14dd007466aba21a8e469a2b7d124d84a934068120dd224649a18a189014d42170dd0049ed95b0cb248f5bedcb868a9703bd0447291c8da1c40b3e93940be207c54a4a6b886bc7b117510e2401155977b7f1545d441506511065af8da8aa8bb2162b13bfbaa8ba8af0e9143fb8248e3fa11b9635f1071d78fc8e17d41a475fd88dcbd2f888c5d3f2247f7059189eb47e4f8be20b27b11752f4caeb188ba072aba84b05b11f5b7c52f0ff7d1fa243badcfa0a68d5cb2cdfa88ed89c5ba180a3b617822313ce4122f650f55db492aa32ac3c5b925e55d591f52c61c4103346f04d4499660a128307e701712259ca6a0686e2bb738620389fe53f74397cc27502417c677740825f24bab6b48755e104ec1521e88c7b8f1ce61d6e6e46052e81dba402e3489b3cf8fa03f5130266727d7127d87f065450042870b65e4efa896783641cea40b386e534211cd496d89d4789ce65d6a7642602ea55261d877e1a00417a5b0469efa6b46c81821b6fe0b6b62899edd12a79ce47a13416de4108f3b1855443db8d34456556e6d69dc1c433585c2a0f0a4bfcf147074c48d4027e4ea1c9132aceea269dcb2cb0ee54c30d0ed0301b22bf0edfa910ba49183f2e21b12d20588700a0d3bcc63b343a374ba98ce0a914bc8ac629a6cad8684a5810d61c3622925253cf062a7b86bcbd8d82585e3b1a0d551445308dce98108b526112af5d4ab6b75779010321fe9dd61c70f725aa32665158d143697eb10a2b01cc41c82e32d92405471e94a3e90612401c97eca45083c25b8268fb4d1d41e0ce8076632174bd2a67fa5ad2106a2649c079c11d2888b9504c57fc69b03ba4896dcfc1037be2c3b66998e24f0e18f983d667203d9e6e771760b4d8c789c4cfcd873c20fe2dfe94e19df97c5a6b314ac09050981a3ac1d5bd9ad0c0195f7337251b13375c94553fa09faf8d9f7de4e6c232e51b0fa5d4d7e93d4cd82c39c1c3a46b84cf2da25da4ffb1217d21d874a0a071c1712754422ac5c05e864ef1b958188092d5f02909091a01ecd43cf46f60724b28fd9aa7b26c6583e41264cea100a706249b344b44b6622b49296b48eeb94c50a30904f218e9b5c4f844a75c8b130982d4c948a59fa211b0a0b858d14ae8b0ae228c9ee0c4228a4b96bb72004210dc270e5d930600b1c3026c54f683635ab00d6fa688af860cb443a244c1583c0389a4a7e01d9bc3728f5641e4c4d3cf524498b2e363ad80cf5b1f9206340d0ab2081149a08de95e7fc098c40c9b084430c670cf840c2c30f80c1001c72a3194cc61aa744850e3d04b1b03d3ab8d9413ec822bd068f000b0550d7b21ea77848e6d0820405be34e44ba3c3bb979b21d294f9a6ac6c324898105f3eef85321bd08c03a944affa37399518f854a264b612a46b78e9665837e93605c7df919d97b17e9c682fbe3dbc5d7dd9d216f910179773b795c36d3596d57b7a3f85d95244a87095c41ae3ab3cbe7a2fd4522e197c1fc80d02f26553a9bb6d92b5975c9529ea3da1226175581e8e9d003afca4be5a223c8d1dd6b1ca4d86d089879b7c07a5515d1e6079e220f730fc4f674e6e99ea7c4a6fcbec5b315b97b3f59eb3ab0923db26f00ea026b3fed1701dc9cabe6d5492748924e97c0ed7882d6435fae7b86830703b4af160f1a12cd9b407799af2ae171cad3c821f620a5c698a59f511d988b0c5f7a8016e3f291dc2ab0777d1456fbf1dd503b80a996be23700e23d231d6c71ef05b7b3011d3bf7fefb062960728e82342d8b6b900cc5e50dbec311c38292e1586a4afa350f91f328e15902d5b4151ce636bcf6509cd8a85526bf902f5e62d5e00b4f7cc58ebdddca313462bd02c9e921b5ca387a6374204d9fd7261057f07f5de10d68ba6d6a8ec28b4a668ed804fecbeb540c5394c5d81d5f712a95e0a70ced28d8eedc5edb8e1a7e478d6bd851c38f7ba51d855e77e73bb7c585403f322b4766db062503831a25811a7bd801efdd8148311e194556f468346b4cab1ae221176535ef4aa65ff6d6eed590ea1a69b4cfc4317b11a74ca76571b9a9bfb6b2295454fcae08e7607b2565b3aaa404a2baab4a4a807d04be9262717acec8035703032e989c159d754a640147f079ae90f81a37d0872a65dff3ac04ce72a710f181af81841c78579d196a20b6ac8184acb2b8936f32c9302e78707dade56f56a20632263d6b825352ba0e16c569cb65eec0578e41c4c1dab154bf387e0dfaa5635b2e17c0a3adc0700c2faa861597e8700e1ffad5e320f5fa3b9b280b2c81e86e0616488598c1f5dbefe7769ac8451714c7a02d898f57d1edb4a36dea1dc96dafe17d65bcf82a3dd99b868e47bf293ef9d5676f19d0f2b401d6f296b53c59956552f441a5e80df39698a53c4dfd83ec68f9e6aab746f596f937291396399eb1dd6d848574f66d44c0587438c5cd2ca9ec036cf37f0b0de3ebb0c8d80d9a1672b079a95dac8b45a2e2f439ee36e2e48b8db192b550550564771bc377292cdb98a735bb4ffca3a5fdf47ccec8e3b4f77ce450ca314cf8d69fe8047a3f22878e20fcdaff19f79e7434a3c746ebefac0dca7bf7dfbc36328542a6edb820b046600432719855c908c5604614532916a51dc32363fdba353d22d40c25b264e141fc88e82de6f851fa0349af1889da620490914b38808c3880440e860248c3c16513f65ae35786fd00d2ec08206309203d9c12f92a808ca6b80254c19100d29401a447c5226ea72f6500697d00197b3be92355e5d713a3238999b16dc1a2646ac606e245d6be134c3ebc8d41b32bcfd0ec6ed1e3c48a97becfd8ffff8cf51750b65c46aa38fcb211ed36e06ddc30edc657387689ea5ae68c04575f54db8239f95583c21d259e3d51a9c80984574c3ab62bd2debfb351fa2b49df5f09d88a559dc9167f25e0247f69659ca9fc9586f82b6ec05f69f5fd9506dfb13c25f8bc593c83898168ef7819edb16790fea93656c29531b92dc3e9b631e7adb35c01e3727499d6e15008d849b3385d64ef9638319907d92dcef6af04245d64f6d8be210d990cdc472248b8432a9797f8f46523e3e668992de55ca7de35d729a1aa53e9b3b8ea53ba3241e5b634cec1ad82dbf229f257908c2c9ec50b0e635956966141f1157268c47b09e0bdc470e7254625ff212e1ae2bd9832f41c702bb4fca25bfb4b4174e61acb79826461243f15364c32fc34462ea121730a88b0635c868d7c0e5c2e0918c13f3ec1ee2049d102d7fe49ea16fc85002be94fc0ae8acafc3b702f455adcf7b5f2e46906e10294915cc077a9785d5d9574627f8904bb8a21f13edb8a7ed9063b20a15ccd22152117b762a0148b24c4e5c5ad7e469696ab344d799b2b4dffd1a6fc93fef49d8fcc2e2eb7e75d6fd5cd2e2fafcecdf6da6e6df6d1f6ba5a7db8d39eebd197f575e95fecb5bbb3bdd5ee34ded7ddca6acf2daeb87317967b8bd38b2bf3ed8b8a7f0c99def9fe2e0d55ed6e77b5ebf07f5b2cae3c5a4d567cacd310ed8a33e0e9bd73b32b0036476db4baacbb0ed8bdd98797a9e111374bfd0bedae9b5b5de97567e77a8aeb00e9eb77e0786e757ef191c7f744efe581e5fcd06b5cee63cfa9f44df21f4350bb47786176e551225777f1dc6cf771b7d47edcbd7fa1bde22163d7b32b1ebe62cd9ae66bddd5deeadceab2f3ff71488969ffff18e132651a3cdac61cb22ce9dd1756da17d70806ed50684aa83eb278b13d3ffdf0e3bdf63ab05cef752fcc097569ee1f349552ff05ee7357f400d00700008101010100204b21f3cba072cc493e70861540df4677b498b0505a8b8e2a346b85a0c2dd2fc4263c4a7d8629026c4eb594ad96fac2bfe5f8ffebb9c841c353920b7b8ec11abc0100d90778da8d563b8f1c4510eedbf7e37cf209d9e60808402496c0dcdaac4e8ece01090112afe83043ef74ed4e6b677a86ee9edd5b3b2121b049888d842c84c0c1456702eb20b036424242c2e00408800c24fe03d53db3f33a58e860b6bbeaebeaeaaaafaab7f55bff9d1a796df0e5798263c37cc89f2fbe657e1eb8c7cb92e0de5f83c1eded95e4fded2d08150faf5ea5237e69f7855db2d3c199e351e5915a339c0b900d4103681849dff5c09daa3818bc34ec5057f319d54036b6c640752cc1617c024a17515d1a6b2f945c2f48a3ab3d09ca0b7dd68ab9d097078d292cd4267e9c39f089a70faea351378c85563b11c8802bf44c383eccc0cf20cd39e55a9d31df4c766ee487eed4f528174e4425baab412ab2fd44400f1dab73046827567402f6ece195a73495139455b44ee4ead4bb1db3594b2a94b929fa51367179f0f4882adc00722dea6c6edb0798d3452a7fd60d858643ed8c2598c8297bf18227220efe2f948148a1851bbb515c72a47ce34cbbeec655133b0106781de0c9aa059f8f41f3200b19833148090c41870e1c465c528b9b73c1c2798a3a57b5c2c0cfe276de28b9f0b90027552b7e6375c085d35a0691f6ac7a7768c39351b2a4eabb54b8e0dba3486d2b597131b1f0b3553ab68cff9c15a9dec3adc83b0327b5764a645b3bbd7c77b2ce294f6a755cf4a278e473d7c1692b91a74e75d083a9b5d828596cb8218364a6175132eb4b782fe61202581d2b906ec926dcee4a2cd2302de6ec9354785ea52d5bd5900bda21ea652849adab4030243b676debdc60af83126d32d91c2d34a85341c20682e6d233ab41b8f02f154e6a05e4e9b897c2b319c990c52e3a859123b533d932bbdf76c276c527c2e4b21ceb4d8cd8aa8bb1b56dac6d90260d1b8db10c036bbaa54063abace4ba8ea2241c3da3f77980ddaa92bd2e7628c7629ab617f54c2527174b05a6ae8a8236da3229af186acd0293fea689c65e7716ccb0eb61a892b5e548eeca2475a55ec7d3d32658c78357533c329d62a2b5eda28a6cb492c93f3758e35524f9ac128236578e11276e742c286468aca330a42cf661ab98b783ebbd58643cafff27cf7b71c4685a678db575669c5f1543c3e0735af70bef07a975ec4a819b769132cbcc6379f1637c36f3278f7c7debe2cb1f7c7eadd434c8feb73fdd3bfaf4956223c0f1fcb4fec587792193fd4fee3cc31edc2956278e5f1fdd7cfc59566c1fbd39fc19d8d14999a138ee42707492b171f5c0afa848c877af9e78c7cb22f570ec3f77fb789951c882be4940930cf4f0d1db6fdc5f16528fe3ddaf0eee2fb324e3d8fb1e057942cd851ffef1fb8fc5fcd920f8af3f2e66c9fcffb84b7ff865b7ce875708c9ff60d8f137aa5a1fa900d00700001001010020742877c36a520b152b1337ea1ecd37b0c98ad07289c32fec392e7eebab9f0ac71f7bc8c718cfa75317b2e15702372a9222c4616783ee7b3f0ec6358f8c328eea00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232201a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b72410000d00700001001010020058e23368b919493d6ac61d27f66b829a53893e88ddde857d3b82d913960960d22fa36f397752b98c295e3b31927f740127c0a99e76f8bfeea88f44466b8fbfd00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea990000d0070000100101001f43fe868e263d8134cf705aa85e26ce78ebb058edd558865fa3d240f5cb9e50c2389e9c8276eac800b7233a552045b2e79124c97e5156a0649849cc7f5d09eee600005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232204a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f0000d0070000100101001f29e82b08ccf15e2187f29fea11ee3f4974f41b51e45b19f353348d8848b86fb71cadd88630456b7a1c60803c7b402487d41fbf18f0b0a13b4cca1f740447938300005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff5260000d0070000100101002047a8b784c3765b5c63ac52e3d8461b80bc2d3e3f62434f8accb277d9f2487cfd3c0728fcd26b5119a11288e5db46bc5b547877e220971609d1cef8cba443340800005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322068dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a2974280000d007000010010100203e701fbafd4149bc95b55a6bfc3b78246f5c2668ccc05ed4059a36ceb38f140b31e3b69e15f2579571e5bde39e034947271599c200e540b3949112bef163074c00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c430000d0070000100101001f0cc7352e60f4f8476783d6d1b48766a111c56fee2c1a552e76a75c92bc17de172f994ffc854c09717c904054819ca7a17379ddecaf531c439b35337ba099b81300005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232208ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4050000d0070000100101002040965063a83be2d53b36c8d7e0775f503c2caa1407e586314562aace52c272fe60659e196413a6c9db4168470bcabb9a5851121c10c7b665f363f6cd4d1e4bda00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232202652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed250000d0070000100101002074ea7468b2a031c4cd53bf10ec3ac66b0c4b5c8779e045f1ef8d9c7b116be649217ff340107d0163397b99918ee2ce822b66cd6fce7b385af97a04671136e2ee00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d0000d007000010010100204dfb21ca5140582379bc026792c16b4cf97827143a4a9cd99ae70b3e6016cd6316bcbb9f1cb1233f12a0bbcd9debafa64724d0459b5c8d3cb67ceddfb2e3962500005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232204e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d670000d0070000100101002033446a3a94ade71dff3edb786259679487ab701bbc147490b1d4159fecf545fa22fee0698db16bf616465e5cebb985bfc4d9ed1ec4a55e38997dd4b4bbc427eb00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232204fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c20000d0070000100101001f3f67edd35bf731a07f40c638e8812112cd7d1baa39ec7dac4a1b2f0c83ac8bd53689b56dba69a7386e3860a6f8976695ac0bc2b5dacae91080f1d54df2dac0c000005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b44767070000d0070000100101001f1e030564013603d54f9e983b63cd940f8ff09ae038b14813f4021bb0c09ebb640d90cb4f8d57be2809f492a51737b671a5f549d4efa8e7efdaeaa9663c09d1ad00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead450710000d007000010010100205cea642eecf05568ce8c5564e63349eea3b816108914ba2ab5efffbb8ea467265f0b6d474f03ed02a3bf529fd6e55a595cbf8dd1adf4311cb9c51e862f8a535400005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232205443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b40000d0070000100101001f4556076cc86e0840bf69664f1ef8fcd4d91abda313d08e7840d24ba45cb429cf12b7d3a1f64250c19d1b975e7b107853beff70ebfc4c27c44f825dc05cdc9cd600005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e990000d0070000100101001f354d903ad0f2c6cc9d9a377d681ffaa00475d1e559e48074b4c8cce3111d5c172903b2f179ad4d736dda4e7d1b6a859baeab9dde5e5e495ce09733ec4650634400005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb400000d0070000100101001f1766fa716a828da244c9ce52919b7a19acb38dbd110d1bb0039bb2477c17e4465dceecb8330ed5ee9de1330930dfcfa1a5e8149ce8536a82c0093642adf7328200005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232206bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc0000d00700001001010020488923db1c78fa430a3a9eab75f4ee467c7b9a3d3b4eb3bd08e183c82ef79b9102a4d2a7d1ec79c96b404911ae1b10f579bd82a660011c1ca2b872b30ef7dcac00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322035c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b0000d0070000100101001f61ee60b366c2f3623012648000835e6089f9e9594a113acad200ae8a87bd05274acede23160e2e187d9921ea2ff6f37e3bd10ffd624ffceb511455c42f1c9ee200005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322063320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011a0000d0070000100101001f184aad2b65730f7485957642fa1688c66e8ece7827ee2e8e01f8bc904cedd8ec5462c12a1e3c6cd41f4a15a350ec8575bb05e9597f4316ff73a4e1066aeab3d500005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220fce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb40000d0070000100101002041c996b52c4bdbbc4fbdaf707dd01e74c46c51ce2c8e10e174e12502cb6be3f23e2d44e8e8802e970bc5ccfc4d056e400c92a56667183c37e0f79fbe77540a0000005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322009e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc160000d0070000100101002064bee652fda3828128a5e42da08c65127b9e247662507ed6b46d8d18ecf73afd3313c096184a70a88fad68232faa6438a23eb399cfeeb9b18e044f94ffa6049f00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232208cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7000001 +DMLOG ACCEPTED_BLOCK 3 03000000030000000200000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100012d5b1b639d6ae94fcdd0536b224644931573d1ccb2a0c548613cd1feea18888b0200000000000000010000000000ea305503000000010000000000ea305502000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100000000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb023b3d4b0000000000ea305500000000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e82d8cdf5a48c796389dd8078c999af1b8542b7e1d56784775146ab9963566974d9426f419e11a481547c9e05fe24a17d138a7fcdcf5ff599a09e03630dec0c79900000000000000207b3e5f9a58ec8f603285898120bc8c58e80f603b37dddf6f00bf79d75d62f5024d1665fa7e7af18a5f639ba3d3bc6d6858c31658a0473ed2ce4c879daa5efe560000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef26198000000000001010ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd0001023b3d4b0000000000ea305500000000000213588be25132b4167ced6df22b5439e376d5a20284190bb94a43e3e82d8cdf5a48c796389dd8078c999af1b8542b7e1d56784775146ab9963566974d9426f419e11a481547c9e05fe24a17d138a7fcdcf5ff599a09e03630dec0c79900000000000000207b3e5f9a58ec8f603285898120bc8c58e80f603b37dddf6f00bf79d75d62f5024d1665fa7e7af18a5f639ba3d3bc6d6858c31658a0473ed2ce4c879daa5efe561800d0070000fb05010100203b7de491b51d3d74624078bc2c5dc4420985f0350afb6923a5585b5621750c9f126d7cff0efeade2068c7b618fc754b2abb5bff8cdb9bd0ecb4432b72ae1ed380100a82f78daed5c7b8c5ce755ff1ef7357b67e3ebc6d94c3609f9e662d0b8a4659bb8eb2575dbbddbc476694b9cca2dfea3b0bbd99d647776bdbb9e1da70e0adead081045158a7894b6405524a4d21424545aa8cacb0d0815a94891fa20414284ff2a025511a245ad54737ee77cf7ceeccb71f09a87545b9e7be77b9cef7ce79cef3cbf71f44fe94f1bf5d03d9f1951f447e343fdf3d87be873f2879efef473830dea77fff59e7bbef7f440d3bfd197d9f57368d1bfa54767949ab11b9736d48cd9b8840f7a0b372ed11f35136cf0436fe80dfac0b80dbc2afa67f84d6306e6063201ad97a8ff9234d00880f033d54c84469e48cd68b03c8b3ea54dd0909531c1fc52d0b0ed95c70e2dae4f3fd29eed5de8b6a767e77a8b8fcdf6daf32a42d7cd6bdd76d9548e51317aeaedd5f5c5d5e9d9f5f576b7a72c9aa273ed73ebed9e4af025c3b4d595e9f9d9deecf4fae2cfb4558d9b09defcf4409f1a2aa7cead3d2e53ebddf6f90b8b40e6426f41a568ba89e04eaf75171f5b5c6e3f4ac8d519393476dbebab17ba73ede9e5c5738bbd75358c9e70f6e155c24ae17d44a6aeaeadaeb7e7f1327f61aedd5d5737a1d3a1f3e1e5d5b9a5b985d9c595e9b5d9eeecb9768ffae9756e8956e29db9475f6918efa23e77a1db6daff4a67b8be7daea00d316339982ed81b579743afff0f4238b2bf3d38be347558696da34d17361b9b778af3a88ef0707693c3db73adf56868958aed36dcfb5097257d61a2280580ef09890d1fac2ec3d6f1c57af61e4a877bdb74a6445ffcd681aa6a60b6bf3e02dda0ed993275414abb8369444511c0f0d594b9f517c8b1e31237624a07ff4371cd123d60e51efd0adb7da86ff63ab8f46725b10ea353d34145aad7434623774b17959a51baaf8d45f568fb8a6c3d9b5b5e5c7d5eb6a07b42a745a7bfdd83d47c727ee7bd39b87fe66539f0854767bbaa9b5dd3093f2d7a9078655417f5be683f4a5c81ecb752737e3f44d5a9f9cccad539d22ee1417cfe76a9c1a9c29b29e53ef1ad64e4faa62e3c4b0a9dbb45007e81ff5e90e663b4d2fe83d39aca9bdf8cdcb2a33ce1e489d4d8d4ac7b5def8415a6e29a755c64d9d66d262f59651832ba175dc6cd2f3ad0a40313352c533b4f3ffd03ada2854d3601718b7043ccf3b757258611fef0076d96d07d2ecce62649cc0127ae5968b8d4e1e38ddc96ecbb17da75c405b74f67c6e4ed034553cd1c92da19207457c3ed70f0c1b0c21ac685a71b19387d4d78c9c75da192c1c776901daf9131d02648088f62d173b2e62184ec68434c5f29bca465367881c84970c54f4d1c22c80549d0a2430a126fe9ede4b742b469a9637a28be0ed843e6191fd00d024d49de6bd366d0a5a6777d2dc74429b0dde36f5df9e6bec7a5859225a9339fce1c9dc60ae39a894d39e26292146a426345d7a93f272c2484b6b9e2e1154e1a0398c01a6a8778011febd839629d7b3d95d34d54c62415e4c31a2584ca6381a31acea26051d200bf4245168a23feb1ca6d5d2043cd2d9e1eda8f8f61f4e43950da9f42744a85e22fae9c3a08b2e5e0021137ecde82da8ded0adb2d78ef257a75be822622d65756a7949d1bae92fd774c0846b1104fa0872b354c43fcee7e5eb2cceaa08c0b2a62194695a9245a3dc961b6c411509c9112f456fcd80799088f838bb54d8415018cf5c23410b00c783082a10f50e84dded3abb44840118013088481f4a76fd881cda17441ad78fc81dfb8288bb7e440eef0b22adeb47e4ee7d4164ecfa1139ba2f884c5c3f22c7f70591cb6a174cf45e9898014c4c05e33982a10750d17ba2a2050223a0592d1118361ae9778cd51be612eb3957aa3975c4aadc4cb9a78eab14d660aa456f43fc36466f357e9ba03728426c01e32d8f870db33cdef01bc66b7ec378b62d9fc883fbd4017a0b8ae4b1fbd44dfc96d1db30bf35e8ad8e193c2eaec645d5b8b01a17f0fa0d5edf1c57b70aee99c7e5f60a97d10a97db2a5c1abc0b8cbbb9dae36baa3d1eacf69809ce8a9118e10581c42db234bd1d1264d57dea2e2107b5fd4035eece6adc1d6459c844b286602bf4adefd3fe7f92f6da533efd522076fd194daed5619535e0fa38f56e78155bff121a57aefcf1b77ee7d73ffde2d44f929380af57ae7cf6db5fc35720b9b9b9f9fca7fff04f3e72cf43c356be5efe95ef50ef43c3817cddfc230c7ef770e22c7c910f12ba05b9544fd1d3d923f6297dccb263414ecb8f8ed693d42f71e55b1f7e71ea3dbcc4339f7cf1c57ff8e047bef6f98d3ed0bfffbddfa0efef1e8e05ea3c3dc8c59e119833c76c4b409205c8de305a8f539ef639d94705e5437ffbf257805a244096e9419a6541802c1cb3ce03719decded17a94fab537bffde13e10c0fc28808402e4494c08c8c5f6fbdba4fd251e4ed2c9de385a0f531979861ee1b8392de34e1fb3137ed844273b365a0ffcb01e3da271b326c3d68ed9861fd6e8643f365ab77ed83be9118f9b5332ecd4313be98791a20538e3c73d013cc6cd451977f198cdfcb8ac931d1fad6b3fec7df4a88d9bb332ecec313be6878d75b2b78c52f891dd415f9ed190a6d7283eb3194e0bf99b27b324fdb2d131046c8ce4ab19389231e8eea0198a568f24ccc8823c7e4064cec5c507d8f58eb3db9a86d1a0a6039d62ed3cbbc37007e32c240f3f2848d65b2e98526010b5769ab010ae038f30f1b0e277b025f8f92fc012a09310635fd260540df077b6d2bce4647f5eea12572b34fae9bc53d4007b414c1f3719351cc2e45a47da98c714f14094031716fa8220d5eabc4ea926751db1ae09479bbacec3d7e6082462fb1461abca25c5157dde4507b51a2086c978c36344650a3d2378e671fa73468757a36d79743d753d30ed296b52d09ec5612f0283b22d4fd91dd44c795b25e102f218997a4c0750d45614c9842289d0ac0145dae9d3e6886dbd0245a283666f5a0cf7652e3b927edb50e84a24f9b8b911f2f6450ad6157d667654f6725c1e13781095c6095c40a756866653a3bc550e555cd032934211daf1045303a7069d09efb9ea4c8ed96760595ee05e97205a1662d29e4bb22a1c7fa6ae9359cfe89cb9c55d2f6881ee71268c99452f700b562d5b1a1523aec20199181db4bb70e1e346d870f3e0d1c79cac96feaa3511197562c7a6be91227a4a1e93f2382d8fb3c29aa3f218ab38045e819050a478bb8c2816e738036dbe496c7b2b734d58365171658c8f34c2d75d5846ebcdc8eced1c6b0d722c138e3564d24cae847bf4581304060ec559728fe871baa9f138454a891e93cda1abf069c8c125c2790976e1d4a6de7960ee4ebf6775c207e6867108142639236748b4227fcf8884fefb560ebe02cf66fa3cdbd4b229614a764ab856bb1ad78840bb706d53ced910b85613ae65c0d8d5ae81718cc54bb2c31a2ca4eaaf98418892b289d978cc2ec8db647f6dac54cd430309821d9c450e083949b2b45f31bbb673bbb9f7b9f5d2f05e4e35e586844ea48239adfc6095dd46019b2246227596a5a3900f24d5c897ec33dbed18927e2e14b3ff4db5b71e8e2b5d9c94ba38f1eb267d5d9c6c93aaa4b4fd7071f6949a44a4060a93c5252b46af76aa9f17f9a8ed38d5a72be161d1b986537d7a40386604cfb395626a99fbd91010518ab173cd9a77ad2db8572bbef6ec575ffbe030ab7ea44c3397c7d43ab6ec7d8b182e223fcef421e535c0d2a77032e9f85b56ebe8815339b682d93966a4d726348cef82e03b431009d0e9a53c06b221840833428f28fca9af13a231231a6e4174461ef38209a000d1b08f682888f2bc15993a2f324be42e6596e6cd88d6f1d0e22c4fa5fdf440fb99b23d19907119c6f957efacdd4fed792a6a1ab27f2015ce672d957a25426f3763619dfd083b3a2f3e074727ad952a33fd4598347de34ddae92d7af1ecdede06fb1ba52dfb22f46243ccbad8b2c957f040763767c99ee6ec2a0ec8cc80ffb1b6c5b5d8d59c5d456f95562cbc8a15bb8c8481bec479f2cb8a83576477103b2134297833766a03e859f16345c3e5014e2ce144f8fbe347e87338f7d17ff9cc37de40bccf5038390595c4d11069b50772d522cd826f2758303e7b993d600b7e247ed49492c8ee0436d4cac3615d2f87d4113d31a3127ecb3a651878d20f7e6058a7a20b8abb3b790492d3493b816202e9da850e1020c1715cd2e19ac0034c1412e8900b3329c7b818a4a038c326b5442e947a482ee11feb6eff967ecc4af4b0a93df57212ab2306e25629e6b054cca1e742d857cce136e90dbd62862e15511a70ca4eeda2a343d6d1c66ba3ad815acb1c45be8e75370825dac2727c717440afb364676ff3ca3de21e7a1b14e6ad2e40eca2bd1db718648f2a151f5d9be326fa1af179c04a964f23407ad373ff00fdbc66e20a9868a6e24b34d070054ab45329e15f30da6e38613b54129f42944b2cca25c1d2568a599fe40cc08a40086639cbca8bf9c04cb15c21c6dd3f90287bec23b44687a34186a6010df5a3dc6e83a6fb395d55ca871ec8e932b4f4dff50d2261b00709d51e2095b84c7b8084d0ecdfa6bf6e593346bcf1a069a6147c3bae9271dabb19d2f18e2ca7f470d0d4db7989efc2d471029d4b6e48579071e69a73cee2097b75459d7711f21379d4fbfd27096e54c49d664487980c1249ee79d2435ea9f20e12d9526d891c083a7af613b97950aaaa2e5ecadeeb7bcb8de5c949d699d0facebc0b03a983cc81613726c1eee85b728274a564f0835229d2eeb4f5cbd2495adaa14e7857b52a5bc14dd007466aba21a8e469a2b7d124d84a934068120dd224649a18a189014d42170dd0049ed95b0cb248f5bedcb868a9703bd0447291c8da1c40b3e93940be207c54a4a6b886bc7b117510e2401155977b7f1545d441506511065af8da8aa8bb2162b13bfbaa8ba8af0e9143fb8248e3fa11b9635f1071d78fc8e17d41a475fd88dcbd2f888c5d3f2247f7059189eb47e4f8be20b27b11752f4caeb188ba072aba84b05b11f5b7c52f0ff7d1fa243badcfa0a68d5cb2cdfa88ed89c5ba180a3b617822313ce4122f650f55db492aa32ac3c5b925e55d591f52c61c4103346f04d4499660a128307e701712259ca6a0686e2bb738620389fe53f74397cc27502417c677740825f24bab6b48755e104ec1521e88c7b8f1ce61d6e6e46052e81dba402e3489b3cf8fa03f5130266727d7127d87f065450042870b65e4efa896783641cea40b386e534211cd496d89d4789ce65d6a7642602ea55261d877e1a00417a5b0469efa6b46c81821b6fe0b6b62899edd12a79ce47a13416de4108f3b1855443db8d34456556e6d69dc1c433585c2a0f0a4bfcf147074c48d4027e4ea1c9132aceea269dcb2cb0ee54c30d0ed0301b22bf0edfa910ba49183f2e21b12d20588700a0d3bcc63b343a374ba98ce0a914bc8ac629a6cad8684a5810d61c3622925253cf062a7b86bcbd8d82585e3b1a0d551445308dce98108b526112af5d4ab6b75779010321fe9dd61c70f725aa32665158d143697eb10a2b01cc41c82e32d92405471e94a3e90612401c97eca45083c25b8268fb4d1d41e0ce8076632174bd2a67fa5ad2106a2649c079c11d2888b9504c57fc69b03ba4896dcfc1037be2c3b66998e24f0e18f983d667203d9e6e771760b4d8c789c4cfcd873c20fe2dfe94e19df97c5a6b314ac09050981a3ac1d5bd9ad0c0195f7337251b13375c94553fa09faf8d9f7de4e6c232e51b0fa5d4d7e93d4cd82c39c1c3a46b84cf2da25da4ffb1217d21d874a0a071c1712754422ac5c05e864ef1b958188092d5f02909091a01ecd43cf46f60724b28fd9aa7b26c6583e41264cea100a706249b344b44b6622b49296b48eeb94c50a30904f218e9b5c4f844a75c8b130982d4c948a59fa211b0a0b858d14ae8b0ae228c9ee0c4228a4b96bb72004210dc270e5d930600b1c3026c54f683635ab00d6fa688af860cb443a244c1583c0389a4a7e01d9bc3728f5641e4c4d3cf524498b2e363ad80cf5b1f9206340d0ab2081149a08de95e7fc098c40c9b084430c670cf840c2c30f80c1001c72a3194cc61aa744850e3d04b1b03d3ab8d9413ec822bd068f000b0550d7b21ea77848e6d0820405be34e44ba3c3bb979b21d294f9a6ac6c324898105f3eef85321bd08c03a944affa37399518f854a264b612a46b78e9665837e93605c7df919d97b17e9c682fbe3dbc5d7dd9d216f910179773b795c36d3596d57b7a3f85d95244a87095c41ae3ab3cbe7a2fd4522e197c1fc80d02f26553a9bb6d92b5975c9529ea3da1226175581e8e9d003afca4be5a223c8d1dd6b1ca4d86d089879b7c07a5515d1e6079e220f730fc4f674e6e99ea7c4a6fcbec5b315b97b3f59eb3ab0923db26f00ea026b3fed1701dc9cabe6d5492748924e97c0ed7882d6435fae7b86830703b4af160f1a12cd9b407799af2ae171cad3c821f620a5c698a59f511d988b0c5f7a8016e3f291dc2ab0777d1456fbf1dd503b80a996be23700e23d231d6c71ef05b7b3011d3bf7fefb062960728e82342d8b6b900cc5e50dbec311c38292e1586a4afa350f91f328e15902d5b4151ce636bcf6509cd8a85526bf902f5e62d5e00b4f7cc58ebdddca313462bd02c9e921b5ca387a6374204d9fd7261057f07f5de10d68ba6d6a8ec28b4a668ed804fecbeb540c5394c5d81d5f712a95e0a70ced28d8eedc5edb8e1a7e478d6bd851c38f7ba51d855e77e73bb7c585403f322b4766db062503831a25811a7bd801efdd8148311e194556f468346b4cab1ae221176535ef4aa65ff6d6eed590ea1a69b4cfc4317b11a74ca76571b9a9bfb6b2295454fcae08e7607b2565b3aaa404a2baab4a4a807d04be9262717acec8035703032e989c159d754a640147f079ae90f81a37d0872a65dff3ac04ce72a710f181af81841c78579d196a20b6ac8184acb2b8936f32c9302e78707dade56f56a20632263d6b825352ba0e16c569cb65eec0578e41c4c1dab154bf387e0dfaa5635b2e17c0a3adc0700c2faa861597e8700e1ffad5e320f5fa3b9b280b2c81e86e0616488598c1f5dbefe7769ac8451714c7a02d898f57d1edb4a36dea1dc96dafe17d65bcf82a3dd99b868e47bf293ef9d5676f19d0f2b401d6f296b53c59956552f441a5e80df39698a53c4dfd83ec68f9e6aab746f596f937291396399eb1dd6d848574f66d44c0587438c5cd2ca9ec036cf37f0b0de3ebb0c8d80d9a1672b079a95dac8b45a2e2f439ee36e2e48b8db192b550550564771bc377292cdb98a735bb4ffca3a5fdf47ccec8e3b4f77ce450ca314cf8d69fe8047a3f22878e20fcdaff19f79e7434a3c746ebefac0dca7bf7dfbc36328542a6edb820b046600432719855c908c5604614532916a51dc32363fdba353d22d40c25b264e141fc88e82de6f851fa0349af1889da620490914b38808c3880440e860248c3c16513f65ae35786fd00d2ec08206309203d9c12f92a808ca6b80254c19100d29401a447c5226ea72f6500697d00197b3be92355e5d713a3238999b16dc1a2646ac606e245d6be134c3ebc8d41b32bcfd0ec6ed1e3c48a97becfd8ffff8cf51750b65c46aa38fcb211ed36e06ddc30edc657387689ea5ae68c04575f54db8239f95583c21d259e3d51a9c80984574c3ab62bd2debfb351fa2b49df5f09d88a559dc9167f25e0247f69659ca9fc9586f82b6ec05f69f5fd9506dfb13c25f8bc593c83898168ef7819edb16790fea93656c29531b92dc3e9b631e7adb35c01e3727499d6e15008d849b3385d64ef9638319907d92dcef6af04245d64f6d8be210d990cdc472248b8432a9797f8f46523e3e668992de55ca7de35d729a1aa53e9b3b8ea53ba3241e5b634cec1ad82dbf229f257908c2c9ec50b0e635956966141f1157268c47b09e0bdc470e7254625ff212e1ae2bd9832f41c702bb4fca25bfb4b4174e61acb79826461243f15364c32fc34462ea121730a88b0635c868d7c0e5c2e0918c13f3ec1ee2049d102d7fe49ea16fc85002be94fc0ae8acafc3b702f455adcf7b5f2e46906e10294915cc077a9785d5d9574627f8904bb8a21f13edb8a7ed9063b20a15ccd22152117b762a0148b24c4e5c5ad7e469696ab344d799b2b4dffd1a6fc93fef49d8fcc2e2eb7e75d6fd5cd2e2fafcecdf6da6e6df6d1f6ba5a7db8d39eebd197f575e95fecb5bbb3bdd5ee34ded7ddca6acf2daeb87317967b8bd38b2bf3ed8b8a7f0c99def9fe2e0d55ed6e77b5ebf07f5b2cae3c5a4d567cacd310ed8a33e0e9bd73b32b0036476db4baacbb0ed8bdd98797a9e111374bfd0bedae9b5b5de97567e77a8aeb00e9eb77e0786e757ef191c7f744efe581e5fcd06b5cee63cfa9f44df21f4350bb47786176e551225777f1dc6cf771b7d47edcbd7fa1bde22163d7b32b1ebe62cd9ae66bddd5deeadceab2f3ff71488969ffff18e132651a3cdac61cb22ce9dd1756da17d70806ed50684aa83eb278b13d3ffdf0e3bdf63ab05cef752fcc097569ee1f349552ff05ee7357f400d00700008101010100204b21f3cba072cc493e70861540df4677b498b0505a8b8e2a346b85a0c2dd2fc4263c4a7d8629026c4eb594ad96fac2bfe5f8ffebb9c841c353920b7b8ec11abc0100d90778da8d563b8f1c4510eedbf7e37cf209d9e60808402496c0dcdaac4e8ece01090112afe83043ef74ed4e6b677a86ee9edd5b3b2121b049888d842c84c0c1456702eb20b036424242c2e00408800c24fe03d53db3f33a58e860b6bbeaebeaeaaaafaab7f55bff9d1a796df0e5798263c37cc89f2fbe657e1eb8c7cb92e0de5f83c1eded95e4fded2d08150faf5ea5237e69f7855db2d3c199e351e5915a339c0b900d4103681849dff5c09daa3818bc34ec5057f319d54036b6c640752cc1617c024a17515d1a6b2f945c2f48a3ab3d09ca0b7dd68ab9d097078d292cd4267e9c39f089a70faea351378c85563b11c8802bf44c383eccc0cf20cd39e55a9d31df4c766ee487eed4f528174e4425baab412ab2fd44400f1dab73046827567402f6ece195a73495139455b44ee4ead4bb1db3594b2a94b929fa51367179f0f4882adc00722dea6c6edb0798d3452a7fd60d858643ed8c2598c8297bf18227220efe2f948148a1851bbb515c72a47ce34cbbeec655133b0106781de0c9aa059f8f41f3200b19833148090c41870e1c465c528b9b73c1c2798a3a57b5c2c0cfe276de28b9f0b90027552b7e6375c085d35a0691f6ac7a7768c39351b2a4eabb54b8e0dba3486d2b597131b1f0b3553ab68cff9c15a9dec3adc83b0327b5764a645b3bbd7c77b2ce294f6a755cf4a278e473d7c1692b91a74e75d083a9b5d828596cb8218364a6175132eb4b782fe61202581d2b906ec926dcee4a2cd2302de6ec9354785ea52d5bd5900bda21ea652849adab4030243b676debdc60af83126d32d91c2d34a85341c20682e6d233ab41b8f02f154e6a05e4e9b897c2b319c990c52e3a859123b533d932bbdf76c276c527c2e4b21ceb4d8cd8aa8bb1b56dac6d90260d1b8db10c036bbaa54063abace4ba8ea2241c3da3f77980ddaa92bd2e7628c7629ab617f54c2527174b05a6ae8a8236da3229af186acd0293fea689c65e7716ccb0eb61a892b5e548eeca2475a55ec7d3d32658c78357533c329d62a2b5eda28a6cb492c93f3758e35524f9ac128236578e11276e742c286468aca330a42cf661ab98b783ebbd58643cafff27cf7b71c4685a678db575669c5f1543c3e0735af70bef07a975ec4a819b769132cbcc6379f1637c36f3278f7c7debe2cb1f7c7eadd434c8feb73fdd3bfaf4956223c0f1fcb4fec587792193fd4fee3cc31edc2956278e5f1fdd7cfc59566c1fbd39fc19d8d14999a138ee42707492b171f5c0afa848c877af9e78c7cb22f570ec3f77fb789951c882be4940930cf4f0d1db6fdc5f16528fe3ddaf0eee2fb324e3d8fb1e057942cd851ffef1fb8fc5fcd920f8af3f2e66c9fcffb84b7ff865b7ce875708c9ff60d8f137aa5a1fa900d00700001001010020742877c36a520b152b1337ea1ecd37b0c98ad07289c32fec392e7eebab9f0ac71f7bc8c718cfa75317b2e15702372a9222c4616783ee7b3f0ec6358f8c328eea00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232201a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b72410000d00700001001010020058e23368b919493d6ac61d27f66b829a53893e88ddde857d3b82d913960960d22fa36f397752b98c295e3b31927f740127c0a99e76f8bfeea88f44466b8fbfd00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea990000d0070000100101001f43fe868e263d8134cf705aa85e26ce78ebb058edd558865fa3d240f5cb9e50c2389e9c8276eac800b7233a552045b2e79124c97e5156a0649849cc7f5d09eee600005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232204a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f0000d0070000100101001f29e82b08ccf15e2187f29fea11ee3f4974f41b51e45b19f353348d8848b86fb71cadd88630456b7a1c60803c7b402487d41fbf18f0b0a13b4cca1f740447938300005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff5260000d0070000100101002047a8b784c3765b5c63ac52e3d8461b80bc2d3e3f62434f8accb277d9f2487cfd3c0728fcd26b5119a11288e5db46bc5b547877e220971609d1cef8cba443340800005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322068dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a2974280000d007000010010100203e701fbafd4149bc95b55a6bfc3b78246f5c2668ccc05ed4059a36ceb38f140b31e3b69e15f2579571e5bde39e034947271599c200e540b3949112bef163074c00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c430000d0070000100101001f0cc7352e60f4f8476783d6d1b48766a111c56fee2c1a552e76a75c92bc17de172f994ffc854c09717c904054819ca7a17379ddecaf531c439b35337ba099b81300005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232208ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4050000d0070000100101002040965063a83be2d53b36c8d7e0775f503c2caa1407e586314562aace52c272fe60659e196413a6c9db4168470bcabb9a5851121c10c7b665f363f6cd4d1e4bda00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232202652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed250000d0070000100101002074ea7468b2a031c4cd53bf10ec3ac66b0c4b5c8779e045f1ef8d9c7b116be649217ff340107d0163397b99918ee2ce822b66cd6fce7b385af97a04671136e2ee00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d0000d007000010010100204dfb21ca5140582379bc026792c16b4cf97827143a4a9cd99ae70b3e6016cd6316bcbb9f1cb1233f12a0bbcd9debafa64724d0459b5c8d3cb67ceddfb2e3962500005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232204e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d670000d0070000100101002033446a3a94ade71dff3edb786259679487ab701bbc147490b1d4159fecf545fa22fee0698db16bf616465e5cebb985bfc4d9ed1ec4a55e38997dd4b4bbc427eb00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232204fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c20000d0070000100101001f3f67edd35bf731a07f40c638e8812112cd7d1baa39ec7dac4a1b2f0c83ac8bd53689b56dba69a7386e3860a6f8976695ac0bc2b5dacae91080f1d54df2dac0c000005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b44767070000d0070000100101001f1e030564013603d54f9e983b63cd940f8ff09ae038b14813f4021bb0c09ebb640d90cb4f8d57be2809f492a51737b671a5f549d4efa8e7efdaeaa9663c09d1ad00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead450710000d007000010010100205cea642eecf05568ce8c5564e63349eea3b816108914ba2ab5efffbb8ea467265f0b6d474f03ed02a3bf529fd6e55a595cbf8dd1adf4311cb9c51e862f8a535400005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232205443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b40000d0070000100101001f4556076cc86e0840bf69664f1ef8fcd4d91abda313d08e7840d24ba45cb429cf12b7d3a1f64250c19d1b975e7b107853beff70ebfc4c27c44f825dc05cdc9cd600005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e990000d0070000100101001f354d903ad0f2c6cc9d9a377d681ffaa00475d1e559e48074b4c8cce3111d5c172903b2f179ad4d736dda4e7d1b6a859baeab9dde5e5e495ce09733ec4650634400005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb400000d0070000100101001f1766fa716a828da244c9ce52919b7a19acb38dbd110d1bb0039bb2477c17e4465dceecb8330ed5ee9de1330930dfcfa1a5e8149ce8536a82c0093642adf7328200005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232206bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc0000d00700001001010020488923db1c78fa430a3a9eab75f4ee467c7b9a3d3b4eb3bd08e183c82ef79b9102a4d2a7d1ec79c96b404911ae1b10f579bd82a660011c1ca2b872b30ef7dcac00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322035c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b0000d0070000100101001f61ee60b366c2f3623012648000835e6089f9e9594a113acad200ae8a87bd05274acede23160e2e187d9921ea2ff6f37e3bd10ffd624ffceb511455c42f1c9ee200005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322063320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011a0000d0070000100101001f184aad2b65730f7485957642fa1688c66e8ece7827ee2e8e01f8bc904cedd8ec5462c12a1e3c6cd41f4a15a350ec8575bb05e9597f4316ff73a4e1066aeab3d500005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed323220fce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb40000d0070000100101002041c996b52c4bdbbc4fbdaf707dd01e74c46c51ce2c8e10e174e12502cb6be3f23e2d44e8e8802e970bc5ccfc4d056e400c92a56667183c37e0f79fbe77540a0000005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed32322009e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc160000d0070000100101002064bee652fda3828128a5e42da08c65127b9e247662507ed6b46d8d18ecf73afd3313c096184a70a88fad68232faa6438a23eb399cfeeb9b18e044f94ffa6049f00005206e10b5e02005132b41600000000010000000000ea30550000002a9bed3232010000000000ea305500000000a8ed3232208cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7000001 DMLOG START_BLOCK 4 DMLOG FEATURE_OP ACTIVATE 1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241 {"feature_digest":"1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241","subjective_restrictions":{"enabled":true,"preactivation_required":true,"earliest_allowed_activation_time":"1970-01-01T00:00:00.000"},"description_digest":"f3c3d91c4603cde2397268bfed4e662465293aab10cd9416db0d442b8cec2949","dependencies":[],"protocol_feature_type":"builtin","specification":[{"name":"builtin_feature_codename","value":"ONLY_LINK_TO_EXISTING_PERMISSION"}]} DMLOG FEATURE_OP ACTIVATE ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99 {"feature_digest":"ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99","subjective_restrictions":{"enabled":true,"preactivation_required":true,"earliest_allowed_activation_time":"1970-01-01T00:00:00.000"},"description_digest":"9908b3f8413c8474ab2a6be149d3f4f6d0421d37886033f27d4759c47a26d944","dependencies":[],"protocol_feature_type":"builtin","specification":[{"name":"builtin_feature_codename","value":"REPLACE_DEFERRED"}]} @@ -176,7 +176,7 @@ DMLOG RLIMIT_OP ACCOUNT_USAGE UPD {"owner":"eosio","net_usage":{"last_ordinal":1 DMLOG APPLIED_TRANSACTION 4 82716fa2e8aba5088d0051632c9b2b79cee395da8b671197e476ca75bf1ceaaa04000000033b3d4b0100000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba0100d00700008c01000000000000000060040000000000000001010000010000000000ea3055c843ae0bfe7bfb9d664537bcb46ba4ffa14d4a4238f7c9b9e0183ffb6959b3441d000000000000001d00000000000000010000000000ea30551d0000000000000002020000000000ea30550000000000ea305500000000b863b2c2010000000000ea305500000000a8ed3232f3130000000000ea3055e9130e656f73696f3a3a6162692f312e320117626c6f636b5f7369676e696e675f617574686f726974792276617269616e745f626c6f636b5f7369676e696e675f617574686f726974795f76301c086162695f686173680002056f776e6572046e616d6504686173680b636865636b73756d32353608616374697661746500010e666561747572655f6469676573740b636865636b73756d32353609617574686f726974790004097468726573686f6c640675696e743332046b6579730c6b65795f7765696768745b5d086163636f756e7473197065726d697373696f6e5f6c6576656c5f7765696768745b5d0577616974730d776169745f7765696768745b5d1a626c6f636b5f7369676e696e675f617574686f726974795f76300002097468726573686f6c640675696e743332046b6579730c6b65795f7765696768745b5d15626c6f636b636861696e5f706172616d65746572730011136d61785f626c6f636b5f6e65745f75736167650675696e7436341a7461726765745f626c6f636b5f6e65745f75736167655f7063740675696e743332196d61785f7472616e73616374696f6e5f6e65745f75736167650675696e7433321e626173655f7065725f7472616e73616374696f6e5f6e65745f75736167650675696e743332106e65745f75736167655f6c65657761790675696e74333223636f6e746578745f667265655f646973636f756e745f6e65745f75736167655f6e756d0675696e74333223636f6e746578745f667265655f646973636f756e745f6e65745f75736167655f64656e0675696e743332136d61785f626c6f636b5f6370755f75736167650675696e7433321a7461726765745f626c6f636b5f6370755f75736167655f7063740675696e743332196d61785f7472616e73616374696f6e5f6370755f75736167650675696e743332196d696e5f7472616e73616374696f6e5f6370755f75736167650675696e743332186d61785f7472616e73616374696f6e5f6c69666574696d650675696e7433321e64656665727265645f7472785f65787069726174696f6e5f77696e646f770675696e743332156d61785f7472616e73616374696f6e5f64656c61790675696e743332166d61785f696e6c696e655f616374696f6e5f73697a650675696e743332176d61785f696e6c696e655f616374696f6e5f64657074680675696e743136136d61785f617574686f726974795f64657074680675696e7431360b63616e63656c64656c617900020e63616e63656c696e675f61757468107065726d697373696f6e5f6c6576656c067472785f69640b636865636b73756d3235360a64656c657465617574680002076163636f756e74046e616d650a7065726d697373696f6e046e616d651366696e616c697a65725f617574686f7269747900040b6465736372697074696f6e06737472696e67067765696768740675696e7436340a7075626c69635f6b657906737472696e6703706f7006737472696e671066696e616c697a65725f706f6c6963790002097468726573686f6c640675696e7436340a66696e616c697a6572731566696e616c697a65725f617574686f726974795b5d0a6b65795f7765696768740002036b65790a7075626c69635f6b6579067765696768740675696e743136086c696e6b617574680004076163636f756e74046e616d6504636f6465046e616d650474797065046e616d650b726571756972656d656e74046e616d650a6e65776163636f756e7400040763726561746f72046e616d65046e616d65046e616d65056f776e657209617574686f726974790661637469766509617574686f72697479076f6e6572726f7200020973656e6465725f69640775696e743132380873656e745f747278056279746573107065726d697373696f6e5f6c6576656c0002056163746f72046e616d650a7065726d697373696f6e046e616d65177065726d697373696f6e5f6c6576656c5f77656967687400020a7065726d697373696f6e107065726d697373696f6e5f6c6576656c067765696768740675696e7431361270726f64756365725f617574686f7269747900020d70726f64756365725f6e616d65046e616d6509617574686f7269747917626c6f636b5f7369676e696e675f617574686f726974790c72657161637469766174656400010e666561747572655f6469676573740b636865636b73756d323536077265716175746800010466726f6d046e616d65067365746162690002076163636f756e74046e616d65036162690562797465730a736574616c696d6974730004076163636f756e74046e616d650972616d5f627974657305696e7436340a6e65745f77656967687405696e7436340a6370755f77656967687405696e74363407736574636f64650004076163636f756e74046e616d6506766d747970650575696e743809766d76657273696f6e0575696e743804636f64650562797465730c73657466696e616c697a657200011066696e616c697a65725f706f6c6963791066696e616c697a65725f706f6c69637909736574706172616d73000106706172616d7315626c6f636b636861696e5f706172616d657465727307736574707269760002076163636f756e74046e616d650769735f707269760575696e74380873657470726f64730001087363686564756c651470726f64756365725f617574686f726974795b5d0a756e6c696e6b617574680003076163636f756e74046e616d6504636f6465046e616d650474797065046e616d650a757064617465617574680004076163636f756e74046e616d650a7065726d697373696f6e046e616d6506706172656e74046e616d65046175746809617574686f726974790b776169745f776569676874000208776169745f7365630675696e743332067765696768740675696e743136110000002a9bed32320861637469766174650000bc892a4585a6410b63616e63656c64656c6179000040cbdaa8aca24a0a64656c65746561757468000000002d6b03a78b086c696e6b617574680000409e9a2264b89a0a6e65776163636f756e7400000000e0d27bd5a4076f6e6572726f7200905436db6564acba0c72657161637469766174656400000000a0656dacba07726571617574680000000000b863b2c206736574616269000000ce4eba68b2c20a736574616c696d6974730000000040258ab2c207736574636f64650070d577d14cb7b2c20c73657466696e616c697a6572000000c0d25c53b3c209736574706172616d730000000060bb5bb3c207736574707269760000000038d15bb3c20873657470726f6473000040cbdac0e9e2d40a756e6c696e6b61757468000040cbdaa86c52d50a757064617465617574680001000000a061d3dc31036936340000086162695f68617368000000012276617269616e745f626c6f636b5f7369676e696e675f617574686f726974795f7630011a626c6f636b5f7369676e696e675f617574686f726974795f7630000000000000000000000082716fa2e8aba5088d0051632c9b2b79cee395da8b671197e476ca75bf1ceaaa04000000033b3d4b0100000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba010000000000ea3055690100000000000000000000000000 DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":3,"value_ex":82933334,"consumed":9952},"average_block_cpu_usage":{"last_ordinal":3,"value_ex":401659723,"consumed":48101},"pending_net_usage":17800,"pending_cpu_usage":4100,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1050675,"virtual_cpu_limit":200400} DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":4,"value_ex":230575556,"consumed":17883},"average_block_cpu_usage":{"last_ordinal":4,"value_ex":432479225,"consumed":4499},"pending_net_usage":0,"pending_cpu_usage":0,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1051726,"virtual_cpu_limit":200600} -DMLOG ACCEPTED_BLOCK 4 04000000040000000300000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add8010000000000030000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb2d5b1b639d6ae94fcdd0536b224644931573d1ccb2a0c548613cd1feea18888b0f87ad8b5169aafcab7e8eb69d0ceea2aaf352570f2271f3f0b32d56c69033f40300000000000000010000000000ea305504000000010000000000ea305503000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add801000000000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba033b3d4b0000000000ea305500000000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb21729c7835698f124c8be1f086f7e6001e73e9c3554da1bf5cab4c4b3860ae83b8bdd3e98936ab78d42df8f5b434eda1ee1a725329b68ccbc00d433e080116930000000000010000c105161a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea994a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0fe0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff52668dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c438ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4052652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead450715443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b4bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb406bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc35c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b63320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011afce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb409e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc168cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7002052096236d23a2f907bea206d91251b18f98eff6c1bfb3acdc8a0d7a1578fca146b6bb6825b1aae9852dce7197c2919c833a423ac79339e1357c9d46cbec943de0000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef261980000000000011709e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc160ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b72412652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b447670735c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c25443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b463320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011a68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a2974286bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4058cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead45071d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb40e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1dfce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb40001033b3d4b0000000000ea305500000000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb21729c7835698f124c8be1f086f7e6001e73e9c3554da1bf5cab4c4b3860ae83b8bdd3e98936ab78d42df8f5b434eda1ee1a725329b68ccbc00d433e080116930000000000010000c105161a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea994a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0fe0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff52668dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c438ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4052652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead450715443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b4bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb406bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc35c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b63320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011afce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb409e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc168cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7002052096236d23a2f907bea206d91251b18f98eff6c1bfb3acdc8a0d7a1578fca146b6bb6825b1aae9852dce7197c2919c833a423ac79339e1357c9d46cbec943de0200d0070000a5100101002007b4601e1e0103d3bf16298ebb6907bb9074ffcd1efb95d3342eb82dc86e056a2a15ea47d496b7f8fd88eab6146003c28b587e42f26248c351fcfc40ca1333060100fd810178daed7d0b7c5d5595f7d9e771ef4d4ed29cbe4353e0dc6b94145b48d2344d234a4f475a6881561e0e8e3a69682e6d6ed23c6e6ed2564b13a03015510b82bc7cf0d296d7c827f88d0a1f06544444ad50b03a8cf6fb869fd31971ac33fe6650d17eebbfd63ee79e9ba44d31eaf8cd67da73cf3efbecc75a6bafbdf6da6bafbd4ff27fbb7f6d19f957df7499417f0a3fc6bfd45f4cbfcbdf78ede79e8c47187b7fdad8f8f48fcc62d4a3f4d03eb01989d401e5af378cf5e6f0b0b1de1a0e7fd5f07a7b58ee89e1611d4a0e47417b07fdd1dd503b28e18e1d94cd09df99d1ef0e1d69ac4f203dc7ab1d5419d2731154db0e4ea828460d7389c84b2fa858868653a574d514918c42ea727579081ec0e652e5c1a44ce64872a195ed1972da2fedcd170c03e18a6cef40676f5bfbc04096a214a2129bb39b07b205c3c243921e36f70e65e509af36f46d9387545fbeb3a730d0d62dd9ca2fed1e68bbacaf6d736f876147111b1bdb36b7f79546b47774180e225c44f4b57752391b8d04c7745cda7659674f475b67739391448cb761309fcff614daf2d90dd9cea16cde4821ba52206e1bd8d4deb8a4d930397331ae416262a99634344a9ca7e3f29d7dd9cd1d0dcdf5129d889754d64614d9d45de85c6c94e1d92e169988975536a61097802462e5dbbab2db8c72c4f844c9b64bbb7b37746dd8d4ded943d8e6db37670bd93c10dfd095ed10e2f91b274be622d91c94d697efedeb1dc87620d031b881d21815dc9421a50a9d9bb342a55928b67d4381e8164bcd45cd08c91083b8928ba1261828f4e6b3dc06d3382a9fed1feca498f6c1c226a30a51d30009b5547b77e7fb50a68a22892b863abbb31b09660f91331199cf0ef40ee63764dbba3b377716068ce97873c2c4d8b465b71a3338675f3ecbc0b717b26d9765db0b83f9ac31931b41584f1e66750e846fdbc2e41dc62c696b7aeeed69eb682fb4b70d10a4c66c8ea6723bda62ef8424d3e37da16d436f47d698c31594c46fce0e0cb46fcc4a839713b14063908a3b4525450cf6750062c4cd2d76944d8496b2deb7cda89e35cb9d69cc9a497f96e5ba33ddf213e629cb55b665a99955335dd3ac99651a338bffe6cf9c699ee89aee494a99f48ffe66aa93e5cf55faa64e76e91f85e8cf70e959e1a23fd35554a24937db517d6ae64cc752864accb1865530326ab8c34670d781e8962c534e7b5f5ff736635159ad3202e5ceb8f0e2bf7cd7bbdf7bbdba417d54dda43ea66e56b7a85bd56dea53ea0e75a72a7fe271739b67788dde05de45de3bbd4bbcbff2dee3fed2327cc35bed2df40d5fbdc35ebe6264e497fbefdd79eb4bb70dfb263d23dacb3b14fde08b1fbae9f67b2f2ec60e84b1f77ff719a3185d40f4c8a1cffd74f4f90fc6ca18d4a9bff0a59f5f548c1da2d8573fb3ffb9bd773eff44ac8c2d5cc6c8feafdef5fd15c5d8ad123bf29fb7c463df2fb17ffbab1ffecb3db1fab64bf4fffccf17e3892f47ec17fee39e9b6f8bc7eee0b4a31ffd5549da6167c5e1fb7ef4c16ffdf383f1d8ab1427fed8b77eb23f1e7db5448ffce081db5ffc520c8cbf91f87d9f7bf8d15b1b8bd1bb287af7bddffffe3f5c776b49f20f28c73704cbd12fddf2827181bd3ce0e7ef3ffff04faffc99e19de28efb7362e9c3362ca107957045690981e12d751f51ca1af68db43297e39f6f0456ff599506428b0ca392f8c237baaa5c8a57b9b4b296fb8a13285fd1ebb4498f2a97b1d2ca37cfaa342b889529667b98d2ce65549d6964cc6078c0378323bff9f5965c7f3072c5953bb7a212976aa158d54fd528ae06b91619aaa4dc4aa3c2a462ad603897b6e84e09dd3c010d102b8dc0a8224402a3cebc0ec07b4319ca6736d393c4ee5c9ea11cb9a02a17f4f5678c668ae05abd826fac41760aab1cd273956905a0d47223185e85b7c1ee51c35354ab7bad923a09d0ee8c1a53b142c526015c52316115566ceb8a2d546c4bc556583120489b4c8db1151791f4914579e63c9fe866b8cd4a0dbf81f0edca507cb359412fa9d20aa27611d591e58845cd8171a681aadcc5ca1c465bfa460d9a1cf4f64ecf18952a30d20cf6489031d1bce674c3e0e634dc048443835b66206333d5cd210321fa7f27e812ec7cc2f09698cb991c41c622906f7cc2485b01dfa9e6c00577e1f5ee206307fe2ab058b09330f7ea39f970daa16626286ca20b9742e58348c81c7075bb03bc7728d266caed0e8819ecc02c10dd839d2ae70338ca6de77c068e816af61df71c4048d080c0ccdb967077c6662ece38abc0cfdcfac2e7608a616223cd7ab6ef7431ebb9bf2401cd8c171c79e27ba7af11bef5de82d6a1b89127bf777a0f313beedd5da84f927ef4272f5992d60cea73ad06d591362a941b9869895485e08eaf10939a9e3dcf4591b90c10d9fd6da327a3822347acfee0650325c71352c9668eb3bf6c7477e1d56f9130fe9e28611162544e7757bc206b6c410914641dbda054cefd8125a8abe023df46cfe1e03f46a1b2352c2b9420c86464aaa8f13057414600960c40efe17651c13f25a5042b5602e3d637b6044b4ab626a486752c6a70cda0aa6e1b157c79e75b430c3ef5a1440904d4482658d2246934b6b60a1113c19107fe8f1d668a35530c03eba8ed135854be121a8dee7c6bcf04e858e3d00f6c121494c5048728e19149da7672b2b88ee11986fba80c052b8ca5a6c16d4e98a023992c94099b7e69e20526a7808c4ee548e0a0d7918042b23ad3a64c7d6bc1ec75664ac8ccf2d63da712bdca1369e82d4657f3494edb3e77e73c17821e9c129a80d74322cae8a2d033889cba8f98b9c0e004552e80ff9a527609f011e80493eee6811af2e9a73f93a8c938be0988e8d645618b02d6393544bc22ec490dbb25b05b1af614c1eef82c98007b2a82dd8960b7e2b053c10cbbe32759c6f8891a1f4dcb68d0cb52345e955126e8f1551743283244c87f56250bf9545aea41e92937e84d97312ffef8c89123e843819b2ee36e9729471b512bd2ad9c902b6d847499eb97112e2665b651b2e9a77c1b5051444222487e24fd848e231a75d1586a776592d15b921b29dc1d4aa893a9a07c25bd4e794daeb4700a14a194180341b1324d3114c0b80be6cf6acc7b8f897932c23c09cc5363314f09e66545cccbc6639e72fd5429e6c9087360091cad388e498da3558a63b20447cd0982cd015325455a82a1225420c24364f0b0a39fb51a3cda691bb85182a1b41d94d378433f0e402466a76b4d0df5724643091ac8e9001a62a295ba0e272887f6a050248894d054a3d18f7986b83f47982724172417b5346b1e63a143262a862e64c44d931d03a9ab3b7594da4e2b01feb7aa1f0868f1c84da0007382686886243e4bd38ec2292ec04f0809358700ca9098221ed09d5ca1ecd3c22721c816fab31d925413d411982cc064011e0bb414a8096fe12f96291c6547383b117b39d2392168202649eee4c030635892255889c042b6986802369c196c6541b501677a6742affa368b2ce59d162bf8ff657cf694b60cd803aa9db0b449104bc945b46c41cb7445d282a57c7b15c3416d4e4c6f43f1f362202a1a907dab32029006b748d01a11dc396623e06f84632510c088a380d2c74c227c11501b803a1127339436774357ba1f1197a0e74e33a66112d206099218347e609408dbc089da2011b5414297a7256802228a1b85c606cf9a47d853d84469745b43c304687f5e0d467ca4c57b24230d996837beedeca8edc21ec345c5db2ec199fd4417c080764ddc80b693ae358d8760aac6f44ef3ce729f5425642ae14c5b588b99510b21c5325b6bd8255c3886020e28408d8bd626ce25f1dc25e53241148fed248f0953d40bc51ba3b4e656eb58ac9a08593542e9ebc041e64966ad28db4ece6b65f9a87504ad34b0ecc75b02a628b44912af091932a68f088b8842e06865a628bc62ca8c4c713174ace49ee0109f5a3c8924d4509e9e7688bcce18c4fd3c85e67718af6a3522a2a295634a642cb50ebddd3d5f2680070dd2fe1833151c307290a38a4708401386649a72ee3c7ed41a113171973787620e1a54897b574acf6cea2089598feb0a92ab792a7af8ab34150dfbaae87dac3a7214eb895c19855897e72228b735716e9aa0c572db51ee8a29e5f6a6947bd69472574f29f7fc29e5f6a794bb764ab9eba6947be19472d74f2977d39472b74c29f71953ca3d6a94665747cb6ec6b29b51f62729bb9a3cbb1a939d459bfb2b65a9617307cb9d7a529145a0f2ccc8f35618a4b67a6918924c0a3b161bbb72d033127ab22d33ae15a96b48ee271a0c6367c65ed1b26b27a9370a83298df8f48ec75fd11e57326cf78a058e722eb5584994bc4b2d03d5c1e6473215b5a35656a031718585498af156ea093e576fc19a03236621d89af3a6138ca9dc02cbc0340d379b87cfa596a1eb73d91a507b6cdc67fd89e13eebf78cfba7a3415478e731aa63ee100cb7c115c330becd59ad874bd207cf26c0693e46bae1100df2260dd626469fbf308c91f7dfd0625640a983796d35a6c1c1c8c8c876df1aec1753482c5d8a074ee37c2a0d86ecad9c00c1d72c2828a8e248b29f7523f371d6f3dc17a7a984d65782e715e683f5d4240f7d8d1ac55ace4cad860858d80bd1a3a07ed461a03e2763d7c0e20c13f3002c36760d0cbc5430bde67ab9c8ed6bc51a1e3ca77a320efd7667125d69a9e4f053bf7b25c8dc159883ac1818941311b0abd0cc93d4e659e8ee5cb4055cf0224300b05a2a2a94ea5e59a9d8ee49153fa6d0f8843f9be0a9c21e9ab3eba4964e0a28cb29314ab1c252ace8550581de934940865022794f8a63f4de8bdedb13be9f3149fe3993e49f1fbd4f4cf8fea449caaf9d247fdd24f59f3a49f9a74d92bf6992fa5b27797fc624f5930c3f76014f199340f8b4314915df9eac8ae726abe2c064251c2c26484d98e0e5c9aaf8f16458fccb6425bc32590987272be1d5c9b0f8ed6474b8524d52c52e354909d7aa49b0f8f06455dc3859151f9bac8a8f4f56c25d9325f8cc6440ee9d0c8607262be17393c1f0c864557c69b2121e9f0c86d1c9aaf8ea64253c355909fb8a0948cab3ec270580ade89c8ee798814c6561220dea5766cc9a341bc37332b2d1949dd260c6e99beedf25695ca5e1df67b527b2d5b0b1dfaed46b81293d8d96d1124600aabe5acfb02d7a65793ecfab4d9e7aead9bc2d16cdd2a9b485a9f42c5862792aed61dc6a36ab59a5125b82a5a7e39e58e5551eb9f82daf0cb0311756536dfbb0ea287702d8267d6b91e16578110003305ecdf27961801eedd024066b84a5573dca571617076dac0adab22ac84bc1e6ca4a99c243d559894cc82d8b0d6c2256be539ce543b3b639319a804181d6a6b4058cd726987004af862d27e6a804bd13d36a0e25d97e32cdd617e898b09753ed5dde2a98f5182146129a082aeecfa89a66d2bb2ca143d2c7630a8f0bac94687ba4b679efe05c6265e3945e9358780ca6bdb420d7481382a1c0eaf6026949588465b95fd1bbb595989d24d9fa4e11a0e04334dc0849460d59db7e215cdb36e3ef1363de4b6d09d288087b9baaeccf07d585a07ad07bbbe8b45c1ddb9c288c1252c105392aa68a0ddbd33c71228cfe1457999abcbea1a0aa1f5516a2faa034c76ab3343e170049aecd9ca8362a2b51e06506e7a844904ad9c6aaeb72b5e3846f9f53c9f64b6a272f5cd512860f97dd90db6713914c44ce30e092f0d0335430faeec3a141363016190756f3025ec0ddf4d9d5b2a4be7f75f01f04c66919035db3e803c2bdd2d0bd5261ad443c09f6ad0e9899a95ba208d8e0f6af8662ced2847a24c57eeb19f88518bce865a278dfe0b52e136e18062fac52e523546dcbbc803d0a0eac76abdce059029b1038c110789f5d2dbd01ce00fb577b4d6e293a87e2e8bca4d179797570fbd75e073a078be8bca4d179b9041d8afd871274a8f871e89863d039c4e8bc548ace4b31745e1e8fceab71745ed1e8fc6275f0da33af039dc345745ed1e8fca2041d8afd69093a54fc3874ac31e8bccae8bc528ace2b31747e311e9d47d6c4d0d9bb46d079684df0856fbe0e741e5813a1832280ce436be2e850ecbddf8ca343c58f43c71e83ce236b80cede6f96a0b3774d119d87d6009d8f001d6d5f356a75b2c36bf42278f08b6fe2956763ce0f1c5fd638be823bbd580c83cbcb1aea437c87c68b045afbc5623ed7476ff90ddd9d31b01e5e03bbb918684f526a583c685e5e93510c2b86c5436b7c057887d87c0b200f9eab691f8cda04077af9b942d0037ca768aaeda573b5d86627a5c8144da3c6a88c4e0400c8c786ec1ce74d8c81eee0b96e0caafde7c6a03a70ae40f58f73d5e9a0e288d59549d48a29021602ba9d4ca37170e44c084f2738023b949f0c9ca1748aa4e6ab660ee30d09f1e0703178a8183c580c1e2806f74541dc9e36e53e0aad8746e12f135ba5cb281921fb38e208bfffc5f7203594296f351ea38760179571ffc31f79edfabb1f3efcf7badc118a3b72cf17777dedfa1f5c79fbb0c4bdaa722bfeedc78ffee3dd7b7eb3e7d312b7e29f9eddb3efc57d3f79f8517a3eaca4f6430a3512b188ba69f82f252dac17257995ea205ede41c36632e79711b5b1945447b144191e25e4add7866c61319439b1808a5d31626f47f118d35c4abe92938fd2a0e862a0214c5da80a2e759870d1f528359bb214f13b55ae7dd7d83b0f8b7dae3b6115e5bf73f91395a67e9fa505c7c2fd6825a116375ce5b3d3aeefa4cb8e425d772ad49526f4cb40ddb2a352d789957f7cf0bb3ac17abca8330f03aceaa15663b72911bf501997236ed411afaa4c3947dca2235e53990a8ef8b88e183133951c71878ed86966a671c43d3a629749da52f550a68a24b78ebacecc589ce60144900e43c107113439782f82d3e05be9b51a9fc643251ea6b71a77e2a1020f335a8d4fe0a11c0f335b8d5bf1e0e26156ab7193c904a307ea5ed74b1d292af86fa50e04ef933a4804cc6e353e2375d0c39c56e32ea9831ee6b61a9f943ae8a1bad5b84deaa087135a8d8f491df430afd5b881eb68353ecb15b41af773e9adc61e2eabd5f81417d36adcce25b41a3773e656e3a35c71ab713764d0611abb7c2d94e5a9ba280fe9c98b8bb9ad72eb13570a494013222a671189bc740d31d87cba4ea4eb24ba4ea6cba72b4d5786ae37d0554bd71be97a135da7d05547d702ba4ea5ebcd742da46b115da7d1753a5df57435d0d548d762ba9ae029c2ecdfe027075a8d010a35faf32894a7d062bf8c42fd146a024cbbcd0134461f3d2ff25d7ab38542a7f92750688842a7fbb3283448a17aa4bed1a4a7023d2df0cb29f47e0a9dea5753e87d147ab33f9342db28b410696f415a2287ff46bf8242c3147a933f97423b28748a3f83429753a80e693f8eb4dbe929ed5752e82af4908c3f87825722f8067f3a05af4070be5f45c15d089ee84fa3e0df2078923f9b82d72078b2ef51f06a046b51f41d287a048f3e1eefc1e34eee818b8c0f28fcde8ba80fe8a86b39ea3e445daba33ec851f7230a41f0d36efdea3a7ef5005e5da7a33ec4510f22ea433aeac31cf5b788fab08efa08477d165108968c719be8393ebe91542819db2e1933b6ad13963bbb28bb480348bb3101c8a1f898b6bc28f7e2326fb908a6b3452e9d2d6399cf4be5130f634711b8babe12597b9c551e43c6ea526b8a45160b3c4a618950a4aed312b54f1e2fd2f2b4208f9764caf871ab3cbe470bd7edf2b85e8b5666227aeed09275a77edea405eb2efddc9da9e267e6882a969ccc09d338c88c56c94166d20a0e326f9773109da28c43dbb4e8940e98e4503f970949f961a525a5e6d44a0e5ea3b494d45da79c833bb84c84dea745a574ef24872016aa8411211c99c92b75cf2a979e5b269ddd15f99014a152c13dd50a9c422609057f7d4e049eaffc641711fce4413fd94fa2533c9c52b22ca2e06047bcbf4ba5e7e3bed74c57e1fe372a7d22ee7bccf434dcaf51e99370ff8c999e8dfbd52a7d32ee9f36d31eee3b559a3bf4552a9dc6fd6e335d89fb952a9dc1fd2e333d07f72b54fa0db8df69a6a7e33ea2d22c1786d36fc4ed5366ba02f71de937e1f649333d17f7cbd3a7e0f609333d03f7ed691653ef4f2fc0ed76335d8efbfbd2a7e2769b99aec67d5bfacdb8dd6aa667e2be35cd62704b7a116e379bc4d4741f4a9f86dbc7ccf409b80fa64fc7ed26333d0bf7429ac5ec40ba01b78f9ae8258b8c7cba11b71bccf43cdcfbd38b71bbde84fabcc8e84b37f1ce00528c2dcc4cf6daaa9a957c935d51b0080a274176732175947da3616fcbd5b1175175018370211819792dd99fe7013918f9add50f7be1509e7d67b0729686370ed6bda5cf4175d6a6435b974c658d52db63c3402d0c22308eb4f0fa981d546e81ebced000fd566dc17a1b15853756306d0b3d58f4c60a4ea0376c9e4cdb515d4ad765d304e825596d73e04689b5023c24d911f2903c9451b31858a8c0834bedca93353c54124f195804c14315319011ec9787e9d4c006e4c3ccf42c42c4cbccf66764e6e4fd5919b3df9fedcf219298c1495be867e6166c9a2907f87670689fac09c2aac9b89667e6fad332584decf72b32d5bed59f079af380deec2d402f899ca95c665a8eda7a1a5613a7f9b30997b9844235416e11c0730834b01cbb18d8d48432f7f393fe5c224d37dc75fd6a0a6da290eb5b14eaa050a53f8d42ebd919703685de43a12a7f0e852ea1d0741f0e03175168a63f8b42eb6886455328e29443caa459ea0ef12b58ce3b758211adcdab0733d60ab57d45cb8a514c0f5bcea9c9d87f25de83ec20b562f86adfde2e6c1594b84ead18de4e598d0b20bbc5b5818aa602305127418d99700bef9ba117695e4d374864f33af88394f30ce34cf028896fdc56a4769d61bc8d431e85decaa16a0a9dc1219f426fe1501d855a39544fa1651c6aa1500b2a75bf08772aea137724c457c00aeeb0734405761e64af0a763217a3297ba71b629316ef2c432cd2de5f0767b2697b5489df982ec867c36ad1f3917d0808e761b12b0707ad1cf504c947810516ab8b125f1fc6d797c6d785f175a5f17e18ef97c65787f1d5a5f15e18ef95c6a7c2f854140fcbe6a85a6a1db4749a51241e01905e625e70c4783db89ff9df0677744410403f1f3474e651e52511f92327c7ae26200af6b631ff4887180eb730113b89d88c5364226a4c448989a830110526c27e22cc4bb1368ed6e014fc54e2bf136a1335a334a13427077c0c3a5e0ab1772420246f762c35ac76b0c7cd6123141f7e7c290deb31c3bc1e035146e31dbc832cf6dd91f52bdfe6ed9fb7bdf2c8bf36f8ca2b8703f4d995e2e36a781b322480ead9251526bd17641d0296ca7abc67d2791db292677bee3b38d9339c8cd0824d5156830cb8523832173508c24c220c57e732491dc646997a18118572d43270abc9a470b3fd5497771e3cca29be48f3843c47f44e82b62dbab40556bd60ce69ea786c92078c485c8121fea93a492a8f41cbc2c8cc56662f3b4fca49c569a409a94bf5e6679c66b3894989e5978bb02ab7620791556da79fcbdf594330f05e54ae346a05d08f322e1467a83a737ec6c4cd5fcb331a078b99a62fad157056d39756252ec0303b5f96dc98050f1ac2c4f4b330e75de6a2a0f56923cc69b017b4eca2e005432774bce67a6968c64cc561b778762cda9443b3b3b93cdc1bc5cbb42676b138beee525412bca9188df95c3e5578982dd03b135659e80207b3b80915ad566f6e010418b7e68bcab4526fb37482ada2b64017b7e991a00060e1beca956cfedd8434bcb45b5c5d66d7ff051927181617c587c17fb287e50afb1cde51e59dce9b5478670b515736bed23c8075d0149682939468c13c61da8430ade6d104084fba6fccb4e247a13a09f1c21d6f2c32825bbfc7bd84673060ae24bc9d6d53300c4e2e4904fe674bb397839a255a6ae053b615344d0e3c38dcc171176a323cf72ad85f9053e5d2957a29983df62ae030e8fa95ec30e8b2c36005bde32d212e1c065de9a54ee830f81f0281e95780c9cc30af380c3aec51ce2e8a2eaa75f56a74e42ce9840e83ba7ac8372a0255167d06b95b1218e841691775b9a09a54e9fae578e123a69c45e402ab4e1eaa45d616bb79b988553fd6d313583c68c276b166d3c70dfd8a8a5f2e3e00f3435ea3eec91287d8662defe253e85a0e20d15dcba7a784de9857ec5a655c9f26b645cc4afd2adc8594f41a98ff7d6e1e2902f18ef4015e85f113ee2da665927856a14704362cd7ea8d8624e7a4197ef3822c70938c806810097aff8bdc364a5c2e6d9183fc931061988030847f1ab54b4425b878b38344f44c84b4f5d2bd4288d99a3de04dd95d11d5f8c317f512f3ba1c0f23368f8058fdb721960dde1dccec6c735bc3cd97e08284246c9a568003eabc53b954438b4183c4a00331e84462d0e64568f739abe822ebcb7e0b93c14523929a3d576f196559c99a345c396dd6a44d0e419366ff0dd6a4915d34ee9908ed215900e3c11ceeeb304dcce6102c1fb3908912b41a498e43ba4494ce89d2c1554649fd2dd7a0ea6be4d88491d123c6553b29a25a471c3cf32af6c8e5a7c3275fb5132f53bbf0f86a155e5523fc5a9233d551f8b7d6550cf6ce9d3ba5e75135de1fb89a6a261088369d4320e40c0e817c5ec96e0ed5159c2c82f4d1fd91af37bbdc9a2c99425f6f9f99c0e416f50df7364b39c3e14a6d9d6cab22ea7505d59be145036f8bfb5fb8ef05f4c2e03e0464dbac542cc9ec60ff7d2f9cca2e19ecb693496253294def920087dd47d8c7251954777b8be185ccb37caccbf1d88851a3ba3b93ca894b8c819e4e5033df63b34b4212217b8edda9abf5be473b23d3ad4c52069f548e061a718bc53004bfdc343c890cd920c31d02fc49affb68661af53738d914773a2b2ec4b7d3bc6b94074dae529334c9551246d8a6ed9d436f93e258edc816c784ec125232005309ec3544d0134449e4a4412bdaba0cf1837554a0b74a4f6f79a7948dc5d52f56276c9244152c8dcccb6181a9906e0799e39dc8a368058fc7b2a539637505f59b832b46e059429cf0f2b3e004d9a88d5b459a4701eaf5e2a172d3b7c6bf87bfb2a975519317384ddda748f78533d94113aa3174019fabf33651e43e8e8ce5da670a53058f7c2bf2083026f608805398258ad27e937b323c02f621484d76c094d15f3c0210cb95a93cb678b15b80853ab45b8015ba0550b2a74bd018258082c75e0f305f2d0233aa8179ba0418c46241b61498c72604861ddb9bcd2f606c4e08440f9972df6beac6e3260033435b1c4de678669c42b81cd30ba81c66b097294d6a4754359aec55095614a953598c9d863ad973cd2a6e27b4200cae308551befa2dd12a9083d5d34c153c1aeab8411611d5336cb040de1abfca7bdb3cd9cc5967bec6ef5f95f7198f53649c9accf48933678c1a28adf4ee80e957e28c125646286a1af5ece96755c2c7de5984b93ffdacaa34431b80233bed3d98023cd22e2dd24ba6f3312675e688e54ff39d1a18a6bc65680442e8d56f151dd05e0df731735af4b610cfda5c6686e0f9550675b404cf19c78be7f8cc453c9f868a16c3b3624a78568cc5f3e3df3e069e68700c523349271ebefa126ee9fdc8c0d1e999e29458151aa7c4a593f5fbb4cc48110a77180f515c95de684c7d4bc1a7cdf03a79afc6d3584c194d6aba0aefb28b26414250ddf4fcd77fe874531dd5449d01fd18ec7ff693bf4a0c506c652c961eabf098f6c20dc375e6036671eae374066a4da68a20f5584dee4f4f077c5e7a3a22ce91f8bef474dedb411d6b3a366ab08094338faa783a42ea354cb3867e61eab12f53e5433df6abfc59fdfef45595962b2e798c203c7a96498bc4b2aad2aca841c0a80218552edda920735c41fa2ca5e010cfb64bdef1991db26fd5f778ac3e95c4467a36307a04c6d287d3738a3b9067fbcec337fa73de1ba8814ad9ff0f674aeb1c6a1da64f60accae7d3559c618e3ffb266f6d6636b6e0ac7daf8fbb61ace7769f7d2f139286eae9381005c7124c27d8097a1ac7ab78f2c1b855a14ca0277c51e5ad2b6e5397a6aa3a8f6dba7bb183dccbf17cceaf820e5f4550a567858dfa88f9b020763c1071a6592803cd4ff7e998bea88054670a0ee7367615840fa6133cd4f6d3813d83aeeb8ee0718b2cc5f4c9543136be278c446f897f90c3d38c25dc5464c690a73255e0004f362ad7990f9972e205bce04c2aafbcd9d4674078d81fe54173f0349d8af57a7e557fdad3f544cceb85cceba122796fa008f108f5e4f023743ccd10cd74635205af697140f5cc28edd67551b75e38ae5bcf88ba754a7a75971e730c58aecc79ba47c3cb4b3fe11dfba9690737dfe8e2ee8e745c0c32524459095bc343aed97c2c5994195fe0f002eb0b499e61c26bcf4f79edba0ab19d51cd235660eae16cd4f6cae6b18f2e9ec42d8d8138702e8058c61e6ac1f5df615b16fbe919636060d1efbd751e0fe3d8a4428adb2d96586f6ed443ff6edcd92dd7da65854dff8a99e1b63e6cca463182fb5553fc76e78b992cf4e115e04849f2c2d373c4c99afbc88d96f46d0aeeb6108b792e9e6ee1175ed8f7a9f85d16667eac24bc0a02180261a021c49d2167e078e2f70a2b28b640e0754b252cfc04b11badd0a434e1c04823c5f6b458e7481d8126c20b81ac6ac09d52d40b0e2556a85db2dd1fbaa19f782713fe81ef84e311b185a617d1cd0a5de7b5fd882d28d8a04e0d2013ea4fedd7464ab639ea6df2e178422aa7b7590613299286636aec93a41e444608e28d4610bd92df68799d66dc7d0bbd408a472fe3b3c028e35e1e056236371ed28b047ada8ca2f7c5a20f98516d8762e354c82e25363ac6bf4f9629b9a2a090d3d3743e6826b8446ffda540ecdc87603db2b09182f77058abf4dec54326ebeb60dc57046d289a1598aa7fd5526e7cb7bf81ddfe759b7d55547c55f04fcfdeba786d78dc032c0575dd70cfc5d6c31c1b302c9f73d11cb08ea65e4919e974796cf0537eaa2b5da6178393be85f5be4b7299723f85b042d8d5a749b1b1c82d4eb34c423e532e9d86e76315c2f8e50447214c5481f37528599467538ee7677aea66065bf1bc35c7561699bcadd74abf2b4547357024cc43049350d0f6192f1cb661eae9b2e6139adbf1d6982456a4713e4534bb4bcaecced4f3373f626ca51b962daec0c09711018d023ab0e9469534ac2a36acc9bb624ce17ae60d7084fbdd32d31e3677bc017258efcd81a8dbb79ae6dcec41cf475e651272e815a9865a9d3ff41c75a6588ab4a4c0d17ac82de79e2871bc873b3fb44a1b4eb435a100ddf57cb86620596285715134b276c91e1ee53b39d9c323f212e6b987942c9dc8ae1376e0a0e9c6230af38f8754bc14e59d8de513a5575be0db66044f50e52b610c2ab6ab6e3fcb77c04ed608d52a0d6b05bbc3cc28e53b94f52f8e9975772c2b497f0c0b00e18eb094dd8a81dda45fab3c805e2f3b8acc1abcea90fd44668db7e29835dd11aba945978e6afe7552185b8a19f71950a5cf0eb785705100e212fd4ee05b1787efa2e384cfd3b5302fbc2c7258b610e94d57b1bd54aa26b6934ad57875aecc26bdd88e263de320688a919744912dc5c8e551e41daa18bb5745d1eb8bb19ba2c8ddb1b4b714d38ec4a27715a3fb8ab15b3952a289fb62e9bfa0703c0f38531ff9c2ac4b98671cdee71d9e161a6c876dcae992decdfbe83c6b9ededac3db01ce17feafce24b507018d9eda830021f12040483c082c21b1584e539c650d1c9a38cb6a0e21cb391c4296b39199ed9f67f2be39d83fdfc621d83fdfca21d83fcfd0fd90e068b90620941a26932586c964a9613219334c266386c96464984c8686c9e522fcc0d67628327efdbc1efa99cc71228b7d7d546160bacfe173d3c23148e42060b0d7f2363cb380fd21d1781d4ada48268a69cdc690837d757a68e753f6c2c341d9b1484ef7e4d3a8526836390f08aff80828a9bd13cc8df984cdebcffdfa682839d18c6e7d6cd9ab638725331cab4d3d7ef3f6c535d8eec8074559cbc3b53992a4248ce5a02b4b1f2f10da4c31fb340b99a49c9bcb48a599050d6cae30e5097d36c5964a997472b41c9f608a3b138df96c8184158197e37401341e17f39d254a0ea7403647004862d009f5d228827173357234cdb55d376a84d05c196b0444d97a98b442bc337246145628fa8ad3c2123ac468902024e901470996a2857ec83350264b2a2212c892f453115938ba840229502096e42814488ca54062020ab8471d7e0feae1f7a54987df9726187e0fc687df9779fbd964c3efc1d5b1c2fe9b0dbfa3eacfc32f6f0afff3f0fbbb0ebfc4427f1e7e8f73f8fdb16d41a425c62ff9c94ccf127f013d330c7e4c7788e2d8815294c80c3ef7e3674f90711347f3c196e6dbb0b6f1829f13c8a13a44933a2cf899ae3ef36501affad9d2b770ca304d29957e825b8a2d2b82bcea8999190d82c9a57acd2e83c1205d260bf71832148b3b0fc7d3220b64be5fc6afa82e3f2107183b3c71a5e9288e327365fe5a51327fade84a570672b64f4a4e47b8046b5b1508f3fcd5d2f3573bf4070bc712cc5f352e0e26a965e16a666cfe5a26f3573bca533a7f75a2f9ab336efe6a49d1510d1c89451d82499c78016f3a553273358bab924ace912e9779abab0f330ee7ade6d879ab13cd5b1d86349ab73ac79ab73a7ce684132e5d46ee46eecba6e5843e6efa68c6925324025e7164179b680dd39165b00478ec41de46a97dfb932b92bbe06e76013bb1f09e0c475655136c9078708501b775839a806465790db1151a8a37ace8cd1ff12290ab3a435d1b27150ff9e5355d7e42bb359575114189c9baa4fe1434bb54696617260f8b7d8830f26b6f72624b626d57b6244812ded9efbd2fb4ae70fb24a08df00ab129cbdabcaa9862b7bd44741ce3a3fbb52b92f6ddc09631f1ddc0c917d7aa79fae048f779d3b2435715a17210296c21a5c361474ef3e0a577521e0267f041e8acd1f609a098d0283250a199c80957b04ded418261114739b2db0dafbe1fb32412dec8930c865756ea5554e9991539ef436a9e9cc185c2fc9c569ab5e2a9cfc44dca570320547036b4f761a599c082f3575726799e1ccfee27bb84d8dab64d116bf56801af765b1bca6cb9b1b5908a079d70beed478a44fdac652ad1fafcf862fbefd9d5c5885c5d8cc8d5c588bbba18e2ea6244ae2e46e4ea62fcffe7ea42522513ecdd67786fa0c6ea0bbec2217162713f5eea962487a51abaad4cb495e2c91f379d6e2ba5abe003db745b295d191ffca7db8a427b683c94b6c2f930d25608495b2934a68db6429cb45598ce89d2d9a1ffa905225a6389689510d12a25a21523a21523a21511d12ab61557e3fd81aba96602495b21246d8590b4950a7cefcdecd746adb3e7cf4e637f6a4e63a657cbe3ccd7f669af0f6ea82fc64e522e3afed26ce18d7c0814fba5a77893160fae8eb66b1087b3bb118d4ca7a7b57b8c1d7aea5221fcecbd699e2e4067a624c935e2c2a07d6ae1bb2a5edd2bb46d3f2546733bd8da1fa456c5fd6fad28ad88ecb7f3511e005bc08d4ea83e3ed05c3878feb990090af986293c515fc213c2118111d9b65e47a130f5d4ca0dfebff800031660ebf4be072e2bee918d294c9d98c86aa1d1b2ef7cd8facc243888a5561c10561d2bdf5ce453613e758c7c3ac39c311918f080dd13235ef6654ee06d9b27f6aa94cc5119bf0a59fdd167ac1e517ad7873e857785288db625c7e2637a25d6a3af7cafe4a85eec9176f4b1f719561da12c1d31fa8950d87569161edcc916cf2474e9046c68c108ce87718b87b839a2f6995d381f8d862bfe040a7c0ebb42fd34c109d38e3ebb8ddd357dfb3cf9a6916e23db0da781892efd5d95b531c24939ace0d2f421723865fb140e17bec52cf1344d89f3a69db1bad86ac81aa46cfb124755c55fdbb2f5f6aff02361a17f2a708273eaf0b9954ace8ad59ffc31f1710f33f82d8ea8159f549ea25ad0b0f10118665c2b9ca25a323fc5c1fc691e1a68b2c68b991a16add48630c0385c9c322baaa88bf7085015678bc958bebec06a28bb6de80f5fe88fc9d8ac89875e9f557aaef639d3b48835e0dfa97eaf1ded22b9add31d0dbb379973cf9eb09b5d246cffb431a6837139b2199e73b788ed8bed0fc4f8626ae4c9312c62295906957942bd5e0665abc8ba8c23db98d4520b7d5ff639e124e23abd2506bb36b9d44ba537b5c47ad319616f1a6143c6b953146fb0987c8711a7c8ef28d39606f8af1efc62a3de98212f4816c51136662460e849621b33cd4de0786dacf24e0e07c4fb4c42c80ee7304743a85a2354fdbb2254ad334f8690a387f1cc78ac70365e98d20b537a6312a56289aa43fa541f4525c0ee8b84d028298e8e29b60da6cb98546534efc7378f7c7db49e7b175c254ac6388865a654d1ed5053053b92ec31bb91ec5a160b8eee76fa684b5b8cf222feaaa5ffa442dd86d1b6bc119a7402241946bc9e15dc0d7d6c52a34188e49897d6c38a2fdfcfe0626bd9393c5c1e49c43685d924de137c7c050309970ca2f8fa1c5cf6417f5970c1441ce6acee5ca60c2594a364fd252218ab5c16b07eb9ccfaede0eca8fdc4949da990122cd9b77276ceafc099d64f7f8ff7af4f57ec2534cc4624e2ce723191f8653c3187f9a43cb22fa5249900cd5b73f88c724b2c24766cf07ccee23d39657173129b55f860763e905db63e4e348a5a7c12279842e4b90c2bf0fec028eaf028ea6014c54684247fd08b0d3c6347d164cc789249606661c9400adb8d778592213429c7795ae7c93e2b3ecc822d997a1f40c60a5bc40245cba216b1d022e57cd0005ac4e5efd158ec19c34b52bebb528f9267e7f43b6d38cc544a09a648616a91cab12de208a9138498abc95dae0d5215300ee2c893263e23455a24a5bf53028ba1eda7e4302c2570684cccc014738a5134644546a20a01c6f4ae54cc28c816fa7479b2f3e45309ab9cdad42ab6695135900f66611b8a33186a07d83295d11dc8d6b7e8a4245867c3bea5fde7f46e17f92a595c8988569ff5301e36288e8fc552f4286f7621fde14cbd4e2c1f46a344d02a4e96cf09c2c4680ff2576e9238c98087695e968609d60ecbb11076b0a66dc9a9b6c40c9047c45e608b323eeb22c2a63a7774bc804bda8e69224ca172412101db5ac22fefe213e8b0285a0655864dcb0969d3724480f764739fe2fd28e1390beb73d8dc6768a68ab1149bafa7691b3d18b44ac83b8d39531badab8477a745366c2c95c33cae740c88b5294c9df02b259514c465432e380445460b0b8215e25a345356a62a2b2cad2db16756225de98a3d3bf659b132b16e5305da3a2cd22f114aaf64a9f49b16fa85cbd77145fa5589f43362d24f85f959e03ab1bec6d2cf19dbd72c21b84af33e2046689ad607aba4b3f0924c527749bf523a462520a914273e1ec4bfa9582dd496fda8ff4101b774ef18abc2d9695b46ec707fb47cbf477f2ecbd607ac6454283b9d6806c29b4f4a64a78acb4ef91665283bb137b33803b1213b590700835a6c8016d9c99307678f77bb723f6f852a564cc79dea407b91dcd64503ed25a2bfc676d2873aebb8adbea2f162b8c1926e44313b5278431517d3bb9d6a6235d78ed45c3ba6e626265273134535171aee4619b29767ccd800fe769c32cd1b47f400ce9c5788adbdd8c176bdf6c2af3695bcea0b9765ec605d3868af2b49714971e1c60e176e180c197749a0bf3d1c7745cd66eb1d84f45466ade6f859ab2db356cd33e684b356bd5891c026332c14efc9986bf584d4e9f2eed11c6acb160353c65bccf6eabb7925213aadfb7725a0792c029ad016428f4d252b0b8ef6ece0095011002c89ddad3052a7e089c25b354b38d1d15b354dfe789770a2c3b1baef3aacf7522174afc57d1e7f6838235fd3e36f385f1b3bcef60f3c83d43d83bac53591024b0f6bc23dd447f9d4d89ab1b6025336a71edfa7c626cacd07481dd7a7c65e5f6e6f4ab9674d2977f59472cf9f526e7f4ab96ba794bb6e4ab9174e2977fd9472374d2977cb94729f31a5dce33e3576b4ecc7f9a9b1a3f5f0893e3576e39fca6ac3022bc5128c55aeeb6cc7e6e14e2faa1f3e8a349d101c670c384e2d7f5c95a529effab5c27347022f2d5f168bf4fce8d41f388671b9a5278e38383924a965af231bfab1648eb904f8b74c9f3452ce13c47d72d248f8155f3eeaa31c278d247c974f1a49f04923e5ec278b936471d248427fd963cc4923657e39085916e60dc12f83982c035ea876dc49236678d288197e9a8ca627f8b2c2984f93956b830d2a4a8012529f1b7a688d393948136dccf941963e3fa85a42727e10ab6954405274b497892629ed7f2767fa281c7ccafad9ba6850f3725e6f741eced3163b4bfd697142090b70db3bbaed93f1b64fc5db3e296d9f8ab57df278dadef193687b27cc1b02ecb0a94eb77df2b8da3e3941db27a3b677a4eda5bea9b7bdfe6c5f7fd492579b5a8d8d7d2936fa482abb1b859fe8060df7e0201d6a81e42e3e769066d0c98268aff956a32a76f88531eec00d5b36df51bbab98d053da0a91d09fed8bbc9d6cb8a1855aa2e37d50c1552c852fcad99931fe401c095a2598fce1a92e76514be4733472de750aa2010777c9e4336672f98d62339a398e0cfaf3bf43fcf5dd9a07c30f376321c4166ad8a0867d746a581351c392e1633c35ac526a680822c7e44ca2463ba4454728266a781eafebf29df327ac6b8295a718facfa96370c11f970574f662fdd4dc99f1487159132da74538bda82cabd4054cf8ca925209c216e26e47af48e9aff8a4e11a4da839da4d2b3cae448e43d3a68f3ab33e9378908d4947cf139aff12a17b97c95640368ef0977d8243fb43cb88afbfac0dcf4a3904b605ae9656299356898b977c0ef9c8b15a8c7dd8feabfa6d22eab7bacce4c4653a63e637e137a469c2b45bcd0bddde12701bd41f272fe9b13f1adf63c3dd9a4b4c4fac877f840eea85d6405bec0a6189d6c425aa310aa746dabb31f2b28bda79ae30859c61c3ec9186bfe051e3874c15dac4649a8d25892093cb68d3635d6e8969ac3020506a21cb5886e9c33522ce10d7c4f0109e3ab38e93173fa855c35f3af1f9bba65cab7b9b553cb3713c23b6fc710790901593f121c44f2e31eb65e9fa98d54f3b9eeaa71dbdfab0c727211fecf1dd3ec97ba5825f14bb7d125324a7c41e60471f261edf5fc64c36b8e224444572ec78a659a84389794a9885e71dd1f1d4969ce1174e51c6236cea6fb695622b162bee08419a2bfda35472bffa23d4421c9e2e9df71da5286b4c51fa602b58a03ea8e6b9d42de8ff3cee42d2d9c4397ca13691b99fd5ce43c5cf0afa26af73cd8abbb8578861d5ce17b10b4d46134f67e5808589048ced7d5ccd2b1558ecfdc287fce90dd8297d5c9676203760e0e79dff30f0db3c1087cb71d5397e2dc358dcc06f8be5de942f2a0a4da3e92e6f14976ddff201be70adc393b3c7f4c7dee4bc8e83a14ecf2bd11f90554efbbf749553fb0a2521d5c6af727e425b5d9d92554e4c4c5b78bf8626ab03b25afaf0cc4c820b8cad9be035f5e93164d50b55d8eb970ab5826801d2892d40866de75bde27b9c18b5c9398c80c9100859bb821c75922608c47916334e53b1d5551ba30397e69b265737169b25e96268b1b366c99e199d1b29e192deb99d1b29e29cb7ae166b3f8c265cb040b97b1054b6e1b5eb0fcf40ddfa85d5ba9827be81e5baf440a2bb8fe1bf7cc8013141a4815265cadc486576abce20a2535a45ea144931657287f27a4409930d452b27e196d3f0ad730f9a88c8996f4c4c0afd73615af6db2342bae6d62cd5b2f6652a1e578cd8c573176713c29acc7cbe3fb35ebf11a5c45f17b8bdbc19fdb73a72be32d0a67e22b14aef4b1f9029dded1a181d49b15182c6d6088add4dbe14a3d33af5e7f346599de18db0594e083d29dd0b65f3ca354b833f41a3b28e7945aa1efcad4a44572bcb4b0445a58222d9263a505248338d3c9e6905c7182965c35b60b26f547574ac7a6e4d8b1499fb28aaf1e489bd5e578e1712c99e47448434bd67a3943157eccd169e2ec252aa603233ae3d12b3ea7e49c2279961396a822ef2e5e86a91721702e2f362d307496a355618ca9c21853053fff7e0bdbaad4301767ea95795d9c66da6271e1fbb0b8f0392c2e7c5e88e785b2c42da76552d9b54bcd5a3e7d28747c35dc43b62c338d1ab1419d37dbb3dc95e769a6a1b483821c81cc7d21c576a9480f34574d30b83ba18bdb18fd451b984a7824729b69c9f1068f713cc2a3af7c4622e211d6354ac9ce51a59cc251a5cca2577f35bf9832a2c4d498857f2874a9a80571cd6fa272ec71e588f4c4615f192c3ece7b7d446396e0d3e2431eab2d215f9ccd62e433c793cf1c4fbe22bf857b29c7e842a36cb2bb0307d3c6f68ce89da1fa607ae666f6ba0e8f5b9a851f2f637589ab9c597a1ca9a1bf4dad4db5e5fc495ffd91695ea5ce8b4a137ea83a41e321cfaaf55e3d3e05541ff19ee26317587e22b33eb2517b0a875f90863f8b8c47a6fece347f5894e415518ecf72a0d60cdd6f8c666d020b95687ae73569179948dcc53b6e44b8df4bf9e29c285b71f561569ea8cd8cf0ac70f8f1f5b48bf4db330cf780a3ca862770e70eea37173dbab1a21b336cebde12fbde745da4fe843a8359a208d54fe006ae5b3d7206170701f6077ff1be174e85fbd60b748f7984238519dcfbc27db38fc3273ca8ef8edcc2e14d9096ed43f02b48c90c3b74351b0fbc15294656d1a41f734eab2f752767929595ec482eeb4a97c77724c3058194bd32d9914c61474bed84f68b0d1dabe0ecc4be5cd1c1c697e82ee0842ad8269028257b9033b1ac09df898682beb1990a9294fdbae0c11bed80f6ed28d3769d492b8a58e959f7baf629274bfce427daa75c28d9821cf3e250255e1caae8c5016af12c44957871a8a21787c5fb97430b64b47bd97d6c4ec208768f1aeed065ed9dddd90ebfd0ebb77777f76e682f64fdbef68dd90163b027bbb52fbba1402fb3f97c6fdeefecf12febdc9aed68bb745b213be06fe8ed1928e40737147af346b66743ef604f219ba7c43dbd3d8b2e6d1fc83637f91b36b5e7dba984bcb1eee2156d2bcebdb02dfab878f0a1af18ee274d63fc5f7d7d7d437d63fde2faa6fa25f5cdf54beb5bea9735d437343434362c6e686a58d2d0dcb0b4a1a56159637d63436363e3e2c6a6c6258dcd8d4b1b5b1a972dae5fdcb0b871f1e2c54d8b972c6e5ebc7471cbe2654df54d0d4d8d4d8b9b9a9a963435372d6d6a695ab6a47e49c392c6258b97342d59b2a479c9d2252d4b9635d737373437362f6e6e6a5ed2dcdcbcb4b9a579d9d2faa50d4b1b972e5edab474c9d2e6a54b97b62c5dd652dfd2d0d2d8b2b8a5a965494b73cbd296969665cb08c46554fd322a7a19655b4651213e1ded85762254b67db3df5e286437f71584dc5bf29d4ceb81825fd894f5b33d1dc685e7ac1a43a5fbbf66b89f71a267d0cba20b3149ba5cba2ae99a4ed74cbae6d275225d27d3f546ba489b32de4cd7e9742da1eb2d74bd952e1c43ba8aae73e85a4bd78574bd8bae76bab2746da4ab8baecd74f5d3354cd755747d80ae0fd275135d37d3f509baeea66b0f5d0fd2f5305d9fa7ebcb743d41d7d7e8fa3a5dcfe373f27f4478b74d00f3478e02f7bd63607f7402f8bf4bd7f2e3ff7b1bfd3bb36949f3d29665ad6f39e3ad3ad650a6653b8964aaacdcada89c56e54d9f3173d6ec3973ab4fc0db3397cfab997fe24927fbe9cc1b6adff8a653ea169cfae6858b4e3bbdbea171f1f23f85bf2df9de9e8d3e7a7a0731307174273d0e74be2f5be4d6ef3e63b857a48ce3ff03b36ba627345b1a5635b65d72dedb5b2f3c3b58d4b8a4b9edc20bfff2e2b60bd6b69d7ff1b96d13e65db776dd647991645cde479fcafc7df9e1d65fff6cdefb7e79e6a18bbb2f79a066ee99ade7dfe97cb170f3daeb367da5ea817bd75eb7e1a637ddf8bd9b1bf63cfff3b9cf3cb9ec8b2fbe74d1833583df9cf6a596def39b573cf0cc0b5fbcf8b26f243f7fe2cf72576cfcb7f469df797ae8f2fbf69ef5c2c59b4e3c6ffad8fa7a06375f9acdfbbd979104ed69ef26a2e507fcecd60dd96cc700f7fecded5b3b370f6e6619bc25db61e4b3fd839df92c490cbf3b0b11d1db932de60dc56928308c95e11bbf233bb021dfd957e8ecedf13792d021e14b15b4f7a086b07469b5bec14bbb3b37f85dd96dfec0a6de4134697bbee06fe92c6cf2c3f2fbf2bd0433fdefeb1d18c80e0ca0d481ce8d3ded8541020ed9ba4bf285f00c102a946b4bb673e3a6020d15ed8394db1fecec293437b5113243d9fc65048ad131d84730f09813413361a5324a191105e81d25df46a8e5b3040641b17990a87469b614e94deddd5c1248ac614250c365204c84e5d18dc62954b4a1bda7a7970bda40d4225a7550336c28746f332ecb0ad61d9d343a1638fd10d5d4d1ea1be580b9d0bba1b7db0f53750ef828274a6518b79c7eed8aebdfbeb4ff3bbf1efdfca7fffef3fffcee27dfb9ece7d37ffeae7bae3cf38617fe7afefdb7dc973ca0de7dd58cc7af6ea8fdcae5151717defb78feb7ffe387237b122fdd5ef8f9ed4f3cd179fbcbd7ac7afc674f7de289aa67efaca8ed5df8cf8beedb5958fdd2bd0fbde707bb7e3974c13bde76cbe6a71af63e7d8af5d0d787dff5e57f770f3eb5eafa3d2fbc63c333898d0b16ccbabafcef4e6949d79cf6dae685e7cdac6cb9f0a281f2eca3e5b9a1d3bef1e415572fccdf70e7a12fdfb5e6b2797bfb3eb8e6c9bbdfd1fdf5130eddf0dddac4ad2f5ebd64da2ffaeec879739eb8e78454f7d293cedd72f6299f7feca1a6872be62f5b7ddf81f3d73ef3f177ff7bef699bfef5ca8f10093ebd75d6562378ee39c3dde73cbd33657efdba5f1d797cf74fbad7ddf39f77fff2f17fdbdaff542841b48e00fe2362157a7bc14ff982a1e3c364856d7dd9b071374205c9f76e26dd234fedded7dbd30121d497cf9262627464b56822062115aba3373bd0734a8118bfb061932faa0a73adbc7e336927d90d5de00a230a117f1413961410ef2e9bda07fc765fe42177a5d731c4cbb80d19594657b91e132be8eabd34478c86d403929bf2e6db49bb6a4358d889f0df3cd85de86cebece9c86e358473a95e264398ded03cbc817b822fe50e206fa1fdd2ee2c906ca7f79ba893900e57808636bef2cdbd1d9d976d3b4abdba029de6b82a18ecebe06e19c246bd73239126dfb9b93dbf8da9ba6553b64717096ca8f74ac147a52ef02e25eec66c41f48b697455d1e5695d63065d9d3d43243b3afca1f67c677b0f70022aaf47cba549946bd72da4b2fe2f60f360b000d00700008c010101001f780fd201cefd9151354036328a17a2bb9d5930c45555c9e80fb17a59d5ab72bf28134ab543bf6be24b3bedf03a7c44ffe90cfb72503f2595b321f1fa13c81ec00100b00878da8d563d8c1b45141efffbeccbe992e3382e421441504482e49c609d521d050d4214fc544758c63bcfde917767979959fb0c3505d05007094508418aab2e05d14111b945424ae01aa000ba2051d150f26677bd3f76cedc14f6ee7b6fdebc9fef7d3b8d3fdaef5688fcf7f93ec155323fe4afab6f9bbf7bf6d1b420b8f377a7f3cfc64cf268630d7cc5fd1b37688f5fd979b153daeab9be3db4141f082e06160db5e34bae27974654722ab4758ade1a5d7dba894e2c872a87946bfe5880ac0aea41d548dab603f650855ee7a56e93da9a8fa806525aeb03d5a1048bf101289db75a495d93ea8a762428c777593de4425feb54873051abf8638d810f1cbd7f139dda7e28b4da0e407a5c6152c27261046e6a521b53aed539f39bca2e9e9e0e299fe1d8cd68bfed502eac804a4c578354e4fc86470f925209d056a8e8002227ddeb17359503d0f35a2bb07572ccb6d9ac2515ca540af328bab8d679a647156e00b9d46a3df3ed028ce924913f6bfb42c381b6fa124ce55554b85c2422f4ce6aca4024a6b98ced202c0452cc38d52ecb78dec5b687055e66f0d4bc0797f741732f2d19833e48090c8d0e2c3808b8a491dd980be68f13abcd792f0cdcb46e4f1a25172e1760256ac53f981db0b5a86510682752ef74a3f264f0caabda361536b8d151a4bc16bfcdd0b83e0fe7ba899fb3fca8b4702be2ce98937223198468f65ad9eee87da3cf0575316869e5c6abcd40d9920726e8bad212cfaec7004f20db0ac29ecb6d0b919fe82b811f248feb99cfc047abc9c2e0a083d4466d3e2684fd9bad6ca848b9822ff92373b1ec749b589a61946ab5906ad5f619c44f7a12c44f6d09ef875c8207b37a089c8378136eb725b28f9fb054fa135357463ff588ae2013347cd44b5f629e0a04c344386b44c175769b28d10662b5de44835ae81e3223ba4bce9cefced629d445ca39cb454014ca7321903e0bed4283cbe752619a6596ce698cbf8ac59b31355b4ad50d63693a52aaf6a5ef45feeb0a347e0ee6f05841515c9996d1bbdc43469e6be40ab2a815d9d462ec18b689734c0466f6f38206fa32dd9f73541f790609355398dd95913742f461d5e2f7082e7128abb83d0525292de07941806dd711d92b52aac70f8fff0e98c002c947735568706519711c493332f2193a6b2aac2a0b5d7862b18b3823a148b15ff95fecb7c280d18414aa4b49c1a4301b90aab1cfb0d1ce7d2c49b919bd29b013ca2b22ef3c5e292e7f8ed78becfb4ebefff8f22b1f7dfd7281e1c8de0fbfdc39fcf2d53c6be17a6158f9e6d36cb8c9de17b72eb17bb7f2138bebf7871f9e7c950ee0676f757f0576785c842aaedbe01d1ea7b09c5d84669824e4c7d78f9da3691e83b8f69efbe4689a622938193f78eddba369111d84dc7ff8ce9b77a73908e07aefbbfdbbd3b4d9b8761fa0206bac49f9fea33f7fce77302a83fbc649be4fe6a6769bfef4db4e8577af1392dda7f05677860b5869d96d86fc072150af820001 +DMLOG ACCEPTED_BLOCK 4 04000000040000000300000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100030000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb2d5b1b639d6ae94fcdd0536b224644931573d1ccb2a0c548613cd1feea18888b0f87ad8b5169aafcab7e8eb69d0ceea2aaf352570f2271f3f0b32d56c69033f40300000000000000010000000000ea305504000000010000000000ea305503000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add801000000000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba033b3d4b0000000000ea305500000000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb21729c7835698f124c8be1f086f7e6001e73e9c3554da1bf5cab4c4b3860ae83b8bdd3e98936ab78d42df8f5b434eda1ee1a725329b68ccbc00d433e080116930000000000010000c105161a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea994a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0fe0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff52668dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c438ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4052652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead450715443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b4bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb406bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc35c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b63320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011afce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb409e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc168cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7002052096236d23a2f907bea206d91251b18f98eff6c1bfb3acdc8a0d7a1578fca146b6bb6825b1aae9852dce7197c2919c833a423ac79339e1357c9d46cbec943de0000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef261980000000000011709e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc160ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b72412652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b447670735c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c25443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b463320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011a68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a2974286bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4058cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead45071d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb40e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1dfce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb40001033b3d4b0000000000ea305500000000000314fa060372f82666849e4d15e6726a494024fe2c682ff988d87f7acb21729c7835698f124c8be1f086f7e6001e73e9c3554da1bf5cab4c4b3860ae83b8bdd3e98936ab78d42df8f5b434eda1ee1a725329b68ccbc00d433e080116930000000000010000c105161a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea994a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0fe0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff52668dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c438ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4052652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead450715443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b4bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb406bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc35c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b63320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011afce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb409e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc168cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7002052096236d23a2f907bea206d91251b18f98eff6c1bfb3acdc8a0d7a1578fca146b6bb6825b1aae9852dce7197c2919c833a423ac79339e1357c9d46cbec943de0200d0070000a5100101002007b4601e1e0103d3bf16298ebb6907bb9074ffcd1efb95d3342eb82dc86e056a2a15ea47d496b7f8fd88eab6146003c28b587e42f26248c351fcfc40ca1333060100fd810178daed7d0b7c5d5595f7d9e771ef4d4ed29cbe4353e0dc6b94145b48d2344d234a4f475a6881561e0e8e3a69682e6d6ed23c6e6ed2564b13a03015510b82bc7cf0d296d7c827f88d0a1f06544444ad50b03a8cf6fb869fd31971ac33fe6650d17eebbfd63ee79e9ba44d31eaf8cd67da73cf3efbecc75a6bafbdf6da6bafbd4ff27fbb7f6d19f957df7499417f0a3fc6bfd45f4cbfcbdf78ede79e8c47187b7fdad8f8f48fcc62d4a3f4d03eb01989d401e5af378cf5e6f0b0b1de1a0e7fd5f07a7b58ee89e1611d4a0e47417b07fdd1dd503b28e18e1d94cd09df99d1ef0e1d69ac4f203dc7ab1d5419d2731154db0e4ea828460d7389c84b2fa858868653a574d514918c42ea727579081ec0e652e5c1a44ce64872a195ed1972da2fedcd170c03e18a6cef40676f5bfbc04096a214a2129bb39b07b205c3c243921e36f70e65e509af36f46d9387545fbeb3a730d0d62dd9ca2fed1e68bbacaf6d736f876147111b1bdb36b7f79546b47774180e225c44f4b57752391b8d04c7745cda7659674f475b67739391448cb761309fcff614daf2d90dd9cea16cde4821ba52206e1bd8d4deb8a4d930397331ae416262a99634344a9ca7e3f29d7dd9cd1d0dcdf5129d889754d64614d9d45de85c6c94e1d92e169988975536a61097802462e5dbbab2db8c72c4f844c9b64bbb7b37746dd8d4ded943d8e6db37670bd93c10dfd095ed10e2f91b274be622d91c94d697efedeb1dc87620d031b881d21815dc9421a50a9d9bb342a55928b67d4381e8164bcd45cd08c91083b8928ba1261828f4e6b3dc06d3382a9fed1feca498f6c1c226a30a51d30009b5547b77e7fb50a68a22892b863abbb31b09660f91331199cf0ef40ee63764dbba3b377716068ce97873c2c4d8b465b71a3338675f3ecbc0b717b26d9765db0b83f9ac31931b41584f1e66750e846fdbc2e41dc62c696b7aeeed69eb682fb4b70d10a4c66c8ea6723bda62ef8424d3e37da16d436f47d698c31594c46fce0e0cb46fcc4a839713b14063908a3b4525450cf6750062c4cd2d76944d8496b2deb7cda89e35cb9d69cc9a497f96e5ba33ddf213e629cb55b665a99955335dd3ac99651a338bffe6cf9c699ee89aee494a99f48ffe66aa93e5cf55faa64e76e91f85e8cf70e959e1a23fd35554a24937db517d6ae64cc752864accb1865530326ab8c34670d781e8962c534e7b5f5ff736635159ad3202e5ceb8f0e2bf7cd7bbdf7bbdba417d54dda43ea66e56b7a85bd56dea53ea0e75a72a7fe271739b67788dde05de45de3bbd4bbcbff2dee3fed2327cc35bed2df40d5fbdc35ebe6264e497fbefdd79eb4bb70dfb263d23dacb3b14fde08b1fbae9f67b2f2ec60e84b1f77ff719a3185d40f4c8a1cffd74f4f90fc6ca18d4a9bff0a59f5f548c1da2d8573fb3ffb9bd773eff44ac8c2d5cc6c8feafdef5fd15c5d8ad123bf29fb7c463df2fb17ffbab1ffecb3db1fab64bf4fffccf17e3892f47ec17fee39e9b6f8bc7eee0b4a31ffd5549da6167c5e1fb7ef4c16ffdf383f1d8ab1427fed8b77eb23f1e7db5448ffce081db5ffc520c8cbf91f87d9f7bf8d15b1b8bd1bb287af7bddffffe3f5c776b49f20f28c73704cbd12fddf2827181bd3ce0e7ef3ffff04faffc99e19de28efb7362e9c3362ca107957045690981e12d751f51ca1af68db43297e39f6f0456ff599506428b0ca392f8c237baaa5c8a57b9b4b296fb8a13285fd1ebb4498f2a97b1d2ca37cfaa342b889529667b98d2ce65549d6964cc6078c0378323bff9f5965c7f3072c5953bb7a212976aa158d54fd528ae06b91619aaa4dc4aa3c2a462ad603897b6e84e09dd3c010d102b8dc0a8224402a3cebc0ec07b4319ca6736d393c4ee5c9ea11cb9a02a17f4f5678c668ae05abd826fac41760aab1cd273956905a0d47223185e85b7c1ee51c35354ab7bad923a09d0ee8c1a53b142c526015c52316115566ceb8a2d546c4bc556583120489b4c8db1151791f4914579e63c9fe866b8cd4a0dbf81f0edca507cb359412fa9d20aa27611d591e58845cd8171a681aadcc5ca1c465bfa460d9a1cf4f64ecf18952a30d20cf6489031d1bce674c3e0e634dc048443835b66206333d5cd210321fa7f27e812ec7cc2f09698cb991c41c622906f7cc2485b01dfa9e6c00577e1f5ee206307fe2ab058b09330f7ea39f970daa16626286ca20b9742e58348c81c7075bb03bc7728d266caed0e8819ecc02c10dd839d2ae70338ca6de77c068e816af61df71c4048d080c0ccdb967077c6662ece38abc0cfdcfac2e7608a616223cd7ab6ef7431ebb9bf2401cd8c171c79e27ba7af11bef5de82d6a1b89127bf777a0f313beedd5da84f927ef4272f5992d60cea73ad06d591362a941b9869895485e08eaf10939a9e3dcf4591b90c10d9fd6da327a3822347acfee0650325c71352c9668eb3bf6c7477e1d56f9130fe9e28611162544e7757bc206b6c410914641dbda054cefd8125a8abe023df46cfe1e03f46a1b2352c2b9420c86464aaa8f13057414600960c40efe17651c13f25a5042b5602e3d637b6044b4ab626a486752c6a70cda0aa6e1b157c79e75b430c3ef5a1440904d4482658d2246934b6b60a1113c19107fe8f1d668a35530c03eba8ed135854be121a8dee7c6bcf04e858e3d00f6c121494c5048728e19149da7672b2b88ee11986fba80c052b8ca5a6c16d4e98a023992c94099b7e69e20526a7808c4ee548e0a0d7918042b23ad3a64c7d6bc1ec75664ac8ccf2d63da712bdca1369e82d4657f3494edb3e77e73c17821e9c129a80d74322cae8a2d033889cba8f98b9c0e004552e80ff9a527609f011e80493eee6811af2e9a73f93a8c938be0988e8d645618b02d6393544bc22ec490dbb25b05b1af614c1eef82c98007b2a82dd8960b7e2b053c10cbbe32759c6f8891a1f4dcb68d0cb52345e955126e8f1551743283244c87f56250bf9545aea41e92937e84d97312ffef8c89123e843819b2ee36e9729471b512bd2ad9c902b6d847499eb97112e2665b651b2e9a77c1b5051444222487e24fd848e231a75d1586a776592d15b921b29dc1d4aa893a9a07c25bd4e794daeb4700a14a194180341b1324d3114c0b80be6cf6acc7b8f897932c23c09cc5363314f09e66545cccbc6639e72fd5429e6c9087360091cad388e498da3558a63b20447cd0982cd015325455a82a1225420c24364f0b0a39fb51a3cda691bb85182a1b41d94d378433f0e402466a76b4d0df5724643091ac8e9001a62a295ba0e272887f6a050248894d054a3d18f7986b83f47982724172417b5346b1e63a143262a862e64c44d931d03a9ab3b7594da4e2b01feb7aa1f0868f1c84da0007382686886243e4bd38ec2292ec04f0809358700ca9098221ed09d5ca1ecd3c22721c816fab31d925413d411982cc064011e0bb414a8096fe12f96291c6547383b117b39d2392168202649eee4c030635892255889c042b6986802369c196c6541b501677a6742affa368b2ce59d162bf8ff657cf694b60cd803aa9db0b449104bc945b46c41cb7445d282a57c7b15c3416d4e4c6f43f1f362202a1a907dab32029006b748d01a11dc396623e06f84632510c088a380d2c74c227c11501b803a1127339436774357ba1f1197a0e74e33a66112d206099218347e609408dbc089da2011b5414297a7256802228a1b85c606cf9a47d853d84469745b43c304687f5e0d467ca4c57b24230d996837beedeca8edc21ec345c5db2ec199fd4417c080764ddc80b693ae358d8760aac6f44ef3ce729f5425642ae14c5b588b99510b21c5325b6bd8255c3886020e28408d8bd626ce25f1dc25e53241148fed248f0953d40bc51ba3b4e656eb58ac9a08593542e9ebc041e64966ad28db4ece6b65f9a87504ad34b0ecc75b02a628b44912af091932a68f088b8842e06865a628bc62ca8c4c713174ace49ee0109f5a3c8924d4509e9e7688bcce18c4fd3c85e67718af6a3522a2a295634a642cb50ebddd3d5f2680070dd2fe1833151c307290a38a4708401386649a72ee3c7ed41a113171973787620e1a54897b574acf6cea2089598feb0a92ab792a7af8ab34150dfbaae87dac3a7214eb895c19855897e72228b735716e9aa0c572db51ee8a29e5f6a6947bd69472574f29f7fc29e5f6a794bb764ab9eba6947be19472d74f2977d39472b74c29f71953ca3d6a94665747cb6ec6b29b51f62729bb9a3cbb1a939d459bfb2b65a9617307cb9d7a529145a0f2ccc8f35618a4b67a6918924c0a3b161bbb72d033127ab22d33ae15a96b48ee271a0c6367c65ed1b26b27a9370a83298df8f48ec75fd11e57326cf78a058e722eb5584994bc4b2d03d5c1e6473215b5a35656a031718585498af156ea093e576fc19a03236621d89af3a6138ca9dc02cbc0340d379b87cfa596a1eb73d91a507b6cdc67fd89e13eebf78cfba7a3415478e731aa63ee100cb7c115c330becd59ad874bd207cf26c0693e46bae1100df2260dd626469fbf308c91f7dfd0625640a983796d35a6c1c1c8c8c876df1aec1753482c5d8a074ee37c2a0d86ecad9c00c1d72c2828a8e248b29f7523f371d6f3dc17a7a984d65782e715e683f5d4240f7d8d1ac55ace4cad860858d80bd1a3a07ed461a03e2763d7c0e20c13f3002c36760d0cbc5430bde67ab9c8ed6bc51a1e3ca77a320efd7667125d69a9e4f053bf7b25c8dc159883ac1818941311b0abd0cc93d4e659e8ee5cb4055cf0224300b05a2a2a94ea5e59a9d8ee49153fa6d0f8843f9be0a9c21e9ab3eba4964e0a28cb29314ab1c252ace8550581de934940865022794f8a63f4de8bdedb13be9f3149fe3993e49f1fbd4f4cf8fea449caaf9d247fdd24f59f3a49f9a74d92bf6992fa5b27797fc624f5930c3f76014f199340f8b4314915df9eac8ae726abe2c064251c2c26484d98e0e5c9aaf8f16458fccb6425bc32590987272be1d5c9b0f8ed6474b8524d52c52e354909d7aa49b0f8f06455dc3859151f9bac8a8f4f56c25d9325f8cc6440ee9d0c8607262be17393c1f0c864557c69b2121e9f0c86d1c9aaf8ea64253c355909fb8a0948cab3ec270580ade89c8ee798814c6561220dea5766cc9a341bc37332b2d1949dd260c6e99beedf25695ca5e1df67b527b2d5b0b1dfaed46b81293d8d96d1124600aabe5acfb02d7a65793ecfab4d9e7aead9bc2d16cdd2a9b485a9f42c5862792aed61dc6a36ab59a5125b82a5a7e39e58e5551eb9f82daf0cb0311756536dfbb0ea287702d8267d6b91e16578110003305ecdf27961801eedd024066b84a5573dca571617076dac0adab22ac84bc1e6ca4a99c243d559894cc82d8b0d6c2256be539ce543b3b639319a804181d6a6b4058cd726987004af862d27e6a804bd13d36a0e25d97e32cdd617e898b09753ed5dde2a98f5182146129a082aeecfa89a66d2bb2ca143d2c7630a8f0bac94687ba4b679efe05c6265e3945e9358780ca6bdb420d7481382a1c0eaf6026949588465b95fd1bbb595989d24d9fa4e11a0e04334dc0849460d59db7e215cdb36e3ef1363de4b6d09d288087b9baaeccf07d585a07ad07bbbe8b45c1ddb9c288c1252c105392aa68a0ddbd33c71228cfe1457999abcbea1a0aa1f5516a2faa034c76ab3343e170049aecd9ca8362a2b51e06506e7a844904ad9c6aaeb72b5e3846f9f53c9f64b6a272f5cd512860f97dd90db6713914c44ce30e092f0d0335430faeec3a141363016190756f3025ec0ddf4d9d5b2a4be7f75f01f04c66919035db3e803c2bdd2d0bd5261ad443c09f6ad0e9899a95ba208d8e0f6af8662ced2847a24c57eeb19f88518bce865a278dfe0b52e136e18062fac52e523546dcbbc803d0a0eac76abdce059029b1038c110789f5d2dbd01ce00fb577b4d6e293a87e2e8bca4d179797570fbd75e073a078be8bca4d179b9041d8afd871274a8f871e89863d039c4e8bc548ace4b31745e1e8fceab71745ed1e8fc6275f0da33af039dc345745ed1e8fca2041d8afd69093a54fc3874ac31e8bccae8bc528ace2b31747e311e9d47d6c4d0d9bb46d079684df0856fbe0e741e5813a1832280ce436be2e850ecbddf8ca343c58f43c71e83ce236b80cede6f96a0b3774d119d87d6009d8f001d6d5f356a75b2c36bf42278f08b6fe2956763ce0f1c5fd638be823bbd580c83cbcb1aea437c87c68b045afbc5623ed7476ff90ddd9d31b01e5e03bbb918684f526a583c685e5e93510c2b86c5436b7c057887d87c0b200f9eab691f8cda04077af9b942d0037ca768aaeda573b5d86627a5c8144da3c6a88c4e0400c8c786ec1ce74d8c81eee0b96e0caafde7c6a03a70ae40f58f73d5e9a0e288d59549d48a29021602ba9d4ca37170e44c084f2738023b949f0c9ca1748aa4e6ab660ee30d09f1e0703178a8183c580c1e2806f74541dc9e36e53e0aad8746e12f135ba5cb281921fb38e208bfffc5f7203594296f351ea38760179571ffc31f79edfabb1f3efcf7badc118a3b72cf17777dedfa1f5c79fbb0c4bdaa722bfeedc78ffee3dd7b7eb3e7d312b7e29f9eddb3efc57d3f79f8517a3eaca4f6430a3512b188ba69f82f252dac17257995ea205ede41c36632e79711b5b1945447b144191e25e4add7866c61319439b1808a5d31626f47f118d35c4abe92938fd2a0e862a0214c5da80a2e759870d1f528359bb214f13b55ae7dd7d83b0f8b7dae3b6115e5bf73f91395a67e9fa505c7c2fd6825a116375ce5b3d3aeefa4cb8e425d772ad49526f4cb40ddb2a352d789957f7cf0bb3ac17abca8330f03aceaa15663b72911bf501997236ed411afaa4c3947dca2235e53990a8ef8b88e183133951c71878ed86966a671c43d3a629749da52f550a68a24b78ebacecc589ce60144900e43c107113439782f82d3e05be9b51a9fc643251ea6b71a77e2a1020f335a8d4fe0a11c0f335b8d5bf1e0e26156ab7193c904a307ea5ed74b1d292af86fa50e04ef933a4804cc6e353e2375d0c39c56e32ea9831ee6b61a9f943ae8a1bad5b84deaa087135a8d8f491df430afd5b881eb68353ecb15b41af773e9adc61e2eabd5f81417d36adcce25b41a3773e656e3a35c71ab713764d0611abb7c2d94e5a9ba280fe9c98b8bb9ad72eb13570a494013222a671189bc740d31d87cba4ea4eb24ba4ea6cba72b4d5786ae37d0554bd71be97a135da7d05547d702ba4ea5ebcd742da46b115da7d1753a5df57435d0d548d762ba9ae029c2ecdfe027075a8d010a35faf32894a7d062bf8c42fd146a024cbbcd0134461f3d2ff25d7ab38542a7f92750688842a7fbb3283448a17aa4bed1a4a7023d2df0cb29f47e0a9dea5753e87d147ab33f9342db28b410696f415a2287ff46bf8242c3147a933f97423b28748a3f83429753a80e693f8eb4dbe929ed5752e82af4908c3f87825722f8067f3a05af4070be5f45c15d089ee84fa3e0df2078923f9b82d72078b2ef51f06a046b51f41d287a048f3e1eefc1e34eee818b8c0f28fcde8ba80fe8a86b39ea3e445daba33ec851f7230a41f0d36efdea3a7ef5005e5da7a33ec4510f22ea433aeac31cf5b788fab08efa08477d165108968c719be8393ebe91542819db2e1933b6ad13963bbb28bb480348bb3101c8a1f898b6bc28f7e2326fb908a6b3452e9d2d6399cf4be5130f634711b8babe12597b9c551e43c6ea526b8a45160b3c4a618950a4aed312b54f1e2fd2f2b4208f9764caf871ab3cbe470bd7edf2b85e8b5666227aeed09275a77edea405eb2efddc9da9e267e6882a969ccc09d338c88c56c94166d20a0e326f9773109da28c43dbb4e8940e98e4503f970949f961a525a5e6d44a0e5ea3b494d45da79c833bb84c84dea745a574ef24872016aa8411211c99c92b75cf2a979e5b269ddd15f99014a152c13dd50a9c422609057f7d4e049eaffc641711fce4413fd94fa2533c9c52b22ca2e06047bcbf4ba5e7e3bed74c57e1fe372a7d22ee7bccf434dcaf51e99370ff8c999e8dfbd52a7d32ee9f36d31eee3b559a3bf4552a9dc6fd6e335d89fb952a9dc1fd2e333d07f72b54fa0db8df69a6a7e33ea2d22c1786d36fc4ed5366ba02f71de937e1f649333d17f7cbd3a7e0f609333d03f7ed691653ef4f2fc0ed76335d8efbfbd2a7e2769b99aec67d5bfacdb8dd6aa667e2be35cd62704b7a116e379bc4d4741f4a9f86dbc7ccf409b80fa64fc7ed26333d0bf7429ac5ec40ba01b78f9ae8258b8c7cba11b71bccf43cdcfbd38b71bbde84fabcc8e84b37f1ce00528c2dcc4cf6daaa9a957c935d51b0080a274176732175947da3616fcbd5b1175175018370211819792dd99fe7013918f9add50f7be1509e7d67b0729686370ed6bda5cf4175d6a6435b974c658d52db63c3402d0c22308eb4f0fa981d546e81ebced000fd566dc17a1b15853756306d0b3d58f4c60a4ea0376c9e4cdb515d4ad765d304e825596d73e04689b5023c24d911f2903c9451b31858a8c0834bedca93353c54124f195804c14315319011ec9787e9d4c006e4c3ccf42c42c4cbccf66764e6e4fd5919b3df9fedcf219298c1495be867e6166c9a2907f87670689fac09c2aac9b89667e6fad332584decf72b32d5bed59f079af380deec2d402f899ca95c665a8eda7a1a5613a7f9b30997b9844235416e11c0730834b01cbb18d8d48432f7f393fe5c224d37dc75fd6a0a6da290eb5b14eaa050a53f8d42ebd919703685de43a12a7f0e852ea1d0741f0e03175168a63f8b42eb6886455328e29443caa459ea0ef12b58ce3b758211adcdab0733d60ab57d45cb8a514c0f5bcea9c9d87f25de83ec20b562f86adfde2e6c1594b84ead18de4e598d0b20bbc5b5818aa602305127418d99700bef9ba117695e4d374864f33af88394f30ce34cf028896fdc56a4769d61bc8d431e85decaa16a0a9dc1219f426fe1501d855a39544fa1651c6aa1500b2a75bf08772aea137724c457c00aeeb0734405761e64af0a763217a3297ba71b629316ef2c432cd2de5f0767b2697b5489df982ec867c36ad1f3917d0808e761b12b0707ad1cf504c947810516ab8b125f1fc6d797c6d785f175a5f17e18ef97c65787f1d5a5f15e18ef95c6a7c2f854140fcbe6a85a6a1db4749a51241e01905e625e70c4783db89ff9df0677744410403f1f3474e651e52511f92327c7ae26200af6b631ff4887180eb730113b89d88c5364226a4c448989a830110526c27e22cc4bb1368ed6e014fc54e2bf136a1335a334a13427077c0c3a5e0ab1772420246f762c35ac76b0c7cd6123141f7e7c290deb31c3bc1e035146e31dbc832cf6dd91f52bdfe6ed9fb7bdf2c8bf36f8ca2b8703f4d995e2e36a781b322480ead9251526bd17641d0296ca7abc67d2791db292677bee3b38d9339c8cd0824d5156830cb8523832173508c24c220c57e732491dc646997a18118572d43270abc9a470b3fd5497771e3cca29be48f3843c47f44e82b62dbab40556bd60ce69ea786c92078c485c8121fea93a492a8f41cbc2c8cc56662f3b4fca49c569a409a94bf5e6679c66b3894989e5978bb02ab7620791556da79fcbdf594330f05e54ae346a05d08f322e1467a83a737ec6c4cd5fcb331a078b99a62fad157056d39756252ec0303b5f96dc98050f1ac2c4f4b330e75de6a2a0f56923cc69b017b4eca2e005432774bce67a6968c64cc561b778762cda9443b3b3b93cdc1bc5cbb42676b138beee525412bca9188df95c3e5578982dd03b135659e80207b3b80915ad566f6e010418b7e68bcab4526fb37482ada2b64017b7e991a00060e1beca956cfedd8434bcb45b5c5d66d7ff051927181617c587c17fb287e50afb1cde51e59dce9b5478670b515736bed23c8075d0149682939468c13c61da8430ade6d104084fba6fccb4e247a13a09f1c21d6f2c32825bbfc7bd84673060ae24bc9d6d53300c4e2e4904fe674bb397839a255a6ae053b615344d0e3c38dcc171176a323cf72ad85f9053e5d2957a29983df62ae030e8fa95ec30e8b2c36005bde32d212e1c065de9a54ee830f81f0281e95780c9cc30af380c3aec51ce2e8a2eaa75f56a74e42ce9840e83ba7ac8372a0255167d06b95b1218e841691775b9a09a54e9fae578e123a69c45e402ab4e1eaa45d616bb79b988553fd6d313583c68c276b166d3c70dfd8a8a5f2e3e00f3435ea3eec91287d8662defe253e85a0e20d15dcba7a784de9857ec5a655c9f26b645cc4afd2adc8594f41a98ff7d6e1e2902f18ef4015e85f113ee2da665927856a14704362cd7ea8d8624e7a4197ef3822c70938c806810097aff8bdc364a5c2e6d9183fc931061988030847f1ab54b4425b878b38344f44c84b4f5d2bd4288d99a3de04dd95d11d5f8c317f512f3ba1c0f23368f8058fdb721960dde1dccec6c735bc3cd97e08284246c9a568003eabc53b954438b4183c4a00331e84462d0e64568f739abe822ebcb7e0b93c14523929a3d576f196559c99a345c396dd6a44d0e419366ff0dd6a4915d34ee9908ed215900e3c11ceeeb304dcce6102c1fb3908912b41a498e43ba4494ce89d2c1554649fd2dd7a0ea6be4d88491d123c6553b29a25a471c3cf32af6c8e5a7c3275fb5132f53bbf0f86a155e5523fc5a9233d551f8b7d6550cf6ce9d3ba5e75135de1fb89a6a261088369d4320e40c0e817c5ec96e0ed5159c2c82f4d1fd91af37bbdc9a2c99425f6f9f99c0e416f50df7364b39c3e14a6d9d6cab22ea7505d59be145036f8bfb5fb8ef05f4c2e03e0464dbac542cc9ec60ff7d2f9cca2e19ecb693496253294def920087dd47d8c7251954777b8be185ccb37caccbf1d88851a3ba3b93ca894b8c819e4e5033df63b34b4212217b8edda9abf5be473b23d3ad4c52069f548e061a718bc53004bfdc343c890cd920c31d02fc49affb68661af53738d914773a2b2ec4b7d3bc6b94074dae529334c9551246d8a6ed9d436f93e258edc816c784ec125232005309ec3544d0134449e4a4412bdaba0cf1837554a0b74a4f6f79a7948dc5d52f56276c9244152c8dcccb6181a9906e0799e39dc8a368058fc7b2a539637505f59b832b46e059429cf0f2b3e004d9a88d5b459a4701eaf5e2a172d3b7c6bf87bfb2a975519317384ddda748f78533d94113aa3174019fabf33651e43e8e8ce5da670a53058f7c2bf2083026f608805398258ad27e937b323c02f621484d76c094d15f3c0210cb95a93cb678b15b80853ab45b8015ba0550b2a74bd018258082c75e0f305f2d0233aa8179ba0418c46241b61498c72604861ddb9bcd2f606c4e08440f9972df6beac6e3260033435b1c4de678669c42b81cd30ba81c66b097294d6a4754359aec55095614a953598c9d863ad973cd2a6e27b4200cae308551befa2dd12a9083d5d34c153c1aeab8411611d5336cb040de1abfca7bdb3cd9cc5967bec6ef5f95f7198f53649c9accf48933678c1a28adf4ee80e957e28c125646286a1af5ece96755c2c7de5984b93ffdacaa34431b80233bed3d98023cd22e2dd24ba6f3312675e688e54ff39d1a18a6bc65680442e8d56f151dd05e0df731735af4b610cfda5c6686e0f9550675b404cf19c78be7f8cc453c9f868a16c3b3624a78568cc5f3e3df3e069e68700c523349271ebefa126ee9fdc8c0d1e999e29458151aa7c4a593f5fbb4cc48110a77180f515c95de684c7d4bc1a7cdf03a79afc6d3584c194d6aba0aefb28b26414250ddf4fcd77fe874531dd5449d01fd18ec7ff693bf4a0c506c652c961eabf098f6c20dc375e6036671eae374066a4da68a20f5584dee4f4f077c5e7a3a22ce91f8bef474dedb411d6b3a366ab08094338faa783a42ea354cb3867e61eab12f53e5433df6abfc59fdfef45595962b2e798c203c7a96498bc4b2aad2aca841c0a80218552edda920735c41fa2ca5e010cfb64bdef1991db26fd5f778ac3e95c4467a36307a04c6d287d3738a3b9067fbcec337fa73de1ba8814ad9ff0f674aeb1c6a1da64f60accae7d3559c618e3ffb266f6d6636b6e0ac7daf8fbb61ace7769f7d2f139286eae9381005c7124c27d8097a1ac7ab78f2c1b855a14ca0277c51e5ad2b6e5397a6aa3a8f6dba7bb183dccbf17cceaf820e5f4550a567858dfa88f9b020763c1071a6592803cd4ff7e998bea88054670a0ee7367615840fa6133cd4f6d3813d83aeeb8ee0718b2cc5f4c9543136be278c446f897f90c3d38c25dc5464c690a73255e0004f362ad7990f9972e205bce04c2aafbcd9d4674078d81fe54173f0349d8af57a7e557fdad3f544cceb85cceba122796fa008f108f5e4f023743ccd10cd74635205af697140f5cc28edd67551b75e38ae5bcf88ba754a7a75971e730c58aecc79ba47c3cb4b3fe11dfba9690737dfe8e2ee8e745c0c32524459095bc343aed97c2c5994195fe0f002eb0b499e61c26bcf4f79edba0ab19d51cd235660eae16cd4f6cae6b18f2e9ec42d8d8138702e8058c61e6ac1f5df615b16fbe919636060d1efbd751e0fe3d8a4428adb2d96586f6ed443ff6edcd92dd7da65854dff8a99e1b63e6cca463182fb5553fc76e78b992cf4e115e04849f2c2d373c4c99afbc88d96f46d0aeeb6108b792e9e6ee1175ed8f7a9f85d16667eac24bc0a02180261a021c49d2167e078e2f70a2b28b640e0754b252cfc04b11badd0a434e1c04823c5f6b458e7481d8126c20b81ac6ac09d52d40b0e2556a85db2dd1fbaa19f782713fe81ef84e311b185a617d1cd0a5de7b5fd882d28d8a04e0d2013ea4fedd7464ab639ea6df2e178422aa7b7590613299286636aec93a41e444608e28d4610bd92df68799d66dc7d0bbd408a472fe3b3c028e35e1e056236371ed28b047ada8ca2f7c5a20f98516d8762e354c82e25363ac6bf4f9629b9a2a090d3d3743e6826b8446ffda540ecdc87603db2b09182f77058abf4dec54326ebeb60dc57046d289a1598aa7fd5526e7cb7bf81ddfe759b7d55547c55f04fcfdeba786d78dc032c0575dd70cfc5d6c31c1b302c9f73d11cb08ea65e4919e974796cf0537eaa2b5da6178393be85f5be4b7299723f85b042d8d5a749b1b1c82d4eb34c423e532e9d86e76315c2f8e50447214c5481f37528599467538ee7677aea66065bf1bc35c7561699bcadd74abf2b4547357024cc43049350d0f6192f1cb661eae9b2e6139adbf1d6982456a4713e4534bb4bcaecced4f3373f626ca51b962daec0c09711018d023ab0e9469534ac2a36acc9bb624ce17ae60d7084fbdd32d31e3677bc017258efcd81a8dbb79ae6dcec41cf475e651272e815a9865a9d3ff41c75a6588ab4a4c0d17ac82de79e2871bc873b3fb44a1b4eb435a100ddf57cb86620596285715134b276c91e1ee53b39d9c323f212e6b987942c9dc8ae1376e0a0e9c6230af38f8754bc14e59d8de513a5575be0db66044f50e52b610c2ab6ab6e3fcb77c04ed608d52a0d6b05bbc3cc28e53b94f52f8e9975772c2b497f0c0b00e18eb094dd8a81dda45fab3c805e2f3b8acc1abcea90fd44668db7e29835dd11aba945978e6afe7552185b8a19f71950a5cf0eb785705100e212fd4ee05b1787efa2e384cfd3b5302fbc2c7258b610e94d57b1bd54aa26b6934ad57875aecc26bdd88e263de320688a919744912dc5c8e551e41daa18bb5745d1eb8bb19ba2c8ddb1b4b714d38ec4a27715a3fb8ab15b3952a289fb62e9bfa0703c0f38531ff9c2ac4b98671cdee71d9e161a6c876dcae992decdfbe83c6b9ededac3db01ce17feafce24b507018d9eda830021f12040483c082c21b1584e539c650d1c9a38cb6a0e21cb391c4296b39199ed9f67f2be39d83fdfc621d83fdfca21d83fcfd0fd90e068b90620941a26932586c964a9613219334c266386c96464984c8686c9e522fcc0d67628327efdbc1efa99cc71228b7d7d546160bacfe173d3c23148e42060b0d7f2363cb380fd21d1781d4ada48268a69cdc690837d757a68e753f6c2c341d9b1484ef7e4d3a8526836390f08aff80828a9bd13cc8df984cdebcffdfa682839d18c6e7d6cd9ab638725331cab4d3d7ef3f6c535d8eec8074559cbc3b53992a4248ce5a02b4b1f2f10da4c31fb340b99a49c9bcb48a599050d6cae30e5097d36c5964a997472b41c9f608a3b138df96c8184158197e37401341e17f39d254a0ea7403647004862d009f5d228827173357234cdb55d376a84d05c196b0444d97a98b442bc337246145628fa8ad3c2123ac468902024e901470996a2857ec83350264b2a2212c892f453115938ba840229502096e42814488ca54062020ab8471d7e0feae1f7a54987df9726187e0fc687df9779fbd964c3efc1d5b1c2fe9b0dbfa3eacfc32f6f0afff3f0fbbb0ebfc4427f1e7e8f73f8fdb16d41a425c62ff9c94ccf127f013d330c7e4c7788e2d8815294c80c3ef7e3674f90711347f3c196e6dbb0b6f1829f13c8a13a44933a2cf899ae3ef36501affad9d2b770ca304d29957e825b8a2d2b82bcea8999190d82c9a57acd2e83c1205d260bf71832148b3b0fc7d3220b64be5fc6afa82e3f2107183b3c71a5e9288e327365fe5a51327fade84a570672b64f4a4e47b8046b5b1508f3fcd5d2f3573bf4070bc712cc5f352e0e26a965e16a666cfe5a26f3573bca533a7f75a2f9ab336efe6a49d1510d1c89451d82499c78016f3a553273358bab924ace912e9779abab0f330ee7ade6d879ab13cd5b1d86349ab73ac79ab73a7ce684132e5d46ee46eecba6e5843e6efa68c6925324025e7164179b680dd39165b00478ec41de46a97dfb932b92bbe06e76013bb1f09e0c475655136c9078708501b775839a806465790db1151a8a37ace8cd1ff12290ab3a435d1b27150ff9e5355d7e42bb359575114189c9baa4fe1434bb54696617260f8b7d8830f26b6f72624b626d57b6244812ded9efbd2fb4ae70fb24a08df00ab129cbdabcaa9862b7bd44741ce3a3fbb52b92f6ddc09631f1ddc0c917d7aa79fae048f779d3b2435715a17210296c21a5c361474ef3e0a577521e0267f041e8acd1f609a098d0283250a199c80957b04ded418261114739b2db0dafbe1fb32412dec8930c865756ea5554e9991539ef436a9e9cc185c2fc9c569ab5e2a9cfc44dca570320547036b4f761a599c082f3575726799e1ccfee27bb84d8dab64d116bf56801af765b1bca6cb9b1b5908a079d70beed478a44fdac652ad1fafcf862fbefd9d5c5885c5d8cc8d5c588bbba18e2ea6244ae2e46e4ea62fcffe7ea42522513ecdd67786fa0c6ea0bbec2217162713f5eea962487a51abaad4cb495e2c91f379d6e2ba5abe003db745b295d191ffca7db8a427b683c94b6c2f930d25608495b2934a68db6429cb45598ce89d2d9a1ffa905225a6389689510d12a25a21523a21523a21511d12ab61557e3fd81aba96602495b21246d8590b4950a7cefcdecd746adb3e7cf4e637f6a4e63a657cbe3ccd7f669af0f6ea82fc64e522e3afed26ce18d7c0814fba5a77893160fae8eb66b1087b3bb118d4ca7a7b57b8c1d7aea5221fcecbd699e2e4067a624c935e2c2a07d6ae1bb2a5edd2bb46d3f2546733bd8da1fa456c5fd6fad28ad88ecb7f3511e005bc08d4ea83e3ed05c3878feb990090af986293c515fc213c2118111d9b65e47a130f5d4ca0dfebff800031660ebf4be072e2bee918d294c9d98c86aa1d1b2ef7cd8facc243888a5561c10561d2bdf5ce453613e758c7c3ac39c311918f080dd13235ef6654ee06d9b27f6aa94cc5119bf0a59fdd167ac1e517ad7873e857785288db625c7e2637a25d6a3af7cafe4a85eec9176f4b1f719561da12c1d31fa8950d87569161edcc916cf2474e9046c68c108ce87718b87b839a2f6995d381f8d862bfe040a7c0ebb42fd34c109d38e3ebb8ddd357dfb3cf9a6916e23db0da781892efd5d95b531c24939ace0d2f421723865fb140e17bec52cf1344d89f3a69db1bad86ac81aa46cfb124755c55fdbb2f5f6aff02361a17f2a708273eaf0b9954ace8ad59ffc31f1710f33f82d8ea8159f549ea25ad0b0f10118665c2b9ca25a323fc5c1fc691e1a68b2c68b991a16add48630c0385c9c322baaa88bf7085015678bc958bebec06a28bb6de80f5fe88fc9d8ac89875e9f557aaef639d3b48835e0dfa97eaf1ded22b9add31d0dbb379973cf9eb09b5d246cffb431a6837139b2199e73b788ed8bed0fc4f8626ae4c9312c62295906957942bd5e0665abc8ba8c23db98d4520b7d5ff639e124e23abd2506bb36b9d44ba537b5c47ad319616f1a6143c6b953146fb0987c8711a7c8ef28d39606f8af1efc62a3de98212f4816c51136662460e849621b33cd4de0786dacf24e0e07c4fb4c42c80ee7304743a85a2354fdbb2254ad334f8690a387f1cc78ac70365e98d20b537a6312a56289aa43fa541f4525c0ee8b84d028298e8e29b60da6cb98546534efc7378f7c7db49e7b175c254ac6388865a654d1ed5053053b92ec31bb91ec5a160b8eee76fa684b5b8cf222feaaa5ffa442dd86d1b6bc119a7402241946bc9e15dc0d7d6c52a34188e49897d6c38a2fdfcfe0626bd9393c5c1e49c43685d924de137c7c050309970ca2f8fa1c5cf6417f5970c1441ce6acee5ca60c2594a364fd252218ab5c16b07eb9ccfaede0eca8fdc4949da990122cd9b77276ceafc099d64f7f8ff7af4f57ec2534cc4624e2ce723191f8653c3187f9a43cb22fa5249900cd5b73f88c724b2c24766cf07ccee23d39657173129b55f860763e905db63e4e348a5a7c12279842e4b90c2bf0fec028eaf028ea6014c54684247fd08b0d3c6347d164cc789249606661c9400adb8d778592213429c7795ae7c93e2b3ecc822d997a1f40c60a5bc40245cba216b1d022e57cd0005ac4e5efd158ec19c34b52bebb528f9267e7f43b6d38cc544a09a648616a91cab12de208a9138498abc95dae0d5215300ee2c893263e23455a24a5bf53028ba1eda7e4302c2570684cccc014738a5134644546a20a01c6f4ae54cc28c816fa7479b2f3e45309ab9cdad42ab6695135900f66611b8a33186a07d83295d11dc8d6b7e8a4245867c3bea5fde7f46e17f92a595c8988569ff5301e36288e8fc552f4286f7621fde14cbd4e2c1f46a344d02a4e96cf09c2c4680ff2576e9238c98087695e968609d60ecbb11076b0a66dc9a9b6c40c9047c45e608b323eeb22c2a63a7774bc804bda8e69224ca172412101db5ac22fefe213e8b0285a0655864dcb0969d3724480f764739fe2fd28e1390beb73d8dc6768a68ab1149bafa7691b3d18b44ac83b8d39531badab8477a745366c2c95c33cae740c88b5294c9df02b259514c465432e380445460b0b8215e25a345356a62a2b2cad2db16756225de98a3d3bf659b132b16e5305da3a2cd22f114aaf64a9f49b16fa85cbd77145fa5589f43362d24f85f959e03ab1bec6d2cf19dbd72c21b84af33e2046689ad607aba4b3f0924c527749bf523a462520a914273e1ec4bfa9582dd496fda8ff4101b774ef18abc2d9695b46ec707fb47cbf477f2ecbd607ac6454283b9d6806c29b4f4a64a78acb4ef91665283bb137b33803b1213b590700835a6c8016d9c99307678f77bb723f6f852a564cc79dea407b91dcd64503ed25a2bfc676d2873aebb8adbea2f162b8c1926e44313b5278431517d3bb9d6a6235d78ed45c3ba6e626265273134535171aee4619b29767ccd800fe769c32cd1b47f400ce9c5788adbdd8c176bdf6c2af3695bcea0b9765ec605d3868af2b49714971e1c60e176e180c197749a0bf3d1c7745cd66eb1d84f45466ade6f859ab2db356cd33e684b356bd5891c026332c14efc9986bf584d4e9f2eed11c6acb160353c65bccf6eabb7925213aadfb7725a0792c029ad016428f4d252b0b8ef6ece0095011002c89ddad3052a7e089c25b354b38d1d15b354dfe789770a2c3b1baef3aacf7522174afc57d1e7f6838235fd3e36f385f1b3bcef60f3c83d43d83bac53591024b0f6bc23dd447f9d4d89ab1b6025336a71edfa7c626cacd07481dd7a7c65e5f6e6f4ab9674d2977f59472cf9f526e7f4ab96ba794bb6e4ab9174e2977fd9472374d2977cb94729f31a5dce33e3576b4ecc7f9a9b1a3f5f0893e3576e39fca6ac3022bc5128c55aeeb6cc7e6e14e2faa1f3e8a349d101c670c384e2d7f5c95a529effab5c27347022f2d5f168bf4fce8d41f388671b9a5278e38383924a965af231bfab1648eb904f8b74c9f3452ce13c47d72d248f8155f3eeaa31c278d247c974f1a49f04923e5ec278b936471d248427fd963cc4923657e39085916e60dc12f83982c035ea876dc49236678d288197e9a8ca627f8b2c2984f93956b830d2a4a8012529f1b7a688d393948136dccf941963e3fa85a42727e10ab6954405274b497892629ed7f2767fa281c7ccafad9ba6850f3725e6f741eced3163b4bfd697142090b70db3bbaed93f1b64fc5db3e296d9f8ab57df278dadef193687b27cc1b02ecb0a94eb77df2b8da3e3941db27a3b677a4eda5bea9b7bdfe6c5f7fd492579b5a8d8d7d2936fa482abb1b859fe8060df7e0201d6a81e42e3e769066d0c98268aff956a32a76f88531eec00d5b36df51bbab98d053da0a91d09fed8bbc9d6cb8a1855aa2e37d50c1552c852fcad99931fe401c095a2598fce1a92e76514be4733472de750aa2010777c9e4336672f98d62339a398e0cfaf3bf43fcf5dd9a07c30f376321c4166ad8a0867d746a581351c392e1633c35ac526a680822c7e44ca2463ba4454728266a781eafebf29df327ac6b8295a718facfa96370c11f970574f662fdd4dc99f1487159132da74538bda82cabd4054cf8ca925209c216e26e47af48e9aff8a4e11a4da839da4d2b3cae448e43d3a68f3ab33e9378908d4947cf139aff12a17b97c95640368ef0977d8243fb43cb88afbfac0dcf4a3904b605ae9656299356898b977c0ef9c8b15a8c7dd8feabfa6d22eab7bacce4c4653a63e637e137a469c2b45bcd0bddde12701bd41f272fe9b13f1adf63c3dd9a4b4c4fac877f840eea85d6405bec0a6189d6c425aa310aa746dabb31f2b28bda79ae30859c61c3ec9186bfe051e3874c15dac4649a8d25892093cb68d3635d6e8969ac3020506a21cb5886e9c33522ce10d7c4f0109e3ab38e93173fa855c35f3af1f9bba65cab7b9b553cb3713c23b6fc710790901593f121c44f2e31eb65e9fa98d54f3b9eeaa71dbdfab0c727211fecf1dd3ec97ba5825f14bb7d125324a7c41e60471f261edf5fc64c36b8e224444572ec78a659a84389794a9885e71dd1f1d4969ce1174e51c6236cea6fb695622b162bee08419a2bfda35472bffa23d4421c9e2e9df71da5286b4c51fa602b58a03ea8e6b9d42de8ff3cee42d2d9c4397ca13691b99fd5ce43c5cf0afa26af73cd8abbb8578861d5ce17b10b4d46134f67e5808589048ced7d5ccd2b1558ecfdc287fce90dd8297d5c9676203760e0e79dff30f0db3c1087cb71d5397e2dc358dcc06f8be5de942f2a0a4da3e92e6f14976ddff201be70adc393b3c7f4c7dee4bc8e83a14ecf2bd11f90554efbbf749553fb0a2521d5c6af727e425b5d9d92554e4c4c5b78bf8626ab03b25afaf0cc4c820b8cad9be035f5e93164d50b55d8eb970ab5826801d2892d40866de75bde27b9c18b5c9398c80c9100859bb821c75922608c47916334e53b1d5551ba30397e69b265737169b25e96268b1b366c99e199d1b29e192deb99d1b29e29cb7ae166b3f8c265cb040b97b1054b6e1b5eb0fcf40ddfa85d5ba9827be81e5baf440a2bb8fe1bf7cc8013141a4815265cadc486576abce20a2535a45ea144931657287f27a4409930d452b27e196d3f0ad730f9a88c8996f4c4c0afd73615af6db2342bae6d62cd5b2f6652a1e578cd8c573176713c29acc7cbe3fb35ebf11a5c45f17b8bdbc19fdb73a72be32d0a67e22b14aef4b1f9029dded1a181d49b15182c6d6088add4dbe14a3d33af5e7f346599de18db0594e083d29dd0b65f3ca354b833f41a3b28e7945aa1efcad4a44572bcb4b0445a58222d9263a505248338d3c9e6905c7182965c35b60b26f547574ac7a6e4d8b1499fb28aaf1e489bd5e578e1712c99e47448434bd67a3943157eccd169e2ec252aa603233ae3d12b3ea7e49c2279961396a822ef2e5e86a91721702e2f362d307496a355618ca9c21853053fff7e0bdbaad4301767ea95795d9c66da6271e1fbb0b8f0392c2e7c5e88e785b2c42da76552d9b54bcd5a3e7d28747c35dc43b62c338d1ab1419d37dbb3dc95e769a6a1b483821c81cc7d21c576a9480f34574d30b83ba18bdb18fd451b984a7824729b69c9f1068f713cc2a3af7c4622e211d6354ac9ce51a59cc251a5cca2577f35bf9832a2c4d498857f2874a9a80571cd6fa272ec71e588f4c4615f192c3ece7b7d446396e0d3e2431eab2d215f9ccd62e433c793cf1c4fbe22bf857b29c7e842a36cb2bb0307d3c6f68ce89da1fa607ae666f6ba0e8f5b9a851f2f637589ab9c597a1ca9a1bf4dad4db5e5fc495ffd91695ea5ce8b4a137ea83a41e321cfaaf55e3d3e05541ff19ee26317587e22b33eb2517b0a875f90863f8b8c47a6fece347f5894e415518ecf72a0d60cdd6f8c666d020b95687ae73569179948dcc53b6e44b8df4bf9e29c285b71f561569ea8cd8cf0ac70f8f1f5b48bf4db330cf780a3ca862770e70eea37173dbab1a21b336cebde12fbde745da4fe843a8359a208d54fe006ae5b3d7206170701f6077ff1be174e85fbd60b748f7984238519dcfbc27db38fc3273ca8ef8edcc2e14d9096ed43f02b48c90c3b74351b0fbc15294656d1a41f734eab2f752767929595ec482eeb4a97c77724c3058194bd32d9914c61474bed84f68b0d1dabe0ecc4be5cd1c1c697e82ee0842ad8269028257b9033b1ac09df898682beb1990a9294fdbae0c11bed80f6ed28d3769d492b8a58e959f7baf629274bfce427daa75c28d9821cf3e250255e1caae8c5016af12c44957871a8a21787c5fb97430b64b47bd97d6c4ec208768f1aeed065ed9dddd90ebfd0ebb77777f76e682f64fdbef68dd90163b027bbb52fbba1402fb3f97c6fdeefecf12febdc9aed68bb745b213be06fe8ed1928e40737147af346b66743ef604f219ba7c43dbd3d8b2e6d1fc83637f91b36b5e7dba984bcb1eee2156d2bcebdb02dfab878f0a1af18ee274d63fc5f7d7d7d437d63fde2faa6fa25f5cdf54beb5bea9735d437343434362c6e686a58d2d0dcb0b4a1a56159637d63436363e3e2c6a6c6258dcd8d4b1b5b1a972dae5fdcb0b871f1e2c54d8b972c6e5ebc7471cbe2654df54d0d4d8d4d8b9b9a9a963435372d6d6a695ab6a47e49c392c6258b97342d59b2a479c9d2252d4b9635d737373437362f6e6e6a5ed2dcdcbcb4b9a579d9d2faa50d4b1b972e5edab474c9d2e6a54b97b62c5dd652dfd2d0d2d8b2b8a5a965494b73cbd296969665cb08c46554fd322a7a19655b4651213e1ded85762254b67db3df5e286437f71584dc5bf29d4ceb81825fd894f5b33d1dc685e7ac1a43a5fbbf66b89f71a267d0cba20b3149ba5cba2ae99a4ed74cbae6d275225d27d3f546ba489b32de4cd7e9742da1eb2d74bd952e1c43ba8aae73e85a4bd78574bd8bae76bab2746da4ab8baecd74f5d3354cd755747d80ae0fd275135d37d3f509baeea66b0f5d0fd2f5305d9fa7ebcb743d41d7d7e8fa3a5dcfe373f27f4478b74d00f3478e02f7bd63607f7402f8bf4bd7f2e3ff7b1bfd3bb36949f3d29665ad6f39e3ad3ad650a6653b8964aaacdcada89c56e54d9f3173d6ec3973ab4fc0db3397cfab997fe24927fbe9cc1b6adff8a653ea169cfae6858b4e3bbdbea171f1f23f85bf2df9de9e8d3e7a7a0731307174273d0e74be2f5be4d6ef3e63b857a48ce3ff03b36ba627345b1a5635b65d72dedb5b2f3c3b58d4b8a4b9edc20bfff2e2b60bd6b69d7ff1b96d13e65db776dd647991645cde479fcafc7df9e1d65fff6cdefb7e79e6a18bbb2f79a066ee99ade7dfe97cb170f3daeb367da5ea817bd75eb7e1a637ddf8bd9b1bf63cfff3b9cf3cb9ec8b2fbe74d1833583df9cf6a596def39b573cf0cc0b5fbcf8b26f243f7fe2cf72576cfcb7f469df797ae8f2fbf69ef5c2c59b4e3c6ffad8fa7a06375f9acdfbbd979104ed69ef26a2e507fcecd60dd96cc700f7fecded5b3b370f6e6619bc25db61e4b3fd839df92c490cbf3b0b11d1db932de60dc56928308c95e11bbf233bb021dfd957e8ecedf13792d021e14b15b4f7a086b07469b5bec14bbb3b37f85dd96dfec0a6de4134697bbee06fe92c6cf2c3f2fbf2bd0433fdefeb1d18c80e0ca0d481ce8d3ded8541020ed9ba4bf285f00c102a946b4bb673e3a6020d15ed8394db1fecec293437b5113243d9fc65048ad131d84730f09813413361a5324a191105e81d25df46a8e5b3040641b17990a87469b614e94deddd5c1248ac614250c365204c84e5d18dc62954b4a1bda7a7970bda40d4225a7550336c28746f332ecb0ad61d9d343a1638fd10d5d4d1ea1be580b9d0bba1b7db0f53750ef828274a6518b79c7eed8aebdfbeb4ff3bbf1efdfca7fffef3fffcee27dfb9ece7d37ffeae7bae3cf38617fe7afefdb7dc973ca0de7dd58cc7af6ea8fdcae5151717defb78feb7ffe387237b122fdd5ef8f9ed4f3cd179fbcbd7ac7afc674f7de289aa67efaca8ed5df8cf8beedb5958fdd2bd0fbde707bb7e3974c13bde76cbe6a71af63e7d8af5d0d787dff5e57f770f3eb5eafa3d2fbc63c333898d0b16ccbabafcef4e6949d79cf6dae685e7cdac6cb9f0a281f2eca3e5b9a1d3bef1e415572fccdf70e7a12fdfb5e6b2797bfb3eb8e6c9bbdfd1fdf5130eddf0dddac4ad2f5ebd64da2ffaeec879739eb8e78454f7d293cedd72f6299f7feca1a6872be62f5b7ddf81f3d73ef3f177ff7bef699bfef5ca8f10093ebd75d6562378ee39c3dde73cbd33657efdba5f1d797cf74fbad7ddf39f77fff2f17fdbdaff542841b48e00fe2362157a7bc14ff982a1e3c364856d7dd9b071374205c9f76e26dd234fedded7dbd30121d497cf9262627464b56822062115aba3373bd0734a8118bfb061932faa0a73adbc7e336927d90d5de00a230a117f1413961410ef2e9bda07fc765fe42177a5d731c4cbb80d19594657b91e132be8eabd34478c86d403929bf2e6db49bb6a4358d889f0df3cd85de86cebece9c86e358473a95e264398ded03cbc817b822fe50e206fa1fdd2ee2c906ca7f79ba893900e57808636bef2cdbd1d9d976d3b4abdba029de6b82a18ecebe06e19c246bd73239126dfb9b93dbf8da9ba6553b64717096ca8f74ac147a52ef02e25eec66c41f48b697455d1e5695d63065d9d3d43243b3afca1f67c677b0f70022aaf47cba549946bd72da4b2fe2f60f360b000d00700008c010101001f780fd201cefd9151354036328a17a2bb9d5930c45555c9e80fb17a59d5ab72bf28134ab543bf6be24b3bedf03a7c44ffe90cfb72503f2595b321f1fa13c81ec00100b00878da8d563d8c1b45141efffbeccbe992e3382e421441504482e49c609d521d050d4214fc544758c63bcfde917767979959fb0c3505d05007094508418aab2e05d14111b945424ae01aa000ba2051d150f26677bd3f76cedc14f6ee7b6fdebc9fef7d3b8d3fdaef5688fcf7f93ec155323fe4afab6f9bbf7bf6d1b420b8f377a7f3cfc64cf268630d7cc5fd1b37688f5fd979b153daeab9be3db4141f082e06160db5e34bae27974654722ab4758ade1a5d7dba894e2c872a87946bfe5880ac0aea41d548dab603f650855ee7a56e93da9a8fa806525aeb03d5a1048bf101289db75a495d93ea8a762428c777593de4425feb54873051abf8638d810f1cbd7f139dda7e28b4da0e407a5c6152c27261046e6a521b53aed539f39bca2e9e9e0e299fe1d8cd68bfed502eac804a4c578354e4fc86470f925209d056a8e8002227ddeb17359503d0f35a2bb07572ccb6d9ac2515ca540af328bab8d679a647156e00b9d46a3df3ed028ce924913f6bfb42c381b6fa124ce55554b85c2422f4ce6aca4024a6b98ced202c0452cc38d52ecb78dec5b687055e66f0d4bc0797f741732f2d19833e48090c8d0e2c3808b8a491dd980be68f13abcd792f0cdcb46e4f1a25172e1760256ac53f981db0b5a86510682752ef74a3f264f0caabda361536b8d151a4bc16bfcdd0b83e0fe7ba899fb3fca8b4702be2ce98937223198468f65ad9eee87da3cf0575316869e5c6abcd40d9920726e8bad212cfaec7004f20db0ac29ecb6d0b919fe82b811f248feb99cfc047abc9c2e0a083d4466d3e2684fd9bad6ca848b9822ff92373b1ec749b589a61946ab5906ad5f619c44f7a12c44f6d09ef875c8207b37a089c8378136eb725b28f9fb054fa135357463ff588ae2013347cd44b5f629e0a04c344386b44c175769b28d10662b5de44835ae81e3223ba4bce9cefced629d445ca39cb454014ca7321903e0bed4283cbe752619a6596ce698cbf8ac59b31355b4ad50d63693a52aaf6a5ef45feeb0a347e0ee6f05841515c9996d1bbdc43469e6be40ab2a815d9d462ec18b689734c0466f6f38206fa32dd9f73541f790609355398dd95913742f461d5e2f7082e7128abb83d0525292de07941806dd711d92b52aac70f8fff0e98c002c947735568706519711c493332f2193a6b2aac2a0b5d7862b18b3823a148b15ff95fecb7c280d18414aa4b49c1a4301b90aab1cfb0d1ce7d2c49b919bd29b013ca2b22ef3c5e292e7f8ed78becfb4ebefff8f22b1f7dfd7281e1c8de0fbfdc39fcf2d53c6be17a6158f9e6d36cb8c9de17b72eb17bb7f2138bebf7871f9e7c950ee0676f757f0576785c842aaedbe01d1ea7b09c5d84669824e4c7d78f9da3691e83b8f69efbe4689a622938193f78eddba369111d84dc7ff8ce9b77a73908e07aefbbfdbbd3b4d9b8761fa0206bac49f9fea33f7fce77302a83fbc649be4fe6a6769bfef4db4e8577af1392dda7f05677860b5869d96d86fc072150af820001 DMLOG START_BLOCK 5 DMLOG CREATION_OP ROOT 0 DMLOG RLIMIT_OP ACCOUNT_USAGE UPD {"owner":"eosio","net_usage":{"last_ordinal":1262304004,"value_ex":160608,"consumed":1},"cpu_usage":{"last_ordinal":1262304004,"value_ex":303261,"consumed":101},"ram_usage":453263} @@ -197,4 +197,4 @@ DMLOG RLIMIT_OP ACCOUNT_USAGE UPD {"owner":"alice","net_usage":{"last_ordinal":1 DMLOG APPLIED_TRANSACTION 5 8b63a515f370b80071c5e36e40dd95ea5f4f4432e0b365ce8417926e725ba44f05000000043b3d4b0100000005100709f04201061fb35b591491e4e468bc346507245fefca90b6fb680100d007000012000000000000000090000000000000000001010000010000000000ea3055f3d881d2f7fbf2f7cb6081aff84e7aca1dd3914a0948ef4fc9422e734e8d4d5720000000000000002000000000000000010000000000855c34010000000000000002020000000000ea30550000000000ea30550040cbdaa86c52d5010000000000855c3400000000a8ed3232310000000000855c34000000008090b1ca00000000a8ed32320100000000010000000000ea305500000000a8ed3232010000000000000000000000008b63a515f370b80071c5e36e40dd95ea5f4f4432e0b365ce8417926e725ba44f05000000043b3d4b0100000005100709f04201061fb35b591491e4e468bc346507245fefca90b6fb68010000000000855c34400100000000000000000000000000 DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":4,"value_ex":230575556,"consumed":17883},"average_block_cpu_usage":{"last_ordinal":4,"value_ex":432479225,"consumed":4499},"pending_net_usage":376,"pending_cpu_usage":4100,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1051726,"virtual_cpu_limit":200600} DMLOG RLIMIT_OP STATE UPD {"average_block_net_usage":{"last_ordinal":5,"value_ex":231787427,"consumed":605},"average_block_cpu_usage":{"last_ordinal":5,"value_ex":463041898,"consumed":4529},"pending_net_usage":0,"pending_cpu_usage":0,"total_net_weight":0,"total_cpu_weight":0,"total_ram_bytes":0,"virtual_net_limit":1052778,"virtual_cpu_limit":200800} -DMLOG ACCEPTED_BLOCK 5 05000000050000000400000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add8010000000000011d1e67b0d77626c361a04b47edd0f754b32f182ea99c71dcf2cd354de66635aa0400000000000000010000000000ea305505000000010000000000ea305504000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add801000000000005100709f04201061fb35b591491e4e468bc346507245fefca90b6fb68043b3d4b0000000000ea3055000000000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba53d626c28615c0fae3e8b8d9df697b30ca0daaaa076f51542911e3f613fc162685bb1fb5bf8c32945ffcf9bb3f19736f6bd158b11ad33fc7523be07796e127cf000000000000001f02daf738645b2a8f506b26e825a57cb5bfcc3b6095a6f318a2b2d24149c9970c3201414e9b0f6f5be1a083d32fc7846e72cd216a3a72a71e4180603a28bc5b0f0000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef261980000000000011709e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc160ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b72412652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b447670735c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c25443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b463320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011a68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a2974286bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4058cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead45071d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb40e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1dfce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb40001043b3d4b0000000000ea3055000000000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba53d626c28615c0fae3e8b8d9df697b30ca0daaaa076f51542911e3f613fc162685bb1fb5bf8c32945ffcf9bb3f19736f6bd158b11ad33fc7523be07796e127cf000000000000001f02daf738645b2a8f506b26e825a57cb5bfcc3b6095a6f318a2b2d24149c9970c3201414e9b0f6f5be1a083d32fc7846e72cd216a3a72a71e4180603a28bc5b0f0200d00700001d010100202a3732319217d40dd86a639d03d9e9d4817fe766affeeb7a4fde21afdfd4e1c12aad15ec9199c06343af61b51a479172de9d7d2d294e7bc73039d8318a1964570000bd0107e10b5e0400ad43f8ec00000000010000000000ea305500409e9a2264b89a010000000000ea305500000000a8ed32328a010000000000ea30550000000000855c3401000000010002bb30f6894f29bb6fca635b1df728ad77e48fdd6123ce5e4455b0f71e072e7df80100010000000000855c3400804a1401ea305501000001000000010003ebcf44b45a71d4f225768f602d1e2e2b25ef779ee9897fe744bf1a16e85423d50100010000000000855c3400804a1401ea30550100000000d0070000120101001f225b3395f6daaf942dbfab54659c85ce5b58cf8eb60832232b33d368a264b8114c7426f801838b1bd9529c149cc598fcadcbfe11c49648b74dc066bab4fb315200006307e10b5e0400ad43f8ec00000000010000000000ea30550040cbdaa86c52d5010000000000855c3400000000a8ed3232310000000000855c34000000008090b1ca00000000a8ed32320100000000010000000000ea305500000000a8ed3232010000000001 +DMLOG ACCEPTED_BLOCK 5 05000000050000000400000000000000010000000000ea3055000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add80100011d1e67b0d77626c361a04b47edd0f754b32f182ea99c71dcf2cd354de66635aa0400000000000000010000000000ea305505000000010000000000ea305504000000000100000001000240e54a7b27e042b80a810153bec1dd166eef95fa69f6c9886ae283363bc2add801000000000005100709f04201061fb35b591491e4e468bc346507245fefca90b6fb68043b3d4b0000000000ea3055000000000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba53d626c28615c0fae3e8b8d9df697b30ca0daaaa076f51542911e3f613fc162685bb1fb5bf8c32945ffcf9bb3f19736f6bd158b11ad33fc7523be07796e127cf000000000000001f02daf738645b2a8f506b26e825a57cb5bfcc3b6095a6f318a2b2d24149c9970c3201414e9b0f6f5be1a083d32fc7846e72cd216a3a72a71e4180603a28bc5b0f0000000029807708239aa7de914d3ed61e9009ab2280bfbc50f1d9769f27f8341ef261980000000000011709e86cb0accf8d81c9e85d34bea4b925ae936626d00c984e4691186891f5bc160ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b72412652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b447670735c2186cc36f7bb4aeaf4487b36e57039ccf45a9136aa856a5d569ecca55ef2b4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d674fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c25443fcf88330c586bc0e5f3dee10e7f63c76c00249c87fe4fbf7f38c082006b463320dd4a58212e4d32d1f58926b73ca33a247326c2a5e9fd39268d2384e011a68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a2974286bcb40a24e49c26d0a60513b6aeb8551d264e4717f306b81a37a5afb3b47cedc8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a4058cb6dd1e5607208331eb5983141e159c75a597413887e80e8a9a4b715a507eb7ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43bcd2a26394b36614fd4894241d3c451ab0f6fd110958c3423073621a70826e99c3a6138c5061cf291310887c0b5c71fcaffeab90d5deb50d3b9e687cead45071d528b9f6e9693f45ed277af93474fd473ce7d831dae2180cca35d907bd10cb40e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1dfce57d2331667353a0eac6b4209b67b843a7262a848af0a49a6e2fa9f6584eb40001043b3d4b0000000000ea3055000000000004643e817cad43f8ecb61f9c4bfe0238eaf2b8df6f76e2e24866ebbcba53d626c28615c0fae3e8b8d9df697b30ca0daaaa076f51542911e3f613fc162685bb1fb5bf8c32945ffcf9bb3f19736f6bd158b11ad33fc7523be07796e127cf000000000000001f02daf738645b2a8f506b26e825a57cb5bfcc3b6095a6f318a2b2d24149c9970c3201414e9b0f6f5be1a083d32fc7846e72cd216a3a72a71e4180603a28bc5b0f0200d00700001d010100202a3732319217d40dd86a639d03d9e9d4817fe766affeeb7a4fde21afdfd4e1c12aad15ec9199c06343af61b51a479172de9d7d2d294e7bc73039d8318a1964570000bd0107e10b5e0400ad43f8ec00000000010000000000ea305500409e9a2264b89a010000000000ea305500000000a8ed32328a010000000000ea30550000000000855c3401000000010002bb30f6894f29bb6fca635b1df728ad77e48fdd6123ce5e4455b0f71e072e7df80100010000000000855c3400804a1401ea305501000001000000010003ebcf44b45a71d4f225768f602d1e2e2b25ef779ee9897fe744bf1a16e85423d50100010000000000855c3400804a1401ea30550100000000d0070000120101001f225b3395f6daaf942dbfab54659c85ce5b58cf8eb60832232b33d368a264b8114c7426f801838b1bd9529c149cc598fcadcbfe11c49648b74dc066bab4fb315200006307e10b5e0400ad43f8ec00000000010000000000ea30550040cbdaa86c52d5010000000000855c3400000000a8ed3232310000000000855c34000000008090b1ca00000000a8ed32320100000000010000000000ea305500000000a8ed3232010000000001