From 8ac4bc1d5839be175870c982740ea6b861b701f2 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 23 Jun 2015 09:08:34 -0400 Subject: [PATCH] Fix #39 Refactor assert_operation Reasons: 1. The protocol should not depend upon implementation details such as how the database objects are structured or reflected 2. The protocol should deal in abstract concepts 3. Should use fc::datastream rather than istringstream for performance and memory allocation reasons 4. Fees should be charged proportional to the size of the operation 5. Validate on the assert operation should also perform sanity checks on types 6. Protocol definition objects should never depend upon the database because they may be used in situations where the database and evaluators are not present. 7. Reflected field names should never have '_' in them because they become part of the *PUBLIC* json definition. --- libraries/chain/CMakeLists.txt | 23 -- libraries/chain/assert_evaluator.cpp | 59 ++--- libraries/chain/field_reflector.cpp | 239 ------------------ .../include/graphene/chain/operations.hpp | 12 +- .../include/graphene/chain/predicate.hpp | 61 +++-- .../include/graphene/chain/wild_object.hpp | 61 ----- libraries/chain/operations.cpp | 34 +++ libraries/chain/predicate.cpp | 34 --- tests/tests/operation_tests2.cpp | 8 +- 9 files changed, 100 insertions(+), 431 deletions(-) delete mode 100644 libraries/chain/field_reflector.cpp delete mode 100644 libraries/chain/include/graphene/chain/wild_object.hpp delete mode 100644 libraries/chain/predicate.cpp 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 );