From 22c42e4aa3b7cc3ba73daeff534436ddb09301f7 Mon Sep 17 00:00:00 2001 From: arhag Date: Fri, 14 Sep 2018 16:43:43 -0400 Subject: [PATCH] fix bugs in abi_serializer variant to binary --- libraries/chain/abi_serializer.cpp | 25 ++++--- unittests/abi_tests.cpp | 112 +++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 19 deletions(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index 255ef2250ec..1c654ddc4d8 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -312,7 +312,7 @@ namespace eosio { namespace chain { vars.emplace_back(std::move(v)); } EOS_ASSERT( vars.size() == size.value, - unpack_exception, + unpack_exception, "packed size does not match unpacked array size, packed size ${p} actual size ${a}", ("p", size)("a", vars.size()) ); return fc::variant( std::move(vars) ); @@ -386,8 +386,6 @@ namespace eosio { namespace chain { } else if( ends_with(field.type, "$") && allow_extensions ) { missing_extension = true; } else { - _variant_to_binary(field.type, fc::variant(), ds, false, recursion_depth, deadline, max_serialization_time); - /// TODO: default construct field and write it out EOS_THROW( pack_exception, "Missing '${f}' in variant object", ("f",field.name) ); } } @@ -395,17 +393,18 @@ namespace eosio { namespace chain { const auto& va = var.get_array(); EOS_ASSERT( st.base == type_name(), invalid_type_inside_abi, "support for base class as array not yet implemented" ); uint32_t i = 0; - if (va.size() > 0) { - for( const auto& field : st.fields ) { - if( va.size() > i ) - _variant_to_binary(_remove_bin_extension(field.type), va[i], ds, allow_extensions && &field == &st.fields.back(), recursion_depth, deadline, max_serialization_time); - else if( ends_with(field.type, "$") && allow_extensions ) - break; - else - _variant_to_binary(field.type, fc::variant(), ds, false, recursion_depth, deadline, max_serialization_time); - ++i; - } + for( const auto& field : st.fields ) { + if( va.size() > i ) + _variant_to_binary(_remove_bin_extension(field.type), va[i], ds, allow_extensions && &field == &st.fields.back(), recursion_depth, deadline, max_serialization_time); + else if( ends_with(field.type, "$") && allow_extensions ) + break; + else + EOS_THROW( pack_exception, "Early end to array specifying the fields of struct '${t}'; require input for field '${f}'", + ("t", st.name)("f", field.name) ); + ++i; } + } else { + EOS_THROW( pack_exception, "Failed to serialize struct '${t}' in variant object", ("t", st.name)); } } } FC_CAPTURE_AND_RETHROW( (type)(var) ) } diff --git a/unittests/abi_tests.cpp b/unittests/abi_tests.cpp index e89bf215d98..b612498a4cb 100644 --- a/unittests/abi_tests.cpp +++ b/unittests/abi_tests.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -558,7 +559,7 @@ struct abi_gen_helper { std::string contract; std::vector actions; - + auto extra_args = std::vector{"-fparse-all-comments", "--std=c++14", "--target=wasm32", "-ffreestanding", "-nostdlib", "-nostdlibinc", "-fno-threadsafe-statics", "-fno-rtti", "-fno-exceptions", include_param, boost_include_param, stdcpp_include_param, @@ -567,7 +568,7 @@ struct abi_gen_helper { bool res = runToolOnCodeWithArgs( new find_eosio_abi_macro_action(contract, actions, ""), source, - extra_args + extra_args ); FC_ASSERT(res == true); @@ -3438,7 +3439,16 @@ BOOST_AUTO_TEST_CASE(abi_recursive_structs) "type": "a" } ] - } + }, + { + "name": "hi2", + "base": "", + "fields": [{ + "name": "user", + "type": "name" + } + ] + } ], "actions": [{ "name": "hi", @@ -3449,10 +3459,10 @@ BOOST_AUTO_TEST_CASE(abi_recursive_structs) "tables": [] } )====="; - + abi_serializer abis(fc::json::from_string(abi_str).as(), max_serialization_time); - string hi_data = "{\"user\":\"eosio\",\"arg2\":{\"user\":\"1\"}}"; - auto bin = abis.variant_to_binary("hi", fc::json::from_string(hi_data), max_serialization_time); + string hi_data = "{\"user\":\"eosio\"}"; + auto bin = abis.variant_to_binary("hi2", fc::json::from_string(hi_data), max_serialization_time); BOOST_CHECK_THROW( abis.binary_to_variant("hi", bin, max_serialization_time);, fc::exception ); } FC_LOG_AND_RETHROW() @@ -3600,4 +3610,94 @@ BOOST_AUTO_TEST_CASE(version) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(abi_serialize_incomplete_json_array) +{ + using eosio::testing::fc_exception_message_starts_with; + + auto abi = R"({ + "version": "eosio::abi/1.0", + "structs": [ + {"name": "s", "base": "", "fields": [ + {"name": "i0", "type": "int8"}, + {"name": "i1", "type": "int8"}, + {"name": "i2", "type": "int8"} + ]} + ], + })"; + + try { + abi_serializer abis( fc::json::from_string(abi).as(), max_serialization_time ); + + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"([])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Early end to array specifying the fields of struct") ); + + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"([1,2])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Early end to array specifying the fields of struct") ); + + verify_round_trip_conversion(abis, "s", R"([1,2,3])", "010203", R"({"i0":1,"i1":2,"i2":3})"); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(abi_serialize_incomplete_json_object) +{ + using eosio::testing::fc_exception_message_is; + + auto abi = R"({ + "version": "eosio::abi/1.0", + "structs": [ + {"name": "s1", "base": "", "fields": [ + {"name": "i0", "type": "int8"}, + {"name": "i1", "type": "int8"} + ]}, + {"name": "s2", "base": "", "fields": [ + {"name": "f0", "type": "s1"} + {"name": "i2", "type": "int8"} + ]} + ], + })"; + + try { + abi_serializer abis( fc::json::from_string(abi).as(), max_serialization_time ); + + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({})"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing 'f0' in variant object") ); + + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"f0":{"i0":1}})"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing 'i1' in variant object") ); + + verify_round_trip_conversion(abis, "s2", R"({"f0":{"i0":1,"i1":2},"i2":3})", "010203"); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE(abi_serialize_json_mismatching_type) +{ + using eosio::testing::fc_exception_message_is; + + auto abi = R"({ + "version": "eosio::abi/1.0", + "structs": [ + {"name": "s1", "base": "", "fields": [ + {"name": "i0", "type": "int8"}, + ]}, + {"name": "s2", "base": "", "fields": [ + {"name": "f0", "type": "s1"} + {"name": "i1", "type": "int8"} + ]} + ], + })"; + + try { + abi_serializer abis( fc::json::from_string(abi).as(), max_serialization_time ); + + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"f0":1,"i1":2})"), max_serialization_time), + pack_exception, fc_exception_message_is("Failed to serialize struct 's1' in variant object") ); + + verify_round_trip_conversion(abis, "s2", R"({"f0":{"i0":1},"i1":2})", "0102"); + + } FC_LOG_AND_RETHROW() +} + + BOOST_AUTO_TEST_SUITE_END()