Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
abi_serializer.cpp
Go to the documentation of this file.
4#include <fc/io/raw.hpp>
5#include <boost/algorithm/string/predicate.hpp>
6#include <fc/io/varint.hpp>
7
8namespace sysio { namespace chain {
9
11
12 using boost::algorithm::starts_with;
13 using boost::algorithm::ends_with;
14 using std::string;
15 using std::string_view;
16
17 template <typename T>
19 T temp;
20 fc::raw::unpack( stream, temp );
21 return fc::variant(temp);
22 }
23
24 template <typename T>
26 fc::yield_function_t y = [&yield](){ yield(0); }; // create yield function matching fc::variant requirements, 0 for recursive depth
27 T temp;
28 fc::raw::unpack( stream, temp );
29 y();
30 return fc::variant( temp, y );
31 }
32
33 template <typename T>
35 return []( const fc::variant& var, fc::datastream<char*>& ds, bool is_array, bool is_optional, const abi_serializer::yield_function_t& yield ){
36 if( is_array )
37 fc::raw::pack( ds, var.as<vector<T>>() );
38 else if ( is_optional )
39 fc::raw::pack( ds, var.as<std::optional<T>>() );
40 else
41 fc::raw::pack( ds, var.as<T>());
42 };
43 }
44
45 template <typename T>
46 auto pack_unpack() {
47 return std::make_pair<abi_serializer::unpack_function, abi_serializer::pack_function>(
48 []( fc::datastream<const char*>& stream, bool is_array, bool is_optional, const abi_serializer::yield_function_t& yield) {
49 if( is_array )
50 return variant_from_stream<vector<T>>(stream);
51 else if ( is_optional )
53 return variant_from_stream<T>(stream);
54 },
56 );
57 }
58
59 template <typename T>
61 return std::make_pair<abi_serializer::unpack_function, abi_serializer::pack_function>(
62 []( fc::datastream<const char*>& stream, bool is_array, bool is_optional, const abi_serializer::yield_function_t& yield) {
63 if( is_array )
64 return variant_from_stream<vector<T>>(stream);
65 else if ( is_optional )
67 return variant_from_stream<T>(stream, yield);
68 },
70 );
71 }
72
74 configure_built_in_types();
75 set_abi(abi, yield);
76 }
77
78 abi_serializer::abi_serializer( const abi_def& abi, const fc::microseconds& max_serialization_time) {
79 configure_built_in_types();
80 set_abi(abi, max_serialization_time);
81 }
82
84 std::pair<abi_serializer::unpack_function, abi_serializer::pack_function> unpack_pack ) {
85 built_in_types[name] = std::move( unpack_pack );
86 }
87
88 void abi_serializer::configure_built_in_types() {
89
90 built_in_types.emplace("bool", pack_unpack<uint8_t>());
91 built_in_types.emplace("int8", pack_unpack<int8_t>());
92 built_in_types.emplace("uint8", pack_unpack<uint8_t>());
93 built_in_types.emplace("int16", pack_unpack<int16_t>());
94 built_in_types.emplace("uint16", pack_unpack<uint16_t>());
95 built_in_types.emplace("int32", pack_unpack<int32_t>());
96 built_in_types.emplace("uint32", pack_unpack<uint32_t>());
97 built_in_types.emplace("int64", pack_unpack<int64_t>());
98 built_in_types.emplace("uint64", pack_unpack<uint64_t>());
99 built_in_types.emplace("int128", pack_unpack<int128_t>());
100 built_in_types.emplace("uint128", pack_unpack<uint128_t>());
101 built_in_types.emplace("varint32", pack_unpack<fc::signed_int>());
102 built_in_types.emplace("varuint32", pack_unpack<fc::unsigned_int>());
103
104 // TODO: Add proper support for floating point types. For now this is good enough.
105 built_in_types.emplace("float32", pack_unpack<float>());
106 built_in_types.emplace("float64", pack_unpack<double>());
107 built_in_types.emplace("float128", pack_unpack<float128_t>());
108
109 built_in_types.emplace("time_point", pack_unpack<fc::time_point>());
110 built_in_types.emplace("time_point_sec", pack_unpack<fc::time_point_sec>());
111 built_in_types.emplace("block_timestamp_type", pack_unpack<block_timestamp_type>());
112
113 built_in_types.emplace("name", pack_unpack<name>());
114
115 built_in_types.emplace("bytes", pack_unpack<bytes>());
116 built_in_types.emplace("string", pack_unpack<string>());
117
118 built_in_types.emplace("checksum160", pack_unpack<checksum160_type>());
119 built_in_types.emplace("checksum256", pack_unpack<checksum256_type>());
120 built_in_types.emplace("checksum512", pack_unpack<checksum512_type>());
121
122 built_in_types.emplace("public_key", pack_unpack_deadline<public_key_type>());
123 built_in_types.emplace("signature", pack_unpack_deadline<signature_type>());
124
125 built_in_types.emplace("symbol", pack_unpack<symbol>());
126 built_in_types.emplace("symbol_code", pack_unpack<symbol_code>());
127 built_in_types.emplace("asset", pack_unpack<asset>());
128 built_in_types.emplace("extended_asset", pack_unpack<extended_asset>());
129 }
130
131 void abi_serializer::set_abi(const abi_def& abi, const yield_function_t& yield) {
133
134 SYS_ASSERT(starts_with(abi.version, "sysio::abi/1."), unsupported_abi_version_exception, "ABI has an unsupported version");
135
136 typedefs.clear();
137 structs.clear();
138 actions.clear();
139 tables.clear();
140 error_messages.clear();
141 variants.clear();
142 action_results.clear();
143
144 for( const auto& st : abi.structs )
145 structs[st.name] = st;
146
147 for( const auto& td : abi.types ) {
148 SYS_ASSERT(!_is_type(td.new_type_name, ctx), duplicate_abi_type_def_exception,
149 "type already exists", ("new_type_name",impl::limit_size(td.new_type_name)));
150 typedefs[td.new_type_name] = td.type;
151 }
152
153 for( const auto& a : abi.actions )
154 actions[a.name] = a.type;
155
156 for( const auto& t : abi.tables )
157 tables[t.name] = t.type;
158
159 for( const auto& e : abi.error_messages )
160 error_messages[e.error_code] = e.error_msg;
161
162 for( const auto& v : abi.variants.value )
163 variants[v.name] = v;
164
165 for( const auto& r : abi.action_results.value )
166 action_results[r.name] = r.result_type;
167
172 SYS_ASSERT( typedefs.size() == abi.types.size(), duplicate_abi_type_def_exception, "duplicate type definition detected" );
173 SYS_ASSERT( structs.size() == abi.structs.size(), duplicate_abi_struct_def_exception, "duplicate struct definition detected" );
174 SYS_ASSERT( actions.size() == abi.actions.size(), duplicate_abi_action_def_exception, "duplicate action definition detected" );
175 SYS_ASSERT( tables.size() == abi.tables.size(), duplicate_abi_table_def_exception, "duplicate table definition detected" );
176 SYS_ASSERT( error_messages.size() == abi.error_messages.size(), duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" );
177 SYS_ASSERT( variants.size() == abi.variants.value.size(), duplicate_abi_variant_def_exception, "duplicate variant definition detected" );
178 SYS_ASSERT( action_results.size() == abi.action_results.value.size(), duplicate_abi_action_results_def_exception, "duplicate action results definition detected" );
179
180 validate(ctx);
181 }
182
183 void abi_serializer::set_abi(const abi_def& abi, const fc::microseconds& max_serialization_time) {
184 return set_abi(abi, create_yield_function(max_serialization_time));
185 }
186
187 bool abi_serializer::is_builtin_type(const std::string_view& type)const {
188 return built_in_types.find(type) != built_in_types.end();
189 }
190
191 bool abi_serializer::is_integer(const std::string_view& type) const {
192 return boost::starts_with(type, "uint") || boost::starts_with(type, "int");
193 }
194
195 int abi_serializer::get_integer_size(const std::string_view& type) const {
196 SYS_ASSERT( is_integer(type), invalid_type_inside_abi, "${type} is not an integer type", ("type",impl::limit_size(type)));
197 if( boost::starts_with(type, "uint") ) {
198 return boost::lexical_cast<int>(type.substr(4));
199 } else {
200 return boost::lexical_cast<int>(type.substr(3));
201 }
202 }
203
204 bool abi_serializer::is_struct(const std::string_view& type)const {
205 return structs.find(resolve_type(type)) != structs.end();
206 }
207
208 bool abi_serializer::is_array(const string_view& type)const {
209 return ends_with(type, "[]");
210 }
211
212 bool abi_serializer::is_optional(const string_view& type)const {
213 return ends_with(type, "?");
214 }
215
216 bool abi_serializer::is_type(const std::string_view& type, const yield_function_t& yield)const {
218 return _is_type(type, ctx);
219 }
220
221 bool abi_serializer::is_type(const std::string_view& type, const fc::microseconds& max_serialization_time) const {
222 return is_type(type, create_yield_function(max_serialization_time));
223 }
224
225 std::string_view abi_serializer::fundamental_type(const std::string_view& type)const {
226 if( is_array(type) ) {
227 return type.substr(0, type.size()-2);
228 } else if ( is_optional(type) ) {
229 return type.substr(0, type.size()-1);
230 } else {
231 return type;
232 }
233 }
234
235 std::string_view abi_serializer::_remove_bin_extension(const std::string_view& type) {
236 if( ends_with(type, "$") )
237 return type.substr(0, type.size()-1);
238 else
239 return type;
240 }
241
242 bool abi_serializer::_is_type(const std::string_view& rtype, impl::abi_traverse_context& ctx )const {
243 auto h = ctx.enter_scope();
244 auto type = fundamental_type(rtype);
245 if( built_in_types.find(type) != built_in_types.end() ) return true;
246 if( typedefs.find(type) != typedefs.end() ) return _is_type(typedefs.find(type)->second, ctx);
247 if( structs.find(type) != structs.end() ) return true;
248 if( variants.find(type) != variants.end() ) return true;
249 return false;
250 }
251
252 const struct_def& abi_serializer::get_struct(const std::string_view& type)const {
253 auto itr = structs.find(resolve_type(type) );
254 SYS_ASSERT( itr != structs.end(), invalid_type_inside_abi, "Unknown struct ${type}", ("type",impl::limit_size(type)) );
255 return itr->second;
256 }
257
258 void abi_serializer::validate( impl::abi_traverse_context& ctx )const {
259 for( const auto& t : typedefs ) { try {
260 vector<std::string_view> types_seen{t.first, t.second};
261 auto itr = typedefs.find(t.second);
262 while( itr != typedefs.end() ) {
263 ctx.check_deadline();
264 SYS_ASSERT( find(types_seen.begin(), types_seen.end(), itr->second) == types_seen.end(), abi_circular_def_exception,
265 "Circular reference in type ${type}", ("type", impl::limit_size(t.first)) );
266 types_seen.emplace_back(itr->second);
267 itr = typedefs.find(itr->second);
268 }
269 } FC_CAPTURE_AND_RETHROW( (t) ) }
270 for( const auto& t : typedefs ) { try {
271 SYS_ASSERT(_is_type(t.second, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(t.second)) );
272 } FC_CAPTURE_AND_RETHROW( (t) ) }
273 for( const auto& s : structs ) { try {
274 if( s.second.base != type_name() ) {
275 const struct_def* current = &s.second;
276 vector<std::string_view> types_seen{current->name};
277 while( current->base != type_name() ) {
278 ctx.check_deadline();
279 const struct_def& base = get_struct(current->base); //<-- force struct to inherit from another struct
280 SYS_ASSERT( find(types_seen.begin(), types_seen.end(), base.name) == types_seen.end(), abi_circular_def_exception,
281 "Circular reference in struct ${type}", ("type",impl::limit_size(s.second.name)) );
282 types_seen.emplace_back(base.name);
283 current = &base;
284 }
285 }
286 for( const auto& field : s.second.fields ) { try {
287 ctx.check_deadline();
288 SYS_ASSERT(_is_type(_remove_bin_extension(field.type), ctx), invalid_type_inside_abi,
289 "${type}", ("type",impl::limit_size(field.type)) );
292 for( const auto& s : variants ) { try {
293 for( const auto& type : s.second.types ) { try {
294 ctx.check_deadline();
295 SYS_ASSERT(_is_type(type, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(type)) );
296 } FC_CAPTURE_AND_RETHROW( (type) ) }
298 for( const auto& a : actions ) { try {
299 ctx.check_deadline();
300 SYS_ASSERT(_is_type(a.second, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(a.second)) );
302
303 for( const auto& t : tables ) { try {
304 ctx.check_deadline();
305 SYS_ASSERT(_is_type(t.second, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(t.second)) );
306 } FC_CAPTURE_AND_RETHROW( (t) ) }
307
308 for( const auto& r : action_results ) { try {
309 ctx.check_deadline();
310 SYS_ASSERT(_is_type(r.second, ctx), invalid_type_inside_abi, "${type}", ("type",impl::limit_size(r.second)) );
312 }
313
314 std::string_view abi_serializer::resolve_type(const std::string_view& type)const {
315 auto itr = typedefs.find(type);
316 if( itr != typedefs.end() ) {
317 for( auto i = typedefs.size(); i > 0; --i ) { // avoid infinite recursion
318 const std::string_view& t = itr->second;
319 itr = typedefs.find( t );
320 if( itr == typedefs.end() ) return t;
321 }
322 }
323 return type;
324 }
325
326 void abi_serializer::_binary_to_variant( const std::string_view& type, fc::datastream<const char *>& stream,
328 {
329 auto h = ctx.enter_scope();
330 auto s_itr = structs.find(type);
331 SYS_ASSERT( s_itr != structs.end(), invalid_type_inside_abi, "Unknown type ${type}", ("type",ctx.maybe_shorten(type)) );
332 ctx.hint_struct_type_if_in_array( s_itr );
333 const auto& st = s_itr->second;
334 if( st.base != type_name() ) {
335 _binary_to_variant(resolve_type(st.base), stream, obj, ctx);
336 }
337 bool encountered_extension = false;
338 for( uint32_t i = 0; i < st.fields.size(); ++i ) {
339 const auto& field = st.fields[i];
340 bool extension = ends_with(field.type, "$");
341 encountered_extension |= extension;
342 if( !stream.remaining() ) {
343 if( extension ) {
344 continue;
345 }
346 if( encountered_extension ) {
347 SYS_THROW( abi_exception, "Encountered field '${f}' without binary extension designation while processing struct '${p}'",
348 ("f", ctx.maybe_shorten(field.name))("p", ctx.get_path_string()) );
349 }
350 SYS_THROW( unpack_exception, "Stream unexpectedly ended; unable to unpack field '${f}' of struct '${p}'",
351 ("f", ctx.maybe_shorten(field.name))("p", ctx.get_path_string()) );
352
353 }
354 auto h1 = ctx.push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
355 auto field_type = resolve_type( extension ? _remove_bin_extension(field.type) : field.type );
356 auto v = _binary_to_variant(field_type, stream, ctx);
357 if( ctx.is_logging() && v.is_string() && field_type == "bytes" ) {
359 auto size = v.get_string().size() / 2; // half because it is in hex
360 sub_obj( "size", size );
361 if( size > impl::hex_log_max_size ) {
362 sub_obj( "trimmed_hex", v.get_string().substr( 0, impl::hex_log_max_size*2 ) );
363 } else {
364 sub_obj( "hex", std::move( v ) );
365 }
366 obj( field.name, std::move(sub_obj) );
367 } else {
368 obj( field.name, std::move(v) );
369 }
370 }
371 }
372
373 fc::variant abi_serializer::_binary_to_variant( const std::string_view& type, fc::datastream<const char *>& stream,
374 impl::binary_to_variant_context& ctx )const
375 {
376 auto h = ctx.enter_scope();
377 auto rtype = resolve_type(type);
378 auto ftype = fundamental_type(rtype);
379 auto btype = built_in_types.find(ftype );
380 if( btype != built_in_types.end() ) {
381 try {
382 return btype->second.first(stream, is_array(rtype), is_optional(rtype), ctx.get_yield_function());
383 } SYS_RETHROW_EXCEPTIONS( unpack_exception, "Unable to unpack ${class} type '${type}' while processing '${p}'",
384 ("class", is_array(rtype) ? "array of built-in" : is_optional(rtype) ? "optional of built-in" : "built-in")
385 ("type", impl::limit_size(ftype))("p", ctx.get_path_string()) )
386 }
387 if ( is_array(rtype) ) {
388 ctx.hint_array_type_if_in_array();
389 fc::unsigned_int size;
390 try {
391 fc::raw::unpack(stream, size);
392 } SYS_RETHROW_EXCEPTIONS( unpack_exception, "Unable to unpack size of array '${p}'", ("p", ctx.get_path_string()) )
393 vector<fc::variant> vars;
394 auto h1 = ctx.push_to_path( impl::array_index_path_item{} );
395 for( decltype(size.value) i = 0; i < size; ++i ) {
396 ctx.set_array_index_of_path_back(i);
397 auto v = _binary_to_variant(ftype, stream, ctx);
398 // QUESTION: Is it actually desired behavior to require the returned variant to not be null?
399 // This would disallow arrays of optionals in general (though if all optionals in the array were present it would be allowed).
400 // Is there any scenario in which the returned variant would be null other than in the case of an empty optional?
401 SYS_ASSERT( !v.is_null(), unpack_exception, "Invalid packed array '${p}'", ("p", ctx.get_path_string()) );
402 vars.emplace_back(std::move(v));
403 }
404 // QUESTION: Why would the assert below ever fail?
405 SYS_ASSERT( vars.size() == size.value,
406 unpack_exception,
407 "packed size does not match unpacked array size, packed size ${p} actual size ${a}",
408 ("p", size)("a", vars.size()) );
409 return fc::variant( std::move(vars) );
410 } else if ( is_optional(rtype) ) {
411 char flag;
412 try {
413 fc::raw::unpack(stream, flag);
414 } SYS_RETHROW_EXCEPTIONS( unpack_exception, "Unable to unpack presence flag of optional '${p}'", ("p", ctx.get_path_string()) )
415 return flag ? _binary_to_variant(ftype, stream, ctx) : fc::variant();
416 } else {
417 auto v_itr = variants.find(rtype);
418 if( v_itr != variants.end() ) {
419 ctx.hint_variant_type_if_in_array( v_itr );
420 fc::unsigned_int select;
421 try {
422 fc::raw::unpack(stream, select);
423 } SYS_RETHROW_EXCEPTIONS( unpack_exception, "Unable to unpack tag of variant '${p}'", ("p", ctx.get_path_string()) )
424 SYS_ASSERT( (size_t)select < v_itr->second.types.size(), unpack_exception,
425 "Unpacked invalid tag (${select}) for variant '${p}'", ("select", select.value)("p",ctx.get_path_string()) );
426 auto h1 = ctx.push_to_path( impl::variant_path_item{ .variant_itr = v_itr, .variant_ordinal = static_cast<uint32_t>(select) } );
427 return vector<fc::variant>{v_itr->second.types[select], _binary_to_variant(v_itr->second.types[select], stream, ctx)};
428 }
429 }
430
432 _binary_to_variant(rtype, stream, mvo, ctx);
433 // QUESTION: Is this assert actually desired? It disallows unpacking empty structs from datastream.
434 SYS_ASSERT( mvo.size() > 0, unpack_exception, "Unable to unpack '${p}' from stream", ("p", ctx.get_path_string()) );
435 return fc::variant( std::move(mvo) );
436 }
437
438 fc::variant abi_serializer::_binary_to_variant( const std::string_view& type, const bytes& binary, impl::binary_to_variant_context& ctx )const
439 {
440 auto h = ctx.enter_scope();
442 return _binary_to_variant(type, ds, ctx);
443 }
444
445 fc::variant abi_serializer::binary_to_variant( const std::string_view& type, const bytes& binary, const yield_function_t& yield, bool short_path )const {
446 impl::binary_to_variant_context ctx(*this, yield, type);
447 ctx.short_path = short_path;
448 return _binary_to_variant(type, binary, ctx);
449 }
450
451 fc::variant abi_serializer::binary_to_variant( const std::string_view& type, const bytes& binary, const fc::microseconds& max_serialization_time, bool short_path )const {
452 return binary_to_variant( type, binary, create_yield_function(max_serialization_time), short_path );
453 }
454
455 fc::variant abi_serializer::binary_to_variant( const std::string_view& type, fc::datastream<const char*>& binary, const yield_function_t& yield, bool short_path )const {
456 impl::binary_to_variant_context ctx(*this, yield, type);
457 ctx.short_path = short_path;
458 return _binary_to_variant(type, binary, ctx);
459 }
460
461 fc::variant abi_serializer::binary_to_variant( const std::string_view& type, fc::datastream<const char*>& binary, const fc::microseconds& max_serialization_time, bool short_path )const {
462 return binary_to_variant( type, binary, create_yield_function(max_serialization_time), short_path );
463 }
464
465 void abi_serializer::_variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream<char *>& ds, impl::variant_to_binary_context& ctx )const
466 { try {
467 auto h = ctx.enter_scope();
468 auto rtype = resolve_type(type);
469
470 auto v_itr = variants.end();
471 auto s_itr = structs.end();
472
473 auto btype = built_in_types.find(fundamental_type(rtype));
474 if( btype != built_in_types.end() ) {
475 btype->second.second(var, ds, is_array(rtype), is_optional(rtype), ctx.get_yield_function());
476 } else if ( is_array(rtype) ) {
478 vector<fc::variant> vars = var.get_array();
479 fc::raw::pack(ds, (fc::unsigned_int)vars.size());
480
481 auto h1 = ctx.push_to_path( impl::array_index_path_item{} );
482 auto h2 = ctx.disallow_extensions_unless(false);
483
484 int64_t i = 0;
485 for (const auto& var : vars) {
487 _variant_to_binary(fundamental_type(rtype), var, ds, ctx);
488 ++i;
489 }
490 } else if( is_optional(rtype) ) {
491 char flag = !var.is_null();
492 fc::raw::pack(ds, flag);
493 if( flag ) {
494 _variant_to_binary(fundamental_type(rtype), var, ds, ctx);
495 }
496 } else if( (v_itr = variants.find(rtype)) != variants.end() ) {
498 auto& v = v_itr->second;
499 SYS_ASSERT( var.is_array() && var.size() == 2, pack_exception,
500 "Expected input to be an array of two items while processing variant '${p}'", ("p", ctx.get_path_string()) );
501 SYS_ASSERT( var[size_t(0)].is_string(), pack_exception,
502 "Encountered non-string as first item of input array while processing variant '${p}'", ("p", ctx.get_path_string()) );
503 auto variant_type_str = var[size_t(0)].get_string();
504 auto it = find(v.types.begin(), v.types.end(), variant_type_str);
505 SYS_ASSERT( it != v.types.end(), pack_exception,
506 "Specified type '${t}' in input array is not valid within the variant '${p}'",
507 ("t", ctx.maybe_shorten(variant_type_str))("p", ctx.get_path_string()) );
508 fc::raw::pack(ds, fc::unsigned_int(it - v.types.begin()));
509 auto h1 = ctx.push_to_path( impl::variant_path_item{ .variant_itr = v_itr, .variant_ordinal = static_cast<uint32_t>(it - v.types.begin()) } );
510 _variant_to_binary( *it, var[size_t(1)], ds, ctx );
511 } else if( (s_itr = structs.find(rtype)) != structs.end() ) {
512 ctx.hint_struct_type_if_in_array( s_itr );
513 const auto& st = s_itr->second;
514
515 if( var.is_object() ) {
516 const auto& vo = var.get_object();
517
518 if( st.base != type_name() ) {
519 auto h2 = ctx.disallow_extensions_unless(false);
520 _variant_to_binary(resolve_type(st.base), var, ds, ctx);
521 }
522 bool disallow_additional_fields = false;
523 for( uint32_t i = 0; i < st.fields.size(); ++i ) {
524 const auto& field = st.fields[i];
525 if( vo.contains( string(field.name).c_str() ) ) {
526 if( disallow_additional_fields )
527 SYS_THROW( pack_exception, "Unexpected field '${f}' found in input object while processing struct '${p}'",
528 ("f", ctx.maybe_shorten(field.name))("p", ctx.get_path_string()) );
529 {
530 auto h1 = ctx.push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
531 auto h2 = ctx.disallow_extensions_unless( &field == &st.fields.back() );
532 _variant_to_binary(_remove_bin_extension(field.type), vo[field.name], ds, ctx);
533 }
534 } else if( ends_with(field.type, "$") && ctx.extensions_allowed() ) {
535 disallow_additional_fields = true;
536 } else if( disallow_additional_fields ) {
537 SYS_THROW( abi_exception, "Encountered field '${f}' without binary extension designation while processing struct '${p}'",
538 ("f", ctx.maybe_shorten(field.name))("p", ctx.get_path_string()) );
539 } else {
540 SYS_THROW( pack_exception, "Missing field '${f}' in input object while processing struct '${p}'",
541 ("f", ctx.maybe_shorten(field.name))("p", ctx.get_path_string()) );
542 }
543 }
544 } else if( var.is_array() ) {
545 const auto& va = var.get_array();
546 SYS_ASSERT( st.base == type_name(), invalid_type_inside_abi,
547 "Using input array to specify the fields of the derived struct '${p}'; input arrays are currently only allowed for structs without a base",
548 ("p",ctx.get_path_string()) );
549 for( uint32_t i = 0; i < st.fields.size(); ++i ) {
550 const auto& field = st.fields[i];
551 if( va.size() > i ) {
552 auto h1 = ctx.push_to_path( impl::field_path_item{ .parent_struct_itr = s_itr, .field_ordinal = i } );
553 auto h2 = ctx.disallow_extensions_unless( &field == &st.fields.back() );
554 _variant_to_binary(_remove_bin_extension(field.type), va[i], ds, ctx);
555 } else if( ends_with(field.type, "$") && ctx.extensions_allowed() ) {
556 break;
557 } else {
558 SYS_THROW( pack_exception, "Early end to input array specifying the fields of struct '${p}'; require input for field '${f}'",
559 ("p", ctx.get_path_string())("f", ctx.maybe_shorten(field.name)) );
560 }
561 }
562 } else {
563 SYS_THROW( pack_exception, "Unexpected input encountered while processing struct '${p}'", ("p",ctx.get_path_string()) );
564 }
565 } else {
566 SYS_THROW( invalid_type_inside_abi, "Unknown type ${type}", ("type",ctx.maybe_shorten(type)) );
567 }
569
570 bytes abi_serializer::_variant_to_binary( const std::string_view& type, const fc::variant& var, impl::variant_to_binary_context& ctx )const
571 { try {
572 auto h = ctx.enter_scope();
573 if( !_is_type(type, ctx) ) {
574 return var.as<bytes>();
575 }
576
577 bytes temp( 1024*1024 );
578 fc::datastream<char*> ds(temp.data(), temp.size() );
579 _variant_to_binary(type, var, ds, ctx);
580 temp.resize(ds.tellp());
581 return temp;
583
584 bytes abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, const yield_function_t& yield, bool short_path )const {
585 impl::variant_to_binary_context ctx(*this, yield, type);
586 ctx.short_path = short_path;
587 return _variant_to_binary(type, var, ctx);
588 }
589
590 bytes abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, const fc::microseconds& max_serialization_time, bool short_path ) const {
591 return variant_to_binary( type, var, create_yield_function(max_serialization_time), short_path );
592 }
593
594 void abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream<char*>& ds, const yield_function_t& yield, bool short_path )const {
595 impl::variant_to_binary_context ctx(*this, yield, type);
596 ctx.short_path = short_path;
597 _variant_to_binary(type, var, ds, ctx);
598 }
599
600 void abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream<char*>& ds, const fc::microseconds& max_serialization_time, bool short_path ) const {
601 variant_to_binary( type, var, create_yield_function(max_serialization_time), short_path );
602 }
603
605 auto itr = actions.find(action);
606 if( itr != actions.end() ) return itr->second;
607 return type_name();
608 }
610 auto itr = tables.find(action);
611 if( itr != tables.end() ) return itr->second;
612 return type_name();
613 }
614
616 auto itr = action_results.find(action_result);
617 if( itr != action_results.end() ) return itr->second;
618 return type_name();
619 }
620
621 std::optional<string> abi_serializer::get_error_message( uint64_t error_code )const {
622 auto itr = error_messages.find( error_code );
623 if( itr == error_messages.end() )
624 return std::optional<string>();
625
626 return itr->second;
627 }
628
629 namespace impl {
630
632 std::function<void()> callback = [old_recursion_depth=recursion_depth, this](){
633 recursion_depth = old_recursion_depth;
634 };
635
638
639 return {std::move(callback)};
640 }
641
642 void abi_traverse_context_with_path::set_path_root( const std::string_view& type ) {
643 auto rtype = abis.resolve_type(type);
644
645 if( abis.is_array(rtype) ) {
647 } else {
648 auto itr1 = abis.structs.find(rtype);
649 if( itr1 != abis.structs.end() ) {
650 root_of_path = struct_type_path_root{ .struct_itr = itr1 };
651 } else {
652 auto itr2 = abis.variants.find(rtype);
653 if( itr2 != abis.variants.end() ) {
654 root_of_path = variant_type_path_root{ .variant_itr = itr2 };
655 }
656 }
657 }
658 }
659
661 std::function<void()> callback = [this](){
662 SYS_ASSERT( path.size() > 0, abi_exception,
663 "invariant failure in variant_to_binary_context: path is empty on scope exit" );
664 path.pop_back();
665 };
666
667 path.push_back( item );
668
669 return {std::move(callback)};
670 }
671
673 SYS_ASSERT( path.size() > 0, abi_exception, "path is empty" );
674
675 auto& b = path.back();
676
677 SYS_ASSERT( std::holds_alternative<array_index_path_item>(b), abi_exception, "trying to set array index without first pushing new array index item" );
678
679 std::get<array_index_path_item>(b).array_index = i;
680 }
681
683 if( path.size() == 0 || !std::holds_alternative<array_index_path_item>(path.back()) )
684 return;
685
686 std::get<array_index_path_item>(path.back()).type_hint = array_type_path_root{};
687 }
688
689 void abi_traverse_context_with_path::hint_struct_type_if_in_array( const map<type_name, struct_def>::const_iterator& itr ) {
690 if( path.size() == 0 || !std::holds_alternative<array_index_path_item>(path.back()) )
691 return;
692
693 std::get<array_index_path_item>(path.back()).type_hint = struct_type_path_root{ .struct_itr = itr };
694 }
695
696 void abi_traverse_context_with_path::hint_variant_type_if_in_array( const map<type_name, variant_def>::const_iterator& itr ) {
697 if( path.size() == 0 || !std::holds_alternative<array_index_path_item>(path.back()) )
698 return;
699
700 std::get<array_index_path_item>(path.back()).type_hint = variant_type_path_root{ .variant_itr = itr };
701 }
702
703 constexpr size_t const_strlen( const char* str )
704 {
705 return (*str == 0) ? 0 : const_strlen(str + 1) + 1;
706 }
707
708 void output_name( std::ostream& s, const string_view& str, bool shorten, size_t max_length = 64 ) {
709 constexpr size_t min_num_characters_at_ends = 4;
710 constexpr size_t preferred_num_tail_end_characters = 6;
711 constexpr const char* fill_in = "...";
712
713 static_assert( min_num_characters_at_ends <= preferred_num_tail_end_characters,
714 "preferred number of tail end characters cannot be less than the imposed absolute minimum" );
715
716 constexpr size_t fill_in_length = const_strlen( fill_in );
717 constexpr size_t min_length = fill_in_length + 2*min_num_characters_at_ends;
718 constexpr size_t preferred_min_length = fill_in_length + 2*preferred_num_tail_end_characters;
719
720 max_length = std::max( max_length, min_length );
721
722 if( !shorten || str.size() <= max_length ) {
723 s << str;
724 return;
725 }
726
727 size_t actual_num_tail_end_characters = preferred_num_tail_end_characters;
728 if( max_length < preferred_min_length ) {
729 actual_num_tail_end_characters = min_num_characters_at_ends + (max_length - min_length)/2;
730 }
731
732 s.write( str.data(), max_length - fill_in_length - actual_num_tail_end_characters );
733 s.write( fill_in, fill_in_length );
734 s.write( str.data() + (str.size() - actual_num_tail_end_characters), actual_num_tail_end_characters );
735 }
736
738 using result_type = void;
739
743
744 std::stringstream s;
745 bool shorten_names = false;
746 bool track_only = false;
748
749 void add_dot() {
750 s << ".";
751 }
752
753 void operator()( const empty_path_item& item ) {
754 }
755
756 void operator()( const array_index_path_item& item ) {
757 if( track_only ) {
758 last_path_item = item;
759 return;
760 }
761
762 s << "[" << item.array_index << "]";
763 }
764
765 void operator()( const field_path_item& item ) {
766 if( track_only ) {
767 last_path_item = item;
768 return;
769 }
770
771 const auto& str = item.parent_struct_itr->second.fields.at(item.field_ordinal).name;
772 output_name( s, str, shorten_names );
773 }
774
775 void operator()( const variant_path_item& item ) {
776 if( track_only ) {
777 last_path_item = item;
778 return;
779 }
780
781 s << "<variant(" << item.variant_ordinal << ")=";
782 const auto& str = item.variant_itr->second.types.at(item.variant_ordinal);
783 output_name( s, str, shorten_names );
784 s << ">";
785 }
786
787 void operator()( const empty_path_root& item ) {
788 }
789
790 void operator()( const array_type_path_root& item ) {
791 s << "ARRAY";
792 }
793
794 void operator()( const struct_type_path_root& item ) {
795 const auto& str = item.struct_itr->first;
796 output_name( s, str, shorten_names );
797 }
798
799 void operator()( const variant_type_path_root& item ) {
800 const auto& str = item.variant_itr->first;
801 output_name( s, str, shorten_names );
802 }
803 };
804
806 using result_type = void;
807
808 path_item_type_visitor( std::stringstream& s, bool shorten_names )
810 {}
811
812 std::stringstream& s;
813 bool shorten_names = false;
814
815 void operator()( const empty_path_item& item ) {
816 }
817
818 void operator()( const array_index_path_item& item ) {
819 const auto& th = item.type_hint;
820 if( std::holds_alternative<struct_type_path_root>(th) ) {
821 const auto& str = std::get<struct_type_path_root>(th).struct_itr->first;
822 output_name( s, str, shorten_names );
823 } else if( std::holds_alternative<variant_type_path_root>(th) ) {
824 const auto& str = std::get<variant_type_path_root>(th).variant_itr->first;
825 output_name( s, str, shorten_names );
826 } else if( std::holds_alternative<array_type_path_root>(th) ) {
827 s << "ARRAY";
828 } else {
829 s << "UNKNOWN";
830 }
831 }
832
833 void operator()( const field_path_item& item ) {
834 const auto& str = item.parent_struct_itr->second.fields.at(item.field_ordinal).type;
835 output_name( s, str, shorten_names );
836 }
837
838 void operator()( const variant_path_item& item ) {
839 const auto& str = item.variant_itr->second.types.at(item.variant_ordinal);
840 output_name( s, str, shorten_names );
841 }
842 };
843
845 bool full_path = !short_path;
846 bool shorten_names = short_path;
847
848 generate_path_string_visitor visitor(shorten_names, !full_path);
849 if( full_path )
850 std::visit( visitor, root_of_path );
851 for( size_t i = 0, n = path.size(); i < n; ++i ) {
852 if( full_path && !std::holds_alternative<array_index_path_item>(path[i]) )
853 visitor.add_dot();
854
855 std::visit( visitor, path[i] );
856
857 }
858
859 if( !full_path ) {
860 if( std::holds_alternative<empty_path_item>(visitor.last_path_item) ) {
861 std::visit( visitor, root_of_path );
862 } else {
863 path_item_type_visitor vis2(visitor.s, shorten_names);
864 std::visit(vis2, visitor.last_path_item);
865 }
866 }
867
868 return visitor.s.str();
869 }
870
871 string abi_traverse_context_with_path::maybe_shorten( const std::string_view& str ) {
872 if( !short_path )
873 return std::string(str);
874
875 std::stringstream s;
876 output_name( s, str, true );
877 return s.str();
878 }
879
880 string limit_size( const std::string_view& str ) {
881 std::stringstream s;
882 output_name( s, str, false );
883 return s.str();
884 }
885
887 std::function<void()> callback = [old_allow_extensions=allow_extensions, this](){
888 allow_extensions = old_allow_extensions;
889 };
890
891 if( !condition ) {
892 allow_extensions = false;
893 }
894
895 return {std::move(callback)};
896 }
897 }
898
899} }
const mie::Vuint & r
Definition bn.cpp:28
std::string name
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
#define SYS_RETHROW_EXCEPTIONS(exception_type, FORMAT,...)
An order-preserving dictionary of variants.
wraps boost::filesystem::path to provide platform independent path manipulation.
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
Definition variant.hpp:191
bool is_array() const
Definition variant.cpp:368
variant_object & get_object()
Definition variant.cpp:554
const string & get_string() const
Definition variant.cpp:606
bool is_null() const
Definition variant.cpp:309
size_t size() const
Definition variant.cpp:570
T as() const
Definition variant.hpp:327
bool is_object() const
Definition variant.cpp:363
variants & get_array()
Definition variant.cpp:496
bool starts_with(std::string const &str, std::string const &pref)
#define FC_CAPTURE_AND_RETHROW(...)
ehm field
return
RUNTIME_API Runtime::ObjectInstance * find(const std::string &name, const IR::ObjectType &type)
static const Segment ds(Segment::ds)
void unpack(Stream &s, std::deque< T > &value)
Definition raw.hpp:540
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
namespace sysio::chain
Definition authority.cpp:3
std::vector< fc::variant > variants
Definition variant.hpp:173
uint64_t y
Definition sha3.cpp:34
void output_name(std::ostream &s, const string_view &str, bool shorten, size_t max_length=64)
constexpr size_t const_strlen(const char *str)
string limit_size(const std::string_view &str)
limits the string size to default max_length of output_name
std::variant< empty_path_item, array_index_path_item, field_path_item, variant_path_item > path_item
string type_name
Definition abi_def.hpp:7
fc::variant variant_from_stream(fc::datastream< const char * > &stream)
auto pack_unpack_deadline()
vector< char > bytes
Definition types.hpp:243
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
#define T(meth, val, expected)
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
uint32_t value
Definition varint.hpp:17
static constexpr size_t max_recursion_depth
type_name get_action_result_type(name action_result) const
static yield_function_t create_yield_function(const fc::microseconds &max_serialization_time)
void set_abi(const abi_def &abi, const yield_function_t &yield)
type_name get_table_type(name action) const
const struct_def & get_struct(const std::string_view &type) const
int get_integer_size(const std::string_view &type) const
bytes variant_to_binary(const std::string_view &type, const fc::variant &var, const fc::microseconds &max_serialization_time, bool short_path=false) const
std::string_view fundamental_type(const std::string_view &type) const
type_name get_action_type(name action) const
std::optional< string > get_error_message(uint64_t error_code) const
bool is_integer(const std::string_view &type) const
void add_specialized_unpack_pack(const string &name, std::pair< abi_serializer::unpack_function, abi_serializer::pack_function > unpack_pack)
fc::variant binary_to_variant(const std::string_view &type, const bytes &binary, const yield_function_t &yield, bool short_path=false) const
bool is_optional(const std::string_view &type) const
bool is_struct(const std::string_view &type) const
bool is_builtin_type(const std::string_view &type) const
bool is_type(const std::string_view &type, const yield_function_t &yield) const
std::string_view resolve_type(const std::string_view &t) const
bool is_array(const std::string_view &type) const
fc::scoped_exit< std::function< void()> > push_to_path(const path_item &item)
void set_path_root(const std::string_view &type)
void hint_variant_type_if_in_array(const map< type_name, variant_def >::const_iterator &itr)
string maybe_shorten(const std::string_view &str)
void hint_struct_type_if_in_array(const map< type_name, struct_def >::const_iterator &itr)
abi_serializer::yield_function_t yield
abi_serializer::yield_function_t get_yield_function()
fc::scoped_exit< std::function< void()> > enter_scope()
map< type_name, struct_def >::const_iterator parent_struct_itr
void operator()(const variant_type_path_root &item)
void operator()(const empty_path_item &item)
void operator()(const field_path_item &item)
void operator()(const array_index_path_item &item)
void operator()(const variant_path_item &item)
void operator()(const empty_path_root &item)
void operator()(const struct_type_path_root &item)
generate_path_string_visitor(bool shorten_names, bool track_only)
void operator()(const array_type_path_root &item)
void operator()(const field_path_item &item)
path_item_type_visitor(std::stringstream &s, bool shorten_names)
void operator()(const empty_path_item &item)
void operator()(const variant_path_item &item)
void operator()(const array_index_path_item &item)
map< type_name, struct_def >::const_iterator struct_itr
map< type_name, variant_def >::const_iterator variant_itr
fc::scoped_exit< std::function< void()> > disallow_extensions_unless(bool condition)
map< type_name, variant_def >::const_iterator variant_itr
Immutable except for fc::from_variant.
Definition name.hpp:43
yh_object_type type
Definition yubihsm.h:672
char * s