From 4c1be978536cd4058790f836f3a2809bc1248915 Mon Sep 17 00:00:00 2001 From: abenso Date: Fri, 8 Nov 2024 12:54:01 -0300 Subject: [PATCH] parse spend plan and clean code --- app/src/c_api/rust.c | 2 +- app/src/common/parser_common.h | 1 - app/src/parser_impl.c | 173 ++++----------------------------- app/src/parser_impl.h | 6 -- app/src/parser_pb_utils.c | 73 +++++++++++--- app/src/parser_pb_utils.h | 19 ++-- app/src/parser_txdef.h | 3 +- app/src/spend_plan.c | 24 +++-- app/src/spend_plan.h | 10 +- tests/parser_impl.cpp | 40 ++++---- 10 files changed, 126 insertions(+), 225 deletions(-) diff --git a/app/src/c_api/rust.c b/app/src/c_api/rust.c index a6a0f973..34946060 100644 --- a/app/src/c_api/rust.c +++ b/app/src/c_api/rust.c @@ -8,7 +8,7 @@ zxerr_t crypto_extractSpendingKeyBytes(uint8_t *key_bytes, uint32_t key_bytes_le // Function to compute BLAKE2b hash with personalization zxerr_t blake2b_hash_with_personalization(const uint8_t *input, size_t input_len, uint8_t *output, size_t output_len, - const uint8_t *label, size_t label_len) { + const uint8_t *label, size_t label_len) { cx_blake2b_t hash_context; // unsigned char *salt = NULL; diff --git a/app/src/common/parser_common.h b/app/src/common/parser_common.h index 2f5ef533..3e2b700f 100644 --- a/app/src/common/parser_common.h +++ b/app/src/common/parser_common.h @@ -31,7 +31,6 @@ extern "C" { if (__err != parser_ok) return __err; \ } - // Convert bytes to uint32_t, // assume data is in BE format #define U32_BE(buffer, number) \ diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 24bb2728..221a2b6d 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -17,17 +17,15 @@ #include "parser_impl.h" #include "parser_interface.h" +#include "parser_pb_utils.h" #include "pb_common.h" #include "pb_decode.h" #include "protobuf/penumbra/core/transaction/v1/transaction.pb.h" +#include "spend_plan.h" #include "zxformat.h" -static bool decode_field(pb_istream_t *stream, const pb_field_t *field, void **arg); static bool decode_action(pb_istream_t *stream, const pb_field_t *field, void **arg); static bool decode_detection_data(pb_istream_t *stream, const pb_field_t *field, void **arg); -static void setup_decode_field(pb_callback_t *callback, decode_memo_field_arg_t *arg, Bytes_t *bytes, uint16_t expected_size, - bool check_size); -static parser_error_t extract_data_from_tag(Bytes_t *in, Bytes_t *out, uint32_t tag); static uint16_t actions_qty = 0; static uint16_t detection_data_qty = 0; @@ -55,38 +53,9 @@ void print_string(const char *str) { #endif } -bool decode_field(pb_istream_t *stream, const pb_field_t *field, void **arg) { - if (stream->bytes_left == 0 || arg == NULL) return false; - - decode_memo_field_arg_t *decode_arg = (decode_memo_field_arg_t *)*arg; - if (decode_arg == NULL || decode_arg->bytes == NULL) { - return false; - } - - if (decode_arg->check_size && stream->bytes_left != decode_arg->expected_size) { - return false; - } - - const uint8_t *first_byte = stream->state; - uint16_t data_size = stream->bytes_left; - - decode_arg->bytes->ptr = first_byte; - decode_arg->bytes->len = data_size; - - return true; -} - -void setup_decode_field(pb_callback_t *callback, decode_memo_field_arg_t *arg, Bytes_t *bytes, uint16_t expected_size, - bool check_size) { - arg->bytes = bytes; - arg->expected_size = expected_size; - arg->check_size = check_size; - callback->funcs.decode = &decode_field; - callback->arg = arg; -} - bool decode_action(pb_istream_t *stream, const pb_field_t *field, void **arg) { penumbra_core_transaction_v1_ActionPlan action = penumbra_core_transaction_v1_ActionPlan_init_default; + spend_plan_t spend_plan; action_t *decode_arg = (action_t *)*arg; if (decode_arg == NULL) { @@ -99,8 +68,8 @@ bool decode_action(pb_istream_t *stream, const pb_field_t *field, void **arg) { const uint8_t *first_byte = stream->state; uint16_t data_size = stream->bytes_left; - decode_arg[actions_qty].action.ptr = first_byte + 2; - decode_arg[actions_qty].action.len = data_size - 2; + decode_arg[actions_qty].action.ptr = first_byte + 3; + decode_arg[actions_qty].action.len = data_size - 3; if (!pb_decode(stream, penumbra_core_transaction_v1_ActionPlan_fields, &action)) { return false; @@ -108,6 +77,7 @@ bool decode_action(pb_istream_t *stream, const pb_field_t *field, void **arg) { decode_arg[actions_qty].action_type = action.which_action; switch (action.which_action) { case penumbra_core_transaction_v1_ActionPlan_spend_tag: + decode_spend_plan(&decode_arg[actions_qty].action, &spend_plan); print_string("Spend action detected \n"); break; case penumbra_core_transaction_v1_ActionPlan_output_tag: @@ -145,14 +115,15 @@ bool decode_detection_data(pb_istream_t *stream, const pb_field_t *field, void * } penumbra_core_transaction_v1_CluePlan cluePlan = penumbra_core_transaction_v1_CluePlan_init_default; - decode_memo_field_arg_t rseed_arg, address_inner_arg, address_alt_bech32m_arg; + fixed_size_field_t rseed_arg, address_inner_arg; + variable_size_field_t address_alt_bech32m_arg; clue_plan_t *clue_plan_arg = (clue_plan_t *)*arg; - setup_decode_field(&cluePlan.rseed, &rseed_arg, &clue_plan_arg[detection_data_qty].rseed, RSEED_SIZE, true); - setup_decode_field(&cluePlan.address.inner, &address_inner_arg, &clue_plan_arg[detection_data_qty].address.inner, - MEMO_ADDRESS_INNER_SIZE, true); - setup_decode_field(&cluePlan.address.alt_bech32m, &address_alt_bech32m_arg, - &clue_plan_arg[detection_data_qty].address.alt_bech32m, 0, false); + setup_decode_fixed_field(&cluePlan.rseed, &rseed_arg, &clue_plan_arg[detection_data_qty].rseed, RSEED_SIZE); + setup_decode_fixed_field(&cluePlan.address.inner, &address_inner_arg, &clue_plan_arg[detection_data_qty].address.inner, + MEMO_ADDRESS_INNER_SIZE); + setup_decode_variable_field(&cluePlan.address.alt_bech32m, &address_alt_bech32m_arg, + &clue_plan_arg[detection_data_qty].address.alt_bech32m); if (!pb_decode(stream, penumbra_core_transaction_v1_CluePlan_fields, &cluePlan)) { return false; @@ -164,109 +135,6 @@ bool decode_detection_data(pb_istream_t *stream, const pb_field_t *field, void * return true; } -parser_error_t extract_data_from_tag(Bytes_t *in, Bytes_t *out, uint32_t tag) { - const uint8_t *start = NULL; - const uint8_t *end = NULL; - bool eof = false; - - pb_istream_t scan_stream = pb_istream_from_buffer(in->ptr, in->len); - pb_wire_type_t wire_type; - uint32_t tag_internal; - while (pb_decode_tag(&scan_stream, &wire_type, &tag_internal, &eof) && !eof) { - if (tag_internal == tag) { - start = scan_stream.state; - if (!pb_skip_field(&scan_stream, wire_type)) { - return parser_unexpected_error; - } - end = scan_stream.state; - break; - } else { - if (!pb_skip_field(&scan_stream, wire_type)) { - return parser_unexpected_error; - } - } - } - - if (!start || !end) { - return parser_unexpected_error; - } - - out->ptr = start + 1; - out->len = end - start - 1; - - return parser_ok; -} - -// parser_error_t extract_data_array_from_tag(Bytes_t *in, Bytes_t out[], uint32_t tag) { -// const uint8_t *start = NULL; -// const uint8_t *end = NULL; -// bool eof = false; - -// pb_istream_t scan_stream = pb_istream_from_buffer(in->ptr, in->len); -// pb_wire_type_t wire_type; -// uint32_t tag_internal; -// size_t out_index = 0; - -// while (pb_decode_tag(&scan_stream, &wire_type, &tag_internal, &eof) && !eof) { -// if (tag_internal == tag) { -// start = scan_stream.state; -// if (!pb_skip_field(&scan_stream, wire_type)) { -// return parser_unexpected_error; -// } -// end = scan_stream.state; - -// if (!start || !end) { -// return parser_unexpected_error; -// } - -// out[out_index].ptr = start + 1; -// out[out_index].len = end - start - 1; -// out_index++; -// } else { -// if (!pb_skip_field(&scan_stream, wire_type)) { -// return parser_unexpected_error; -// } -// } -// } - -// return parser_ok; -// } - -// parser_error_t compute_detection_data(Bytes_t *detection_data, parser_tx_t *v) { -// uint16_t detection_data_bytes = DETECTION_DATA_SIZE; -// uint16_t clue_plans = detection_data->len / detection_data_bytes; - -// if (clue_plans > 1) { -// if ((detection_data->ptr[0] + 1 == clue_plans) || (clue_plans * detection_data_bytes + 1 == detection_data->len)) -// { -// for (uint16_t i = 0; i < clue_plans; i++) { -// v->plan.detection_data.clue_plans[i].ptr = detection_data->ptr + (i * detection_data_bytes + 1); -// v->plan.detection_data.clue_plans[i].len = DETECTION_DATA_SIZE; -// } -// } else { -// return parser_unexpected_error; -// } -// } else { -// v->plan.detection_data.clue_plans[0].ptr = detection_data->ptr; -// v->plan.detection_data.clue_plans[0].len = detection_data->len; -// } - -// return parser_ok; -// } - -// parser_error_t compute_actions(Bytes_t actions[], parser_tx_t *v) { -// for (uint16_t i = 0; i < ACTIONS_QTY; i++) { -// if (actions[i].len > 2) { -// v->plan.actions[i].action_type = actions[i].ptr[1] >> 3; -// // TODO: check if we have to parser each action -// v->plan.actions[i].action.ptr = actions[i].ptr + 2; -// v->plan.actions[i].action.len = actions[i].len - 2; -// } -// } - -// return parser_ok; -// } - parser_error_t _read(parser_context_t *c, parser_tx_t *v) { Bytes_t data; data.ptr = c->buffer; @@ -275,15 +143,16 @@ parser_error_t _read(parser_context_t *c, parser_tx_t *v) { detection_data_qty = 0; penumbra_core_transaction_v1_TransactionPlan request = penumbra_core_transaction_v1_TransactionPlan_init_default; - decode_memo_field_arg_t memo_key_arg, memo_text_arg, memo_return_address_inner_arg, memo_return_address_alt_bech32m_arg; + fixed_size_field_t memo_key_arg, memo_return_address_inner_arg; + variable_size_field_t memo_text_arg, memo_return_address_alt_bech32m_arg; // memo callbacks - setup_decode_field(&request.memo.key, &memo_key_arg, &v->plan.memo.key, MEMO_KEY_SIZE, true); - setup_decode_field(&request.memo.plaintext.text, &memo_text_arg, &v->plan.memo.plaintext.text, 0, false); - setup_decode_field(&request.memo.plaintext.return_address.inner, &memo_return_address_inner_arg, - &v->plan.memo.plaintext.return_address.inner, MEMO_ADDRESS_INNER_SIZE, true); - setup_decode_field(&request.memo.plaintext.return_address.alt_bech32m, &memo_return_address_alt_bech32m_arg, - &v->plan.memo.plaintext.return_address.alt_bech32m, 0, false); + setup_decode_fixed_field(&request.memo.key, &memo_key_arg, &v->plan.memo.key, MEMO_KEY_SIZE); + setup_decode_variable_field(&request.memo.plaintext.text, &memo_text_arg, &v->plan.memo.plaintext.text); + setup_decode_fixed_field(&request.memo.plaintext.return_address.inner, &memo_return_address_inner_arg, + &v->plan.memo.plaintext.return_address.inner, MEMO_ADDRESS_INNER_SIZE); + setup_decode_variable_field(&request.memo.plaintext.return_address.alt_bech32m, &memo_return_address_alt_bech32m_arg, + &v->plan.memo.plaintext.return_address.alt_bech32m); // actions callbacks request.actions.funcs.decode = &decode_action; diff --git a/app/src/parser_impl.h b/app/src/parser_impl.h index e55d3641..dc5435b4 100644 --- a/app/src/parser_impl.h +++ b/app/src/parser_impl.h @@ -26,12 +26,6 @@ extern "C" { #endif -typedef struct { - Bytes_t *bytes; - uint16_t expected_size; - bool check_size; -} decode_memo_field_arg_t; - parser_error_t _read(parser_context_t *c, parser_tx_t *v); #ifdef __cplusplus diff --git a/app/src/parser_pb_utils.c b/app/src/parser_pb_utils.c index 9f48bd1a..13d2ddfc 100644 --- a/app/src/parser_pb_utils.c +++ b/app/src/parser_pb_utils.c @@ -14,41 +14,86 @@ * limitations under the License. ********************************************************************************/ -#include "parser_impl.h" +#include "parser_pb_utils.h" +#include "parser_impl.h" #include "parser_interface.h" #include "zxformat.h" -#include "parser_pb_utils.h" - -bool decode_fixed_field(pb_istream_t *stream, const pb_field_t *field, void **arg) -{ - if (stream->bytes_left == 0 || arg == NULL) - return false; +bool decode_fixed_field(pb_istream_t *stream, const pb_field_t *field, void **arg) { + if (stream->bytes_left == 0 || arg == NULL) return false; fixed_size_field_t *decode_arg = (fixed_size_field_t *)*arg; if (decode_arg == NULL || decode_arg->bytes == NULL) { return false; } - if (decode_arg->check_size && stream->bytes_left != decode_arg->expected_size) { + if (stream->bytes_left != decode_arg->expected_size) { return false; } - const uint8_t *first_byte = stream->state; - uint16_t data_size = stream->bytes_left; + decode_arg->bytes->ptr = stream->state; + decode_arg->bytes->len = stream->bytes_left; + + return true; +} + +bool decode_variable_field(pb_istream_t *stream, const pb_field_t *field, void **arg) { + if (stream->bytes_left == 0 || arg == NULL) return false; + + variable_size_field_t *decode_arg = (variable_size_field_t *)*arg; + if (decode_arg == NULL || decode_arg->bytes == NULL) { + return false; + } - decode_arg->bytes->ptr = first_byte; - decode_arg->bytes->len = data_size; + decode_arg->bytes->ptr = stream->state; + decode_arg->bytes->len = stream->bytes_left; return true; } -void setup_decode_fixed_field(pb_callback_t *callback, fixed_size_field_t *arg, Bytes_t *bytes, uint16_t expected_size, bool check_size) { +void setup_decode_fixed_field(pb_callback_t *callback, fixed_size_field_t *arg, Bytes_t *bytes, uint16_t expected_size) { arg->bytes = bytes; arg->expected_size = expected_size; - arg->check_size = check_size; callback->funcs.decode = &decode_fixed_field; callback->arg = arg; } +void setup_decode_variable_field(pb_callback_t *callback, variable_size_field_t *arg, Bytes_t *bytes) { + arg->bytes = bytes; + callback->funcs.decode = &decode_variable_field; + callback->arg = arg; +} + +parser_error_t extract_data_from_tag(Bytes_t *in, Bytes_t *out, uint32_t tag) { + const uint8_t *start = NULL; + const uint8_t *end = NULL; + bool eof = false; + + pb_istream_t scan_stream = pb_istream_from_buffer(in->ptr, in->len); + pb_wire_type_t wire_type; + uint32_t tag_internal; + while (pb_decode_tag(&scan_stream, &wire_type, &tag_internal, &eof) && !eof) { + if (tag_internal == tag) { + start = scan_stream.state; + if (!pb_skip_field(&scan_stream, wire_type)) { + return parser_unexpected_error; + } + end = scan_stream.state; + break; + } else { + if (!pb_skip_field(&scan_stream, wire_type)) { + return parser_unexpected_error; + } + } + } + + if (!start || !end) { + return parser_unexpected_error; + } + + out->ptr = start + 1; + out->len = end - start - 1; + + return parser_ok; +} \ No newline at end of file diff --git a/app/src/parser_pb_utils.h b/app/src/parser_pb_utils.h index 01d678c1..e1ffb721 100644 --- a/app/src/parser_pb_utils.h +++ b/app/src/parser_pb_utils.h @@ -19,36 +19,37 @@ extern "C" { #endif +#include #include #include - #include -#include #include "parser_common.h" #include "parser_txdef.h" -#include "zxtypes.h" - #include "pb_common.h" #include "pb_decode.h" #include "protobuf/penumbra/core/transaction/v1/transaction.pb.h" +#include "zxtypes.h" typedef struct { Bytes_t *bytes; uint16_t expected_size; - bool check_size; } fixed_size_field_t; +typedef struct { + Bytes_t *bytes; +} variable_size_field_t; + // Callback to parse binding fields in spend plans. all those fields are just // 32-bytes data array that later in rust can be converted into // Fq, Fr types bool decode_fixed_field(pb_istream_t *stream, const pb_field_t *field, void **arg); +bool decode_variable_field(pb_istream_t *stream, const pb_field_t *field, void **arg); -void setup_decode_fixed_field(pb_callback_t *callback, fixed_size_field_t *arg, Bytes_t *bytes, uint16_t expected_size, bool check_size); - +void setup_decode_fixed_field(pb_callback_t *callback, fixed_size_field_t *arg, Bytes_t *bytes, uint16_t expected_size); +void setup_decode_variable_field(pb_callback_t *callback, variable_size_field_t *arg, Bytes_t *bytes); +parser_error_t extract_data_from_tag(Bytes_t *in, Bytes_t *out, uint32_t tag); #ifdef __cplusplus } #endif - - diff --git a/app/src/parser_txdef.h b/app/src/parser_txdef.h index 5d570e7b..5b28627e 100644 --- a/app/src/parser_txdef.h +++ b/app/src/parser_txdef.h @@ -32,7 +32,7 @@ extern "C" { #define ACTIONS_QTY 16 #define ASSET_ID_LEN 32 -#define RSEED_LEN 32 +#define RSEED_LEN 32 typedef struct { const uint8_t *ptr; @@ -76,7 +76,6 @@ typedef struct { Bytes_t proof_blinding_s; } spend_plan_t; - typedef struct { Bytes_t parameters; } transaction_parameters_t; diff --git a/app/src/spend_plan.c b/app/src/spend_plan.c index 8bc78b7d..a3e6cbef 100644 --- a/app/src/spend_plan.c +++ b/app/src/spend_plan.c @@ -16,36 +16,34 @@ #include "parser_impl.h" #include "parser_interface.h" +#include "parser_pb_utils.h" #include "pb_common.h" #include "pb_decode.h" #include "protobuf/penumbra/core/transaction/v1/transaction.pb.h" - -#include "parser_interface.h" #include "zxformat.h" -#include "parser_pb_utils.h" -parser_error_t decode_spend_plan(const Bytes_t *data, spend_plan_t *output) { - penumbra_core_component_shielded_pool_v1_SpendPlan spend_plan = penumbra_core_component_shielded_pool_v1_SpendPlan_init_default; +parser_error_t decode_spend_plan(const Bytes_t *data, spend_plan_t *output) { + penumbra_core_component_shielded_pool_v1_SpendPlan spend_plan = + penumbra_core_component_shielded_pool_v1_SpendPlan_init_default; pb_istream_t spend_stream = pb_istream_from_buffer(data->ptr, data->len); CHECK_APP_CANARY() - // Set up fixed size fields fixed_size_field_t randomizer_arg, value_blinding_arg, proof_blinding_r_arg, proof_blinding_s_arg; - setup_decode_fixed_field(&spend_plan.randomizer, &randomizer_arg, &output->randomizer, 32, true); - setup_decode_fixed_field(&spend_plan.value_blinding, &value_blinding_arg, &output->value_blinding, 32, true); - setup_decode_fixed_field(&spend_plan.proof_blinding_r, &proof_blinding_r_arg, &output->proof_blinding_r, 32, true); - setup_decode_fixed_field(&spend_plan.proof_blinding_s, &proof_blinding_s_arg, &output->proof_blinding_s, 32, true); - setup_decode_fixed_field(&spend_plan.proof_blinding_s, &proof_blinding_s_arg, &output->proof_blinding_s, 32, true); + setup_decode_fixed_field(&spend_plan.randomizer, &randomizer_arg, &output->randomizer, 32); + setup_decode_fixed_field(&spend_plan.value_blinding, &value_blinding_arg, &output->value_blinding, 32); + setup_decode_fixed_field(&spend_plan.proof_blinding_r, &proof_blinding_r_arg, &output->proof_blinding_r, 32); + setup_decode_fixed_field(&spend_plan.proof_blinding_s, &proof_blinding_s_arg, &output->proof_blinding_s, 32); // asset_id in Note fixed_size_field_t asset_id_arg; - setup_decode_fixed_field(&spend_plan.note.value.asset_id.inner, &asset_id_arg, &output->note.value.asset_id.inner, ASSET_ID_LEN, true); + setup_decode_fixed_field(&spend_plan.note.value.asset_id.inner, &asset_id_arg, &output->note.value.asset_id.inner, + ASSET_ID_LEN); // rseed in Note fixed_size_field_t rseed_arg; - setup_decode_fixed_field(&spend_plan.note.rseed, &rseed_arg, &output->note.rseed, RSEED_LEN, true); + setup_decode_fixed_field(&spend_plan.note.rseed, &rseed_arg, &output->note.rseed, RSEED_LEN); if (!pb_decode(&spend_stream, penumbra_core_component_shielded_pool_v1_SpendPlan_fields, &spend_plan)) { return parser_spend_plan_error; diff --git a/app/src/spend_plan.h b/app/src/spend_plan.h index 61d70bfc..84dbb445 100644 --- a/app/src/spend_plan.h +++ b/app/src/spend_plan.h @@ -15,27 +15,23 @@ ********************************************************************************/ #pragma once +#include #include #include - #include -#include #include "parser_common.h" #include "parser_txdef.h" -#include "zxtypes.h" - #include "pb_common.h" #include "pb_decode.h" +#include "zxtypes.h" #ifdef __cplusplus extern "C" { #endif -parser_error_t decode_spend_plan(const Bytes_t *input, spend_plan_t *spend_plan); - +parser_error_t decode_spend_plan(const Bytes_t *input, spend_plan_t *spend_plan); #ifdef __cplusplus } #endif - diff --git a/tests/parser_impl.cpp b/tests/parser_impl.cpp index 7cb00e40..622d3c68 100644 --- a/tests/parser_impl.cpp +++ b/tests/parser_impl.cpp @@ -36,34 +36,34 @@ TEST(SCALE, ReadBytes) { uint8_t buffer[6000]; auto bufferLen = parseHexString( buffer, sizeof(buffer), - "0aaf020aac020aa1010a290a0308904e12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a1012207fa50bf" - "e8946e53b33e8328672a0c6300ad9333c04707475f2d6cc502ec8513a1a520a50e0783360338067fc2ba548f460b3f06f33d3e756ebefa8a8c0" + "0aaf020aac020aa1010a290a0308954e12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a101220c923f2f" + "89ccea2aafe13dca34d083faf4894f156f604dc3d4817bf6717096e331a520a50e0783360338067fc2ba548f460b3f06f33d3e756ebefa8a8c0" "8c5e12a1e667df228df0720fb9bd963894183bc447e1c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891d691a208" - "d0887fd0d20bd002c336054772d56b8f9704a80ac8da45533e72f155f7145032220d1241d9d9f051c437455998e7c1eff33cd085ba9a9092d85" - "2d0381278bb6b1012a205e3505d5b6b1b99a8284f054bd650e8d4b64a23f6322c05a0ccee46bb481cb0932208c6abfdd0dd9111868fbed6a30c" - "9dd5477b123f7aefd15bc73b68185f0a036110ab2020aaf020aa2010a2a0a0408a09c0112220a2029ea9c2f3371f6a487e7e95c247041f4a356" - "f983eb064e5d2b3bcf322ca96a10122079ce0fe2f6695ebe61c22edb8de45e1290448500e0058156cb4e29f4d7234f091a520a50e0783360338" + "0b306db5db558a2d0cd19ad587c1f28b5b9f8139901ade057cba921fc76c7032220bf3a56c1b6236f007ea136ad1fb0735e03e4ccae3a5c93af" + "28326485e3bb65002a20f7298845c66cba673959f9d3ca44d0831c90cd925c92afc1cf5299185a3f120b32202d700f646468f7434f34adc9854" + "d8e8e5b8bb86d35f343db0fd8126ce49ad6030ab2020aaf020aa2010a2a0a0408a09c0112220a2029ea9c2f3371f6a487e7e95c247041f4a356" + "f983eb064e5d2b3bcf322ca96a1012204988c0704b1c5beb92aa58168d75e32e8edbd60c2e7ec470bc50440f1a21f3eb1a520a50e0783360338" "067fc2ba548f460b3f06f33d3e756ebefa8a8c08c5e12a1e667df228df0720fb9bd963894183bc447e1c7ef591fa9625d4a66b7703eec2ec1ef" - "543454673bb61a4f2a3d861114d6891d6910011a20c3021eb39eed577b5d609f5436d9128ff6c8bcc13e326a4fcab5c749e2f489002220c05b9" - "ffb6512d40cc8cf03eb1e2b47f3fcc323a0f2db077362b2300eb77658022a20a45a2fc5a64032c3a68ce4ec9c542b564cfaa9b14d3af386bd14" - "2a645212540f32208cb5d93be25ba643ee760f56624cd051e53ca0e7047e4a95a5faf82f0636f60d0a8b021288020a2a0a0408b0ea0112220a2" + "543454673bb61a4f2a3d861114d6891d6910011a205550f06f9c3ba695c9bab8f317cc4bcd7f62e90f80de421ae70161678cc4200322200e932" + "66e84a34cce2bd5b6021d78226e68f6f760b688765ddbc19b451ced11022a2069e48038c64d148608a27e9846ff74a85b0bc86188f3e5ca037c" + "20b16476ff0032209ee9410d61a376540f140f88d38b0785f8c7852bd11512ea92281003aad805030a8b021288020a2a0a0408b0ea0112220a2" "029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a1012520a50e0783360338067fc2ba548f460b3f06f33d3e756eb" "efa8a8c08c5e12a1e667df228df0720fb9bd963894183bc447e1c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891" - "d691a20cde64106162059a6800918036eb3c0e9b2872f56d4902259fba9b52d18dbd7c52220a1417029703aa865b08f19acc96eba15928a2178" - "956f6c9c538ea5577a03ef032a20df5538e38602a2bd15119182c50d0f1b22fd8deba0a728a8fbbfb7e9b8c6fc0932207dee2a1d6d9c85ba685" - "60cfa514b73897e77fe0355d92c9cc2bb3b522b5f3d110abc021ab9020ad0010a480a220a20116d0cd6de9349c686f5802f7c98179e70d4898a" + "d691a20cee6fb736d421932e6c2b895ae1a0f65b6769ddbf96dfec8d0965d1487549e6622205994ad1abed78f6b5d48fc72eb114742d1b04f94" + "a842621897eca948beb91c002a206c5beeab2d967b5347e002a7e453abee0ee9d9f0239fbc855c05502cdca459113220df0cd5b18b09548339a" + "7d13e8fcdb6a5a8f0dc7fc8a587aa74ee746df2bba1100abc021ab9020ad0010a480a220a20116d0cd6de9349c686f5802f7c98179e70d4898a" "38e1cc3df93705c94e941d0c12220a2029ea9c2f3371f6a487e7e95c247041f4a356f983eb064e5d2b3bcf322ca96a10120408a08d061a02080" "122040a0208032a520a50e0783360338067fc2ba548f460b3f06f33d3e756ebefa8a8c08c5e12a1e667df228df0720fb9bd963894183bc447e1" - "c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891d69322057798bc1a097c26b47e61a9870ca98eb8cbe48e9cba4d" - "b86f5d8b67c12086afb1220fbf22b3005fdd84a03bdf0950c851a9c6e0618b0eea1004ca75e53ce3deef1031a205f9ddbd8093a028de6597d03" - "d785302d8336e20a2148e15ae016bad0536d7e0122200d358f3968b24d5e612504b211cadb44ace485ceb3ad38821aa96b926279e6001213120" + "c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891d69322096f795c0f70ae89247c62febde1488f3673b0fd48e035" + "17b60b17fcc375d2fa912202efb6351ce1af7c5612481ad4660fd184db5e73908224455b50d7fe79f2bdf001a20a4cfed973f3f025070f6de90" + "d2a2ea1336922cce1fbf0f1be443554e9652770a222081dc04672db59a2d011d82aa980a50012de22048b59f719e626f7c15b64a2e111213120" "d70656e756d6272612d746573741a020a0022f4012a780a520a50e0783360338067fc2ba548f460b3f06f33d3e756ebefa8a8c08c5e12a1e667" - "df228df0720fb9bd963894183bc447e1c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891d691220361218d216cfe" - "90f77f54f045ff21b464795517c05057c595fd59e4958e3941718032a780a520a50e0783360338067fc2ba548f460b3f06f33d3e756ebefa8a8" + "df228df0720fb9bd963894183bc447e1c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891d69122083f70f7faa82e" + "24925010fd9fd248fb2bf16f96fb3d1f2aa984696b4f91fbc8d18032a780a520a50e0783360338067fc2ba548f460b3f06f33d3e756ebefa8a8" "c08c5e12a1e667df228df0720fb9bd963894183bc447e1c7ef591fa9625d4a66b7703eec2ec1ef543454673bb61a4f2a3d861114d6891d69122" - "013296da8c9dfdf969be7c7bd74e67e80977cd91635eb32038619f62c732dc46a18022a780a540a520a506ece16f387e0b932082cb0cf682359" - "0fc287d068d6f684a36d1fb19bfd6dce8b22850f535824aeb66cb8c41309e6f5b2d58ff7b651ef4e09a09c7e48d770d190880e1827b47823a1d" - "01f0c4b438a7b43122018bd5cedd0eb952244a296c1e3fba4f417ebdcc1cfec04cb9441a394316a58bd"); + "09ce6c8171c50847dffc5112fde5e1245fa1e561ea4ef2b0259ee3443fb9b26eb18022a780a540a520a50a39eff5d22636e107f0d0797fe9d89" + "d79c98326fe640f8ef8dbe738148555c6d9f9bfc4059a190226bca124981087b604539638a0b16fd0a3ea73d47bb40604a02607ab23ffe910f6" + "36ba4b307db3cde1220a19b520a3749f7511bb02e0eed2e51fb2a6019920cb9dc7ba3f41906c91305c1"); parser_parse(&ctx, buffer, bufferLen, &tx_obj); }