From 248049c5f41b0754d8a2fd3449e00c550906c4ed Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 21 Apr 2017 19:07:56 -0400 Subject: [PATCH] Basic transfer working for base token - absent signature verification --- libraries/chain/database.cpp | 64 ++++++++++++++++++- .../chain/include/eos/chain/database.hpp | 7 +- .../chain/include/eos/chain/genesis_state.hpp | 8 ++- libraries/chain/include/eos/chain/message.hpp | 9 +++ tests/common/database_fixture.cpp | 2 +- tests/tests/block_tests.cpp | 15 +++-- 6 files changed, 94 insertions(+), 11 deletions(-) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 3a8178bf6..6e2cd1cb8 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -32,6 +32,8 @@ #include #include +#include + #include #include #include @@ -487,18 +489,38 @@ void database::validate_uniqueness( const signed_transaction& trx )const { auto& trx_idx = get_index(); FC_ASSERT( trx_idx.indices().get().find(trx_id) == trx_idx.indices().get().end()); } + +void database::validate_referenced_accounts( const signed_transaction& trx )const { + for( const auto& auth : trx.provided_authorizations ) { + get_account( auth.authorizing_account ); + } + for( const auto& msg : trx.messages ) { + get_account( msg.sender ); + get_account( msg.recipient ); + const account_name* prev = nullptr; + for( const auto& acnt : msg.notify ) { + get_account( acnt ); + if( prev ) + FC_ASSERT( acnt < *prev ); + FC_ASSERT( acnt != msg.sender ); + FC_ASSERT( acnt != msg.recipient ); + prev = &acnt; + } + } +} void database::validate_transaction( const signed_transaction& trx )const { try { FC_ASSERT( trx.messages.size() > 0, "A transaction must have at least one message" ); validate_uniqueness( trx ); validate_tapos( trx ); + validate_referenced_accounts( trx ); for( const auto& m : trx.messages ) { /// TODO: this loop can be processed in parallel message_validate_context mvc( trx, m ); auto contract_handlers_itr = message_validate_handlers.find( m.recipient ); if( contract_handlers_itr != message_validate_handlers.end() ) { - auto message_handelr_itr = contract_handlers_itr->second.find( m.type ); + auto message_handelr_itr = contract_handlers_itr->second.find( m.recipient + '/' + m.type ); if( message_handelr_itr != contract_handlers_itr->second.end() ) { message_handelr_itr->second(mvc); continue; @@ -675,6 +697,33 @@ void database::initialize_indexes() { void database::init_genesis(const genesis_state_type& genesis_state) { try { + set_validate_handler( "sys", "sys/Transfer", [&]( message_validate_context& context ) { + idump((context.msg)); + auto transfer = context.msg.as(); + FC_ASSERT( context.msg.has_notify( transfer.to ), "Must notify recipient of transfer" ); + }); + + set_precondition_validate_handler( "sys", "sys/Transfer", [&]( precondition_validate_context& context ) { + idump((context.msg)(context.receiver)); + auto transfer = context.msg.as(); + const auto& from = get_account( transfer.from ); + FC_ASSERT( from.balance > transfer.amount, "Insufficient Funds", + ("from.balance",from.balance)("transfer.amount",transfer.amount) ); + }); + + set_apply_handler( "sys", "sys/Transfer", [&]( apply_context& context ) { + idump((context.msg)(context.receiver)); + auto transfer = context.msg.as(); + const auto& from = get_account( transfer.from ); + const auto& to = get_account( transfer.to ); + modify( from, [&]( account_object& a ) { + a.balance -= transfer.amount; + }); + modify( to, [&]( account_object& a ) { + a.balance += transfer.amount; + }); + }); + FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0, "Genesis timestamp must be divisible by config::BlockIntervalSeconds." ); @@ -689,10 +738,18 @@ void database::init_genesis(const genesis_state_type& genesis_state) uint32_t old_flags; } inhibitor(*this); + + /// create the system contract + create([&](account_object& a) { + a.name = "sys"; + }); + // Create initial accounts for (const auto& acct : genesis_state.initial_accounts) { create([&acct](account_object& a) { a.name = acct.name.c_str(); + a.balance = acct.balance; + idump((acct.name)(a.balance)); // a.active_key = acct.active_key; // a.owner_key = acct.owner_key; }); @@ -1011,4 +1068,9 @@ void database::set_apply_handler( const account_name& contract, const message_ty apply_handlers[contract][action] = v; } +const account_object& database::get_account( const account_name& name )const { +try { + return get(name); +} FC_CAPTURE_AND_RETHROW( (name) ) } + } } diff --git a/libraries/chain/include/eos/chain/database.hpp b/libraries/chain/include/eos/chain/database.hpp index 2f0bd09b0..2fec4f897 100644 --- a/libraries/chain/include/eos/chain/database.hpp +++ b/libraries/chain/include/eos/chain/database.hpp @@ -173,6 +173,8 @@ namespace eos { namespace chain { const signed_transaction& get_recent_transaction( const transaction_id_type& trx_id )const; std::vector get_block_ids_on_fork(block_id_type head_of_fork) const; + const account_object& get_account( const account_name& name )const; + /** * Calculate the percent of block production slots that were missed in the * past 128 blocks, not including the current block. @@ -305,14 +307,17 @@ namespace eos { namespace chain { void apply_debug_updates(); void debug_update(const fc::variant_object& update); + + private: /** * This method validates transactions without adding it to the pending state. * @return true if the transaction would validate */ void validate_transaction(const signed_transaction& trx)const; void validate_tapos( const signed_transaction& trx )const; + void validate_referenced_accounts( const signed_transaction& trx )const; + - private: optional _pending_tx_session; public: diff --git a/libraries/chain/include/eos/chain/genesis_state.hpp b/libraries/chain/include/eos/chain/genesis_state.hpp index 268086418..62079a008 100644 --- a/libraries/chain/include/eos/chain/genesis_state.hpp +++ b/libraries/chain/include/eos/chain/genesis_state.hpp @@ -39,13 +39,15 @@ using std::vector; struct genesis_state_type { struct initial_account_type { initial_account_type(const string& name = string(), + uint64_t bal = 0, const public_key_type& owner_key = public_key_type(), const public_key_type& active_key = public_key_type()) - : name(name), + : name(name), balance(bal), owner_key(owner_key), active_key(active_key == public_key_type()? owner_key : active_key) {} - string name; + string name; + uint64_t balance = 0; public_key_type owner_key; public_key_type active_key; }; @@ -80,7 +82,7 @@ struct genesis_state_type { } } // namespace eos::chain -FC_REFLECT(eos::chain::genesis_state_type::initial_account_type, (name)(owner_key)(active_key)) +FC_REFLECT(eos::chain::genesis_state_type::initial_account_type, (name)(balance)(owner_key)(active_key)) FC_REFLECT(eos::chain::genesis_state_type::initial_producer_type, (owner_name)(block_signing_key)) diff --git a/libraries/chain/include/eos/chain/message.hpp b/libraries/chain/include/eos/chain/message.hpp index 3378bf1c9..51f04e556 100644 --- a/libraries/chain/include/eos/chain/message.hpp +++ b/libraries/chain/include/eos/chain/message.hpp @@ -43,6 +43,15 @@ struct message { type = t; data = fc::raw::pack( value ); } + template + T as()const { + return fc::raw::unpack(data); + } + bool has_notify( const account_name& n )const { + for( const auto& no : notify ) + if( no == n ) return true; + return false; + } }; diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index b095adccc..19c9daadd 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -49,7 +49,7 @@ testing_fixture::testing_fixture() { auto name = std::string("init") + fc::to_string(i); auto private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(name)); public_key_type public_key = private_key.get_public_key(); - default_genesis_state.initial_accounts.emplace_back(name, public_key, public_key); + default_genesis_state.initial_accounts.emplace_back(name, 100000, public_key, public_key); key_ring[public_key] = private_key; private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(name + ".producer")); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index bd7cc4f07..ba3adb1c7 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -65,16 +66,20 @@ BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture) signed_transaction trx; BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); /// no messages trx.messages.resize(1); - - trx.set_reference_block( db.head_block_id() ); trx.set_expiration( db.head_block_time() ); - ilog("."); + trx.messages[0].sender = "init1"; + trx.messages[0].recipient = "sys"; + trx.messages[0].type = "Transfer"; + trx.messages[0].set( "Transfer", eos::chain::Transfer{ "init1", "init2", 100, "memo" } ); + BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); // "fail to notify receiver, init2" + trx.messages[0].notify = {"init2"}; + trx.messages[0].set( "Transfer", eos::chain::Transfer{ "init1", "init2", 100, "memo" } ); db.push_transaction(trx); - ilog("."); + BOOST_CHECK_EQUAL( db.get_account( "init1" ).balance, 100000 - 100 ); + BOOST_CHECK_EQUAL( db.get_account( "init2" ).balance, 100000 + 100 ); db.produce_blocks(1); - ilog("."); BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); /// no messages -- GitLab