diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 0dbaa8c5..8c95289d 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -1,35 +1,12 @@ file(GLOB HEADERS "include/graphene/chain/*.hpp") -add_executable( field_reflector - field_reflector.cpp - types.cpp - type_id.cpp - address.cpp - key_object.cpp - ${HEADERS} ) - -target_include_directories( field_reflector - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) - -target_link_libraries( field_reflector fc graphene_db ) - -add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp - COMMAND field_reflector ${CMAKE_CURRENT_SOURCE_DIR}/db_reflect_cmp.tmpl ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp - COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp.new - DEPENDS field_reflector db_reflect_cmp.tmpl - ) - ## SORT .cpp by most likely to change / break compile add_library( graphene_chain types.cpp type_id.cpp - ${CMAKE_CURRENT_BINARY_DIR}/db_reflect_cmp.cpp - address.cpp asset.cpp - predicate.cpp operations.cpp diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index fe4891b7..aa6862c4 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -24,61 +24,44 @@ namespace graphene { namespace chain { -namespace detail { - -struct predicate_check_visitor +struct predicate_visitor { - predicate_check_visitor( const database& d ): _db(d){} - typedef bool result_type; - template bool operator()( const Predicate& pred )const + const database& db; + + predicate_visitor( const database& d ):db(d){} + + bool operator()( const verify_account_name& p )const { - return pred.check_predicate( _db ); + return p.account_id(db).name == p.account_name; + } + bool operator()( const verify_symbol& p )const + { + return p.asset_id(db).symbol == p.symbol; } - - const database& _db; }; -} // graphene::chain::detail - void_result assert_evaluator::do_evaluate( const assert_operation& o ) { const database& _db = db(); uint32_t skip = _db.get_node_properties().skip_flags; - // TODO: Skip flags + if( skip & database::skip_assert_evaluation ) return void_result(); - for( const vector& s_pred : o.predicates ) + + for( const vector& pdata : o.predicates ) { - std::istringstream is( string( s_pred.begin(), s_pred.end() ) ); - // de-serialize just the static_variant tag - unsigned_int t; - fc::raw::unpack( is, t ); - // everyone checks: delegates must have allocated an opcode for it - FC_ASSERT( t.value < _db.get_global_properties().parameters.max_predicate_opcode ); - if( t.value >= predicate::count() ) + fc::datastream ds( pdata.data(), pdata.size() ); + predicate p; + try { + fc::raw::unpack( ds, p ); + } catch ( const fc::exception& e ) { - // - // delegates allocated an opcode, but our client doesn't know - // the semantics (i.e. we are running an old client) - // - // skip_unknown_predicate indicates we're cool with assuming - // unknown predicates pass - // if( skip & database::skip_unknown_predicate ) continue; - // - // ok, unknown predicate must die - // - FC_ASSERT( false, "unknown predicate" ); + throw; } - // rewind to beginning, unpack it, and check it - is.clear(); - is.seekg(0); - predicate pred; - fc::raw::unpack( is, pred ); - bool pred_passed = pred.visit( detail::predicate_check_visitor( _db ) ); - FC_ASSERT( pred_passed ); + FC_ASSERT( p.visit( predicate_visitor( _db ) ) ); } return void_result(); } diff --git a/libraries/chain/field_reflector.cpp b/libraries/chain/field_reflector.cpp deleted file mode 100644 index ead68f4e..00000000 --- a/libraries/chain/field_reflector.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -using namespace graphene::chain; - -fc::mutable_variant_object g_vo_object_types; -std::vector< fc::mutable_variant_object > g_vo_fields; - -struct serialize_object_type_member_visitor -{ - public: - template - void operator()( const char* name )const - { - fc::mutable_variant_object vo; - vo["name"] = name; - vo["type"] = fc::get_typename::name(); - vo["id"] = g_vo_fields.size(); - g_vo_fields.push_back( vo ); - } -}; - -struct serialize_object_type_visitor -{ - typedef void result_type; - - int t = 0; - serialize_object_type_visitor(int _t ):t(_t){} - - template - result_type operator()( const Type& op )const - { - fc::mutable_variant_object vo; - vo["space_id"] = Type::space_id; - vo["type_id"] = Type::type_id; - g_vo_fields.clear(); - // visit all members - fc::reflector::visit( serialize_object_type_member_visitor() ); - - vo["fields"] = g_vo_fields; - g_vo_object_types[ fc::get_typename::name() ] = vo; - } -}; - -struct getattr_switch_table_entry -{ - uint32_t _switch_val; // (space << 24) | (type << 16) | fieldnum - string _object_typename; - string _field_typename; - string _field_name; -}; - -vector< getattr_switch_table_entry > build_switch_table() -{ - vector< getattr_switch_table_entry > result; - for( const auto& item : g_vo_object_types ) - { - const variant_object& vo = item.value().get_object(); - uint32_t top = (vo["space_id"].as_uint64() << 24) | (vo["type_id"].as_uint64() << 16); - for( const auto& field : vo["fields"].get_array() ) - { - getattr_switch_table_entry e; - e._switch_val = top | field["id"].as_uint64(); - e._object_typename = item.key(); - e._field_typename = field["type"].get_string(); - e._field_name = field["name"].get_string(); - result.push_back( e ); - } - } - - std::sort( result.begin(), result.end(), - []( const getattr_switch_table_entry& a, - const getattr_switch_table_entry& b ) - { - return a._switch_val < b._switch_val; - } ); - - return result; -} - -std::string generate_cmp_attr_impl( const vector< getattr_switch_table_entry >& switch_table ) -{ - std::ostringstream out; - - // switch( space ) - // switch( type ) - // switch( fieldnum ) - // switch( opc ) - - std::map< uint8_t, - std::map< uint8_t, - std::map< uint16_t, - const getattr_switch_table_entry* > > > index; - - for( const getattr_switch_table_entry& e : switch_table ) - { - uint8_t sp = (e._switch_val >> 24) & 0xFF; - uint8_t ty = (e._switch_val >> 16) & 0xFF; - uint16_t fn = (e._switch_val ) & 0xFFFF; - auto& i0 = index; - if( i0.find( sp ) == i0.end() ) - i0[sp] = std::map< uint8_t, std::map< uint16_t, const getattr_switch_table_entry* > >(); - auto& i1 = i0[sp]; - if( i1.find( ty ) == i1.end() ) - i1[ty] = std::map< uint16_t, const getattr_switch_table_entry* >(); - auto& i2 = i1[ty]; - i2[fn] = &e; - } - out << " switch( obj.id.space() )\n" - " {\n"; - for( const auto& e0 : index ) - { - out << " case " << int( e0.first ) << ":\n" - " switch( obj.id.type() )\n" - " {\n"; - for( const auto& e1 : e0.second ) - { - out << " case " << int( e1.first ) << ":\n" - " switch( field_num )\n" - " {\n"; - for( const auto& e2 : e1.second ) - { - const std::string& ft = e2.second->_field_typename; - const std::string& ot = e2.second->_object_typename; - const std::string& fn = e2.second->_field_name; - out << " case " << int( e2.first ) << ":\n" - " {\n" - " // " << ft - << " " << ot - << "." << fn - << "\n" - " const " << ft << "& dbval = object_database::cast< " << ot << " >( obj )." << fn << ";\n" - " return _cmp< " << ft << " >( dbval, lit, opc );\n" - " }\n"; - } - out << " default:\n" - " FC_ASSERT( false, \"unrecognized field_num\" );\n" - " }\n"; - } - out << " default:\n" - " FC_ASSERT( false, \"unrecognized object type\" );\n" - " }\n"; - } - out << " default:\n" - " FC_ASSERT( false, \"unrecognized object space\" );\n" - " }\n"; - - return out.str(); -} - -static const char generated_file_banner[] = -"// _ _ __ _ _ //\n" -"// | | | | / _(_) | //\n" -"// __ _ ___ _ __ ___ _ __ __ _| |_ ___ __| | | |_ _| | ___ //\n" -"// / _` |/ _ \\ '_ \\ / _ \\ '__/ _` | __/ _ \\/ _` | | _| | |/ _ \\ //\n" -"// | (_| | __/ | | | __/ | | (_| | || __/ (_| | | | | | | __/ //\n" -"// \\__, |\\___|_| |_|\\___|_| \\__,_|\\__\\___|\\__,_| |_| |_|_|\\___| //\n" -"// __/ | //\n" -"// |___/ //\n" -"// //\n" -"// Generated by: programs/field_reflector/main.cpp //\n" -"// //\n" -"// Warning: This is a generated file, any changes made here will be //\n" -"// overwritten by the build process. If you need to change what //\n" -"// is generated here, you should either modify the reflected //\n" -"// types, or modify the code generator itself. //\n" -"// //\n" -; - -int main( int argc, char** argv ) -{ - try - { - if( argc != 3 ) - { - std::cout << "syntax: " << argv[0] << " \n"; - return 1; - } - - graphene::chain::impl::wild_object wo; - - for( int32_t i = 0; i < wo.count(); ++i ) - { - wo.set_which(i); - wo.visit( serialize_object_type_visitor(i) ); - } - - vector< getattr_switch_table_entry > switch_table = build_switch_table(); - - fc::mutable_variant_object tmpl_params; - - tmpl_params["generated_file_banner"] = generated_file_banner; - tmpl_params["object_descriptor"] = fc::json::to_string( g_vo_object_types ); - tmpl_params["cmp_attr_impl_body"] = generate_cmp_attr_impl( switch_table ); - - std::ifstream template_file( argv[1] ); - if (!template_file) - FC_THROW("Error opening template file ${template_file}", ("template_file", argv[1])); - std::stringstream ss; - ss << template_file.rdbuf(); - std::string result = fc::format_string( ss.str(), tmpl_params ); - std::ofstream result_file( argv[2] ); - result_file << result; - } - catch ( const fc::exception& e ) - { - edump((e.to_detail_string())); - return 1; - } - return 0; -} diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index f34af396..8a322a50 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -105,17 +105,21 @@ namespace graphene { namespace chain { /** * @brief assert that some conditions are true. * @ingroup operations + * + * This operation performs no changes to the database state, but can but used to verify + * pre or post conditions for other operations. + * */ struct assert_operation { - asset fee; - account_id_type fee_paying_account; - vector< vector< char > > predicates; + asset fee; + account_id_type fee_paying_account; + vector< vector< char > > predicates; flat_set required_auths; account_id_type fee_payer()const { return fee_paying_account; } void get_required_auth(flat_set& active_auth_set, flat_set&)const; - share_type calculate_fee( const fee_schedule_type& k )const{ return k.assert_op_fee; } + share_type calculate_fee( const fee_schedule_type& k )const; void validate()const; void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } diff --git a/libraries/chain/include/graphene/chain/predicate.hpp b/libraries/chain/include/graphene/chain/predicate.hpp index 5980d090..a05d51ab 100644 --- a/libraries/chain/include/graphene/chain/predicate.hpp +++ b/libraries/chain/include/graphene/chain/predicate.hpp @@ -15,46 +15,55 @@ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #pragma once #include -#include - namespace graphene { namespace chain { class database; -class pred_field_lit_cmp +/** + * Used to verify that account_id->name is account_name + */ +struct verify_account_name { -public: - pred_field_lit_cmp( - object_id_type obj_id, - uint16_t field_num, - const vector& lit, - uint8_t opc - ) : - _obj_id( obj_id ), - _field_num( field_num ), - _lit( lit ), - _opc( opc ) - {} - pred_field_lit_cmp() {} // necessary for instantiating static_variant - ~pred_field_lit_cmp() {} + account_id_type account_id; + string account_name; - bool check_predicate( const database& db )const; - - object_id_type _obj_id; - uint16_t _field_num; - vector _lit; - uint8_t _opc; + /** + * Perform state independent checks, such as verifying that + * account_name is a valid name for an account. + */ + bool validate()const { return is_valid_name( account_name ); } }; +/** + * Used to verify that account_id->name is account_name + */ +struct verify_symbol +{ + asset_id_type asset_id; + string symbol; + + /** + * Perform state independent checks, such as verifying that + * account_name is a valid name for an account. + */ + bool validate()const { return is_valid_symbol( symbol ); } +}; + +/** + * When defining predicates do not make the protocol dependent upon + * implementation details. + */ typedef static_variant< - pred_field_lit_cmp + verify_account_name, + verify_symbol > predicate; } } -FC_REFLECT( graphene::chain::pred_field_lit_cmp, (_obj_id)(_field_num)(_lit)(_opc) ); +FC_REFLECT( graphene::chain::verify_account_name, (account_id)(account_name) ) +FC_REFLECT( graphene::chain::verify_symbol, (asset_id)(symbol) ) + diff --git a/libraries/chain/include/graphene/chain/wild_object.hpp b/libraries/chain/include/graphene/chain/wild_object.hpp deleted file mode 100644 index 5b302dac..00000000 --- a/libraries/chain/include/graphene/chain/wild_object.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace graphene { namespace chain { namespace impl { - -/** - * A static_variant of all object types. - * - * Used by field_reflector, this ultimately determines the object - * types which may be inspected by pred_field_lit_cmp. - */ -typedef fc::static_variant< - //null_object, - //base_object, - key_object, - account_object, - asset_object, - force_settlement_object, - delegate_object, - witness_object, - limit_order_object, - call_order_object, - //custom_object, - proposal_object, - operation_history_object, - withdraw_permission_object, - vesting_balance_object, - worker_object - > wild_object; - -} } } diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 6fb26b4a..30cf15d3 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -17,6 +17,7 @@ */ #include #include +#include #include namespace graphene { namespace chain { @@ -813,9 +814,34 @@ share_type account_upgrade_operation::calculate_fee(const fee_schedule_type& k) return k.membership_annual_fee; } +struct predicate_validator +{ + typedef void result_type; + + template + void operator()( const T& p )const + { + p.validate(); + } +}; + void assert_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); + for( const auto& item : predicates ) + { + FC_ASSERT( item.size() > 0 ); + fc::datastream ds( item.data(), item.size() ); + predicate p; + try { + fc::raw::unpack( ds, p ); + } + catch ( const fc::exception& e ) + { + continue; + } + p.visit( predicate_validator() ); + } } void assert_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const { @@ -823,4 +849,12 @@ void assert_operation::get_required_auth(flat_set& active_auth_ active_auth_set.insert(required_auths.begin(), required_auths.end()); } +/** + * The fee for assert operations is proportional to their size + */ +share_type assert_operation::calculate_fee( const fee_schedule_type& k )const +{ + return (k.assert_op_fee * fc::raw::pack_size(*this)) / 1024; +} + } } // namespace graphene::chain diff --git a/libraries/chain/predicate.cpp b/libraries/chain/predicate.cpp deleted file mode 100644 index cedd6fdf..00000000 --- a/libraries/chain/predicate.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include - -#include -#include - -namespace graphene { namespace chain { - -bool pred_field_lit_cmp::check_predicate( const database& db )const -{ - return graphene::chain::impl::cmp_attr_impl( db.get_object( _obj_id ), _field_num, _lit, _opc ); -} - -} } // graphene::chain diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index e61c052b..9627c1cb 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -820,9 +820,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) op.predicates = vector< vector< char > >(); op.predicates.push_back( fc::raw::pack( - predicate( - pred_field_lit_cmp( nathan_key_id, 1, fc::raw::pack( lit_key ), opc_equal_to ) - ) + predicate( verify_account_name{ nathan_id, "nathan" } ) ) ); trx.operations.push_back(op); trx.sign( nathan_key_id, nathan_private_key ); @@ -831,9 +829,7 @@ BOOST_AUTO_TEST_CASE( assert_op_test ) // nathan checks that his public key is not equal to the given value (fail) op.predicates.back() = fc::raw::pack( - predicate( - pred_field_lit_cmp( nathan_key_id, 1, fc::raw::pack( lit_key ), opc_not_equal_to ) - ) + predicate( verify_account_name{ nathan_id, "dan" } ) ); trx.operations.back() = op; trx.sign( nathan_key_id, nathan_private_key );