diff --git a/c/csrc/btcspv.c b/c/csrc/btcspv.c index 0279c25c..3e12c653 100644 --- a/c/csrc/btcspv.c +++ b/c/csrc/btcspv.c @@ -5,7 +5,6 @@ const uint64_t BTCSPV_ERR_BAD_ARG = 0xffffffff; - // Order matters bool btcspv_truncated_uint256_equality(const uint8_t *trun, const uint8_t *full) { @@ -19,7 +18,7 @@ bool btcspv_truncated_uint256_equality(const uint8_t *trun, // equality for 2 buffers bool btcspv_buf_eq(const uint8_t *loc1, uint32_t len1, const uint8_t *loc2, - uint32_t len2) { + uint32_t len2) { if (len1 != len2) { return false; } @@ -43,7 +42,7 @@ void btcspv_buf_rev(uint8_t *to, const uint8_t *from, uint32_t len) { } } -uint8_t btcspv_determine_var_int_data_length(uint8_t tag) { +uint8_t btcspv_determine_compact_int_data_length(uint8_t tag) { switch (tag) { case 0xfd: return 2; @@ -56,26 +55,44 @@ uint8_t btcspv_determine_var_int_data_length(uint8_t tag) { } } -var_int_t btcspv_parse_var_int(const_view_t *b) { - uint64_t data_length = btcspv_determine_var_int_data_length(b->loc[0]); - if (data_length == 0) { - var_int_t result = {.var_int_len = 0, .number = b->loc[0]}; - return result; +uint8_t btcspv_compact_int_length(uint64_t number) { + if (number <= 0xfc) { + return 1; + } else if (number <= 0xffff) { + return 3; + } else if (number <= 0xffffffff) { + return 5; + } else { + return 9; } +} - if (b->len < 1 + data_length) { - var_int_t result = {.var_int_len = BTCSPV_ERR_BAD_ARG, .number = 0}; - return result; +bool btcspv_parse_compact_int(uint64_t *result, const uint8_t *loc, + uint32_t len) { + if (len == 0) { + return false; } - - const_view_t payload = {.loc = b->loc + 1, .len = data_length}; - uint64_t number = 0; + uint64_t data_length = btcspv_determine_compact_int_data_length(loc[0]); + if (data_length == 0) { + *result = loc[0]; + return true; + } + if (len < 1 + data_length) { + return false; + } + const_view_t payload = {.loc = loc + 1, .len = data_length}; + *result = 0; // lazy varlength LE bytes->uint for (uint8_t i = 0; i < data_length; i++) { - number += payload.loc[i] * (0x01 << i * 8); + *result += payload.loc[i] * (0x01 << i * 8); + } + + // forbid non-minimal + if (btcspv_compact_int_length(*result) != data_length) { + *result = 0; + return false; } - var_int_t result = {.var_int_len = data_length, .number = number}; - return result; + return true; } // @@ -115,81 +132,90 @@ void btcspv_hash256(uint8_t *result, const_view_t *preimage) { // Input Functions // -bool btcspv_is_legacy_input(const_view_t *tx_in) { +bool btcspv_is_legacy_input(const_txin_t *tx_in) { return (tx_in->loc)[36] != 0; } -byte_view_t btcspv_extract_sequence_le_witness(const_view_t *tx_in) { +byte_view_t btcspv_extract_sequence_le_witness(const_txin_t *tx_in) { byte_view_t seq = {tx_in->loc + 37, 4}; return seq; } -uint32_t btcspv_extract_sequence_witness(const_view_t *tx_in) { +uint32_t btcspv_extract_sequence_witness(const_txin_t *tx_in) { const_view_t seq = btcspv_extract_sequence_le_witness(tx_in); return AS_LE_UINT32(seq.loc); } -script_sig_t btcspv_extract_script_sig_len(const_view_t *tx_in) { +bool btcspv_extract_script_sig_len(uint64_t *result, const_txin_t *tx_in) { if (tx_in->len < 37) { - script_sig_t result = {.var_int_len = BTCSPV_ERR_BAD_ARG, .script_sig_len = 0}; - return result; + return false; } - const_view_t after_outpoint = {.loc = tx_in->loc + 36, .len = tx_in->len - 36}; - - var_int_t var_int = btcspv_parse_var_int(&after_outpoint); + const_view_t after_outpoint = {.loc = tx_in->loc + 36, + .len = tx_in->len - 36}; - script_sig_t res = {.var_int_len = var_int.var_int_len, .script_sig_len = var_int.number}; - return res; + bool success = + btcspv_parse_compact_int(result, after_outpoint.loc, after_outpoint.len); + return success; } -byte_view_t btcspv_extract_script_sig(const_view_t *tx_in) { - const script_sig_t ss = btcspv_extract_script_sig_len(tx_in); - uint32_t length = 1 + ss.var_int_len + ss.script_sig_len; +scriptsig_t btcspv_extract_script_sig(const_txin_t *tx_in) { + uint64_t script_sig_len = 0; + bool success = btcspv_extract_script_sig_len(&script_sig_len, tx_in); + if (!success) { + RET_NULL_VIEW(scriptsig_t); + } - byte_view_t script_sig = {(tx_in->loc) + 36, length}; + uint32_t length = btcspv_compact_int_length(script_sig_len) + script_sig_len; + scriptsig_t script_sig = {(tx_in->loc) + 36, length}; return script_sig; } -byte_view_t btcspv_extract_sequence_le_legacy(const_view_t *tx_in) { - const script_sig_t ss = btcspv_extract_script_sig_len(tx_in); - const uint32_t offset = 36 + 1 + ss.var_int_len + ss.script_sig_len; +byte_view_t btcspv_extract_sequence_le_legacy(const_txin_t *tx_in) { + uint64_t script_sig_len = 0; + bool success = btcspv_extract_script_sig_len(&script_sig_len, tx_in); + if (!success) { + RET_NULL_VIEW(byte_view_t); + } + + const uint32_t offset = + 36 + btcspv_compact_int_length(script_sig_len) + script_sig_len; byte_view_t seq = {(tx_in->loc) + offset, 4}; return seq; } -uint32_t btcspv_extract_sequence_legacy(const_view_t *tx_in) { +uint32_t btcspv_extract_sequence_legacy(const_txin_t *tx_in) { const_view_t seq = btcspv_extract_sequence_le_legacy(tx_in); return AS_LE_UINT32(seq.loc); } -uint64_t btcspv_determine_input_length(const_view_t *tx_in) { - const script_sig_t ss = btcspv_extract_script_sig_len(tx_in); - if (ss.var_int_len == BTCSPV_ERR_BAD_ARG) { - return BTCSPV_ERR_BAD_ARG; +bool btcspv_determine_input_length(uint64_t *result, const_txin_t *tx_in) { + uint64_t script_sig_len = 0; + bool success = btcspv_extract_script_sig_len(&script_sig_len, tx_in); + if (!success) { + return false; } - return 41 + ss.var_int_len + ss.script_sig_len; + *result = 40 + btcspv_compact_int_length(script_sig_len) + script_sig_len; + return true; } -byte_view_t btcspv_extract_input_at_index(const_view_t *vin, uint64_t index) { - var_int_t var_int = btcspv_parse_var_int(vin); +txin_t btcspv_extract_input_at_index(const_vin_t *vin, uint64_t index) { + uint64_t n_ins = 0; + bool success = btcspv_parse_compact_int(&n_ins, vin->loc, vin->len); - if (var_int.number == 0 || var_int.var_int_len == BTCSPV_ERR_BAD_ARG) { - RET_NULL_VIEW; - } - if (index >= var_int.number) { - RET_NULL_VIEW; // wanted to read more inputs than there are + if (!success || n_ins == 0 || index >= n_ins) { + RET_NULL_VIEW(txin_t); } - uint32_t length = 0; - uint32_t offset = 1 + var_int.var_int_len; + uint64_t length = 0; + uint64_t offset = btcspv_compact_int_length(n_ins); for (int i = 0; i < index + 1; i++) { - const_view_t remaining = {(vin->loc) + offset, (vin->len) - offset}; - length = btcspv_determine_input_length(&remaining); - if (length == 0 || length == BTCSPV_ERR_BAD_ARG) { - RET_NULL_VIEW; + const_txin_t remaining = {(vin->loc) + offset, (vin->len) - offset}; + bool success = btcspv_determine_input_length(&length, &remaining); + if (!success || length == 0) { + RET_NULL_VIEW(const_txin_t); } if (i != index) { offset += length; @@ -197,29 +223,29 @@ byte_view_t btcspv_extract_input_at_index(const_view_t *vin, uint64_t index) { } if (offset + length > vin->len) { - RET_NULL_VIEW; + RET_NULL_VIEW(const_txin_t); } - byte_view_t input = {(vin->loc) + offset, length}; + const_txin_t input = {(vin->loc) + offset, length}; return input; } -byte_view_t btcspv_extract_outpoint(const_view_t *tx_in) { - byte_view_t outpoint = {tx_in->loc, 36}; +outpoint_t btcspv_extract_outpoint(const_txin_t *tx_in) { + outpoint_t outpoint = {tx_in->loc, 36}; return outpoint; } -byte_view_t btcspv_extract_input_tx_id_le(const_view_t *tx_in) { +byte_view_t btcspv_extract_input_tx_id_le(const_txin_t *tx_in) { byte_view_t tx_id_le = {tx_in->loc, 32}; return tx_id_le; } -byte_view_t btcspv_extract_tx_index_le(const_view_t *tx_in) { +byte_view_t btcspv_extract_tx_index_le(const_txin_t *tx_in) { byte_view_t idx = {tx_in->loc + 32, 4}; return idx; } -uint32_t btcspv_extract_tx_index(const_view_t *tx_in) { +uint32_t btcspv_extract_tx_index(const_txin_t *tx_in) { const_view_t idx = btcspv_extract_tx_index_le(tx_in); return AS_LE_UINT32(idx.loc); } @@ -228,40 +254,42 @@ uint32_t btcspv_extract_tx_index(const_view_t *tx_in) { // Output Functions // -uint64_t btcspv_determine_output_length(const_view_t *tx_out) { +bool btcspv_determine_output_length(uint64_t *result, const_view_t *tx_out) { if (tx_out->len < 9) { - return BTCSPV_ERR_BAD_ARG; + return false; } const_view_t after_value = {.loc = tx_out->loc + 8, .len = tx_out->len - 8}; - var_int_t var_int = btcspv_parse_var_int(&after_value); + uint64_t script_pubkey_length = 0; + bool success = btcspv_parse_compact_int(&script_pubkey_length, + after_value.loc, after_value.len); - if (var_int.var_int_len == BTCSPV_ERR_BAD_ARG) { - return BTCSPV_ERR_BAD_ARG; + if (!success) { + return false; } - return 8 + 1 + var_int.var_int_len + var_int.number; + *result = 8 + btcspv_compact_int_length(script_pubkey_length) + + script_pubkey_length; + return true; } -byte_view_t btcspv_extract_output_at_index(const_view_t *vout, uint64_t index) { - var_int_t var_int = btcspv_parse_var_int(vout); +txout_t btcspv_extract_output_at_index(const_vout_t *vout, uint64_t index) { + uint64_t n_outs = 0; + bool success = btcspv_parse_compact_int(&n_outs, vout->loc, vout->len); - if (var_int.number == 0 || var_int.var_int_len == BTCSPV_ERR_BAD_ARG) { - RET_NULL_VIEW; - } - if (index >= var_int.number) { - RET_NULL_VIEW; + if (!success || n_outs == 0 || index >= n_outs) { + RET_NULL_VIEW(txout_t); } - uint32_t length = 0; - uint32_t offset = 1 + var_int.var_int_len; + uint64_t length = 0; + uint64_t offset = btcspv_compact_int_length(n_outs); for (int i = 0; i < index + 1; i++) { const_view_t remaining = {(vout->loc) + offset, (vout->len) - offset}; - length = btcspv_determine_output_length(&remaining); - if (length == 0 || length == BTCSPV_ERR_BAD_ARG) { - RET_NULL_VIEW; + bool success = btcspv_determine_output_length(&length, &remaining); + if (!success || length == 0) { + RET_NULL_VIEW(txout_t); } if (i != index) { offset += length; @@ -269,48 +297,57 @@ byte_view_t btcspv_extract_output_at_index(const_view_t *vout, uint64_t index) { } if (offset + length > vout->len) { - RET_NULL_VIEW; + RET_NULL_VIEW(txout_t); } - const_view_t tx_out = {(vout->loc) + offset, length}; + const_txout_t tx_out = {(vout->loc) + offset, length}; return tx_out; } -byte_view_t btcspv_extract_value_le(const_view_t *tx_out) { +byte_view_t btcspv_extract_value_le(const_txout_t *tx_out) { const_view_t val = {tx_out->loc, 8}; return val; } -uint64_t btcspv_extract_value(const_view_t *tx_out) { +uint64_t btcspv_extract_value(const_txout_t *tx_out) { const_view_t val = btcspv_extract_value_le(tx_out); return AS_LE_UINT64(val.loc); } -byte_view_t btcspv_extract_op_return_data(const_view_t *tx_out) { +op_return_t btcspv_extract_op_return_data(const_txout_t *tx_out) { if (tx_out->loc[9] != 0x6a) { - RET_NULL_VIEW; + RET_NULL_VIEW(op_return_t); } uint8_t data_len = tx_out->loc[10]; if (tx_out->len < data_len + 8 + 3) { - RET_NULL_VIEW; + RET_NULL_VIEW(op_return_t); } - const_view_t payload = {tx_out->loc + 11, data_len}; + op_return_t payload = {tx_out->loc + 11, data_len}; return payload; } -byte_view_t btcspv_extract_hash(const_view_t *tx_out) { +script_pubkey_t btcspv_extract_script_pubkey(const_txout_t *tx_out) { + if (tx_out->len < 8) { + RET_NULL_VIEW(script_pubkey_t); + } + script_pubkey_t spk = {tx_out->loc + 8, tx_out->len - 8}; + return spk; +} + +byte_view_t btcspv_extract_hash(const_txout_t *tx_out) { const_view_t tag = {tx_out->loc + 8, 3}; if (tag.loc[0] + 9 != tx_out->len) { - RET_NULL_VIEW; + RET_NULL_VIEW(byte_view_t); } if (tag.loc[1] == 0) { uint32_t script_len = tag.loc[0]; uint32_t payload_len = tag.loc[2]; - if (payload_len != script_len - 2 || (payload_len != 0x20 && payload_len != 0x14)) { - RET_NULL_VIEW; + if (payload_len != script_len - 2 || + (payload_len != 0x20 && payload_len != 0x14)) { + RET_NULL_VIEW(byte_view_t); } const_view_t payload = {tx_out->loc + 11, payload_len}; @@ -321,8 +358,9 @@ byte_view_t btcspv_extract_hash(const_view_t *tx_out) { if (btcspv_view_eq_buf(&tag, P2PKH_PREFIX, 3)) { const_view_t last_two = {tx_out->loc + tx_out->len - 2, 2}; uint8_t P2PKH_POSTFIX[2] = {0x88, 0xac}; - if (tx_out->loc[11] != 0x14 || !btcspv_view_eq_buf(&last_two, P2PKH_POSTFIX, 2)) { - RET_NULL_VIEW; + if (tx_out->loc[11] != 0x14 || + !btcspv_view_eq_buf(&last_two, P2PKH_POSTFIX, 2)) { + RET_NULL_VIEW(byte_view_t); } const_view_t payload = {tx_out->loc + 12, 20}; return payload; @@ -331,37 +369,39 @@ byte_view_t btcspv_extract_hash(const_view_t *tx_out) { uint8_t P2SH_PREFIX[3] = {0x17, 0xa9, 0x14}; if (btcspv_view_eq_buf(&tag, P2SH_PREFIX, 3)) { if (tx_out->loc[tx_out->len - 1] != 0x87) { - RET_NULL_VIEW; + RET_NULL_VIEW(byte_view_t); } const_view_t payload = {tx_out->loc + 11, 20}; return payload; } - RET_NULL_VIEW; + RET_NULL_VIEW(byte_view_t); } // // Tx Functions // -bool btcspv_validate_vin(const_view_t *vin) { - var_int_t var_int = btcspv_parse_var_int(vin); +bool btcspv_validate_vin(const_vin_t *vin) { + uint64_t n_ins = 0; + bool success = btcspv_parse_compact_int(&n_ins, vin->loc, vin->len); - if (var_int.number == 0 || var_int.var_int_len == BTCSPV_ERR_BAD_ARG) { + if (!success || n_ins == 0) { return false; } - uint64_t n_ins = var_int.number; - uint64_t offset = 1 + var_int.var_int_len; + uint64_t offset = btcspv_compact_int_length(n_ins); for (int i = 0; i < n_ins; i++) { if (offset >= vin->len) { return false; } - const_view_t remaining = {vin->loc + offset, vin->len - offset}; - uint64_t input_len = btcspv_determine_input_length(&remaining); - if (input_len == BTCSPV_ERR_BAD_ARG) { + const_txin_t remaining = {vin->loc + offset, vin->len - offset}; + + uint64_t input_len = 0; + bool success = btcspv_determine_input_length(&input_len, &remaining); + if (!success) { return false; } @@ -370,24 +410,25 @@ bool btcspv_validate_vin(const_view_t *vin) { return offset == vin->len; } -bool btcspv_validate_vout(const_view_t *vout) { - var_int_t var_int = btcspv_parse_var_int(vout); +bool btcspv_validate_vout(const_vout_t *vout) { + uint64_t n_outs = 0; + bool success = btcspv_parse_compact_int(&n_outs, vout->loc, vout->len); - if (var_int.number == 0 || var_int.var_int_len == BTCSPV_ERR_BAD_ARG) { + if (!success || n_outs == 0) { return false; } - uint64_t n_ins = var_int.number; - uint64_t offset = 1 + var_int.var_int_len; + uint64_t offset = btcspv_compact_int_length(n_outs); - for (int i = 0; i < n_ins; i++) { + for (int i = 0; i < n_outs; i++) { if (offset >= vout->len) { return false; } const_view_t remaining = {vout->loc + offset, vout->len - offset}; - uint64_t output_len = btcspv_determine_output_length(&remaining); - if (output_len == BTCSPV_ERR_BAD_ARG) { + uint64_t output_len = 0; + bool success = btcspv_determine_output_length(&output_len, &remaining); + if (!success) { return false; } @@ -400,19 +441,19 @@ bool btcspv_validate_vout(const_view_t *vout) { // Header Functions // -byte_view_t btcspv_extract_merkle_root_le(const_view_t *header) { +byte_view_t btcspv_extract_merkle_root_le(const_header_t *header) { const_view_t root = {header->loc + 36, 32}; return root; } -void btcspv_extract_target_le(uint256 target, const_view_t *header) { +void btcspv_extract_target_le(uint256 target, const_header_t *header) { uint8_t exponent = header->loc[75] - 3; target[exponent + 0] = header->loc[72]; target[exponent + 1] = header->loc[73]; target[exponent + 2] = header->loc[74]; } -void btcspv_extract_target(uint256 target, const_view_t *header) { +void btcspv_extract_target(uint256 target, const_header_t *header) { uint256 target_le = {0}; btcspv_extract_target_le(target_le, header); btcspv_buf_rev(target, target_le, 32); @@ -427,29 +468,29 @@ uint64_t btcspv_calculate_difficulty(uint256 target) { return d1t / t; } -byte_view_t btcspv_extract_prev_block_hash_le(const_view_t *header) { +byte_view_t btcspv_extract_prev_block_hash_le(const_header_t *header) { const_view_t prev_hash = {header->loc + 4, 32}; return prev_hash; } -byte_view_t btcspv_extract_timestamp_le(const_view_t *header) { +byte_view_t btcspv_extract_timestamp_le(const_header_t *header) { const_view_t timestamp = {header->loc + 68, 4}; return timestamp; } -uint32_t btcspv_extract_timestamp(const_view_t *header) { +uint32_t btcspv_extract_timestamp(const_header_t *header) { const_view_t timestamp = btcspv_extract_timestamp_le(header); return AS_LE_UINT32(timestamp.loc); } -uint64_t btcspv_extract_difficulty(const_view_t *header) { +uint64_t btcspv_extract_difficulty(const_header_t *header) { uint256 target = {0}; btcspv_extract_target(target, header); return btcspv_calculate_difficulty(target); } -void btcspv_hash256_merkle_step(uint8_t *result, const_view_t *a, - const_view_t *b) { +void btcspv_hash256_merkle_step(uint8_t *result, const_merkle_node_t *a, + const_merkle_node_t *b) { uint8_t intermediate[32] = {0}; SHA256_CTX sha256_ctx; @@ -477,13 +518,13 @@ bool btcspv_verify_hash256_merkle(const_view_t *proof, uint32_t index) { return false; } - byte_view_t current = {proof->loc, 32}; + merkle_node_t current = {proof->loc, 32}; const_view_t root = {proof->loc + (proof->len - 32), 32}; const uint32_t num_steps = (proof->len / 32) - 1; uint8_t hash[32] = {0}; for (int i = 1; i < num_steps; i++) { - const_view_t next = {proof->loc + (i * 32), 32}; + merkle_node_t next = {proof->loc + (i * 32), 32}; if (idx % 2 == 1) { btcspv_hash256_merkle_step(hash, &next, ¤t); @@ -493,7 +534,7 @@ bool btcspv_verify_hash256_merkle(const_view_t *proof, uint32_t index) { current.loc = hash; idx >>= 1; } - return btcspv_view_eq(¤t, &root); + return btcspv_buf_eq(current.loc, current.len, root.loc, root.len); } // new_target should be BE here diff --git a/c/csrc/btcspv.h b/c/csrc/btcspv.h index 49346ee9..4e02506d 100644 --- a/c/csrc/btcspv.h +++ b/c/csrc/btcspv.h @@ -4,6 +4,19 @@ #include "stdbool.h" #include "stdint.h" +#define DECLARE_VIEW_TYPE(NAME) \ + typedef struct { \ + const uint8_t *loc; \ + const uint32_t len; \ + } NAME##_t; \ + typedef const NAME##_t const_##NAME##_t; + +#define VIEW_FROM_VIEW(view) \ + { view.loc, view.len } + +#define VIEW_FROM_VIEW_POINTER(view) \ + { view->loc, view->len } + #define VIEW_FROM_ARR(arr) \ { arr, sizeof(arr) } @@ -27,8 +40,8 @@ (((uint32_t)(*(a + 0)) << 24) | ((uint32_t)(*(a + 1)) << 16) | \ ((uint32_t)(*(a + 2)) << 8) | ((uint32_t)(*(a + 3)))) -#define RET_NULL_VIEW \ - const_view_t _null_view = {NULL, 0}; \ +#define RET_NULL_VIEW(type) \ + type _null_view = {NULL, 0}; \ return _null_view; #define UINT256_EQ(lhs, rhs) (memcmp(lhs, rhs, 32) == 0) @@ -39,33 +52,46 @@ #define SET_UINT256(to, from) (memcpy(to, from, 32)) +DECLARE_VIEW_TYPE(view); // Unknown or unspecified type +DECLARE_VIEW_TYPE(compact_int); +DECLARE_VIEW_TYPE(scriptsig); +DECLARE_VIEW_TYPE(outpoint); +DECLARE_VIEW_TYPE(txin); +DECLARE_VIEW_TYPE(vin); +DECLARE_VIEW_TYPE(script_pubkey); +// DECLARE_VIEW_TYPE(pkh); +// DECLARE_VIEW_TYPE(wpkh); +// DECLARE_VIEW_TYPE(sh); +// DECLARE_VIEW_TYPE(wsh); +DECLARE_VIEW_TYPE(op_return); +DECLARE_VIEW_TYPE(txout); +DECLARE_VIEW_TYPE(vout); +DECLARE_VIEW_TYPE(header); +DECLARE_VIEW_TYPE(header_array); +DECLARE_VIEW_TYPE(merkle_node); +DECLARE_VIEW_TYPE(merkle_step); +DECLARE_VIEW_TYPE(merkle_array); + +typedef view_t byte_view_t; + /// Error code const uint64_t BTCSPV_ERR_BAD_ARG; /// A 256-bit integer to support Bitcoin operations. typedef uint8_t uint256[32]; -/// A simple memory view struct. -typedef struct { - const uint8_t *loc; /** A pointer to the start of the view */ - const uint32_t len; /** The number of bytes in the view */ -} byte_view_t; +// /// A simple memory view struct. +// typedef struct { +// const uint8_t *loc; /** A pointer to the start of the view */ +// const uint32_t len; /** The number of bytes in the view */ +// } byte_view_t; /// Return type for parse_var_int. Contains information about VarInt structure typedef struct { - const uint32_t var_int_len; /** The number of bytes of VarInt data */ - const uint64_t number; /** The number of bytes in the scriptsig */ + const uint32_t var_int_len; /** The number of bytes of VarInt data */ + const uint64_t number; /** The number of bytes in the scriptsig */ } var_int_t; -/// Return type for extract_script_sig_len. Contains information about tx_in structure -typedef struct { - const uint32_t var_int_len; /** The number of bytes of VarInt data */ - const uint64_t script_sig_len; /** The number of bytes in the scriptsig */ -} script_sig_t; - -/// Alias for constant view -typedef const byte_view_t const_view_t; - // Utilities /// @brief equality for truncated and full-length Bitcoin targets @@ -76,7 +102,7 @@ bool btcspv_truncated_uint256_equality(const uint8_t *trun, /// @brief equality of 2 memory regions using `memcmp` bool btcspv_buf_eq(const uint8_t *loc1, uint32_t len1, const uint8_t *loc2, - uint32_t len2); + uint32_t len2); /// @brief equality between byte_view_t and any other buffer type. /// @note uses buf_eq under the hood @@ -116,152 +142,181 @@ void btcspv_hash256(uint8_t *result, const_view_t *preimage); * --- tx_in & vin Functions --- */ - /// @brief Determines the length of a VarInt in bytes - /// @note A VarInt of >1 byte is prefixed with a flag indicating its length - /// @param flag The first byte of a VarInt - /// @return The number of non-flag bytes in the VarInt -uint8_t btcspv_determine_var_int_data_length(uint8_t tag); - -/// @brief Parse a VarInt into its data length and the number it represents -/// @note Useful for Parsing Vins and Vouts. Returns ERR_BAD_ARG if insufficient bytes. +/// @brief Determines the length of a VarInt in bytes +/// @note A VarInt of >1 byte is prefixed with a flag indicating its +/// length +/// @param flag The first byte of a VarInt +/// @return The number of non-flag bytes in the VarInt +uint8_t btcspv_determine_compact_int_data_length(uint8_t tag); + +/// @brief Determines the serialized length of a compact int in bytes +/// @note Always returns 1, 3, 5 or 9 +/// @param number The desrialized number +/// @return The number of bytes in the serialized int, including the +/// flag +uint8_t btcspv_compact_int_length(uint64_t number); + +/// @brief Parse a CompactInt to number it represents. First argument +/// is the result +/// @note Useful for Parsing Vins and Vouts. Returns false if +/// insufficient bytes. /// @param b A byte-view starting with a VarInt -/// @return A struct containing number of bytes in the encoding (not counting the tag) and the encoded int +/// @return A struct containing number of bytes in the encoding (not +/// counting the tag) and the encoded int /// @warning Caller MUST check that it does not error -var_int_t btcspv_parse_var_int(const_view_t *b); +bool btcspv_parse_compact_int(uint64_t *result, const uint8_t *loc, + uint32_t len); /// @brief Determines whether an input is legacy /// @note False if no scriptSig, otherwise True /// @param input The input /// @return True for legacy, False for witness -bool btcspv_is_legacy_input(const_view_t *tx_in); +bool btcspv_is_legacy_input(const_txin_t *tx_in); /// @brief Extracts the LE sequence bytes from an input /// @note Sequence is used for relative time locks /// @param input The WITNESS input /// @return The sequence bytes (LE uint) -byte_view_t btcspv_extract_sequence_le_witness(const_view_t *tx_in); +byte_view_t btcspv_extract_sequence_le_witness(const_txin_t *tx_in); /// @brief Extracts the sequence from the input in a tx /// @note Sequence is a 4-byte little-endian number /// @param input The WITNESS input /// @return The sequence number (big-endian uint) -uint32_t btcspv_extract_sequence_witness(const_view_t *tx_in); +uint32_t btcspv_extract_sequence_witness(const_txin_t *tx_in); /// @brief Determines the length of a scriptSig in an input /// @note Will return 0 if passed a witness input /// @param input The LEGACY input /// @return The length of the script sig /// @warning Caller MUST check that it does not error -script_sig_t btcspv_extract_script_sig_len(const_view_t *tx_in); +bool btcspv_extract_script_sig_len(uint64_t *result, const_txin_t *tx_in); -/// @brief Extracts the VarInt-prepended scriptSig from the input in a tx +/// @brief Extracts the VarInt-prepended scriptSig from the input in a +/// tx /// @note Will return hex"00" if passed a witness input /// @param input The LEGACY input /// @return The length-prepended script sig /// @warning Caller MUST check that it does not error -byte_view_t btcspv_extract_script_sig(const_view_t *tx_in); +scriptsig_t btcspv_extract_script_sig(const_txin_t *tx_in); /// @brief Extracts the LE sequence bytes from an input /// @note Sequence is used for relative time locks /// @param input The LEGACY input /// @return The sequence bytes (LE uint) -byte_view_t btcspv_extract_sequence_le_legacy(const_view_t *tx_in); +byte_view_t btcspv_extract_sequence_le_legacy(const_txin_t *tx_in); /// @brief Extracts the sequence from the input /// @note Sequence is a 4-byte little-endian number /// @param input The LEGACY input /// @return The sequence number (big-endian uint) -uint32_t btcspv_extract_sequence_legacy(const_view_t *tx_in); +uint32_t btcspv_extract_sequence_legacy(const_txin_t *tx_in); /// @brief Determines the length of an input from its scriptsig /// @note 36 for outpoint, 1 for scriptsig length, 4 for sequence -/// @param input The input +/// @param input The inputzf /// @return The length of the input in bytes /// @warning Caller MUST check that it does not error -uint64_t btcspv_determine_input_length(const_view_t *tx_in); +bool btcspv_determine_input_length(uint64_t *result, const_txin_t *tx_in); /// @brief Extracts the nth input from the vin (0-indexed) -/// @note Iterates over the vin. If you need to extract several, write a custom function +/// @note Iterates over the vin. If you need to extract several, +/// write a custom function /// @param vin The vin as a tightly-packed byte array /// @param index The 0-indexed location of the input to extract /// @return The input as a byte array -/// @warning Caller must check that resulting view loc is not null, and/or len !=0. -byte_view_t btcspv_extract_input_at_index(const_view_t *vin, uint64_t index); +/// @warning Caller must check that resulting view loc is not null, +/// and/or len !=0. +txin_t btcspv_extract_input_at_index(const_vin_t *vin, uint64_t index); /// @brief Extracts the outpoint from the input in a tx /// @note 32 byte tx id with 4 byte index /// @param input The input -/// @return The outpoint (LE bytes of prev tx hash + LE bytes of prev tx index) -byte_view_t btcspv_extract_outpoint(const_view_t *tx_in); +/// @return The outpoint (LE bytes of prev tx hash + LE bytes of prev +/// tx index) +outpoint_t btcspv_extract_outpoint(const_txin_t *tx_in); /// @brief Extracts the outpoint tx id from an input /// @note 32 byte tx id from outpoint /// @param input The input /// @return The tx id (little-endian bytes) -byte_view_t btcspv_extract_input_tx_id_le(const_view_t *tx_in); +byte_view_t btcspv_extract_input_tx_id_le(const_txin_t *tx_in); /// @brief Extracts the LE tx input index from the input in a tx /// @note 4 byte tx index /// @param input The input /// @return The tx index (little-endian bytes) -byte_view_t btcspv_extract_tx_index_le(const_view_t *tx_in); +byte_view_t btcspv_extract_tx_index_le(const_txin_t *tx_in); /// @brief Extracts the tx input index from the input in a tx /// @note 4 byte tx index /// @param input The input /// @return The tx index (big-endian uint) -uint32_t btcspv_extract_tx_index(const_view_t *tx_in); +uint32_t btcspv_extract_tx_index(const_txin_t *tx_in); /* * --- tx_out & vout Functions --- */ - /// @brief Determines the length of an output - /// @note 5 types: WPKH, WSH, PKH, SH, and OP_RETURN - /// @param output The output - /// @return The length indicated by the prefix, error if invalid length - /// @warning Caller MUST check that it does not error -uint64_t btcspv_determine_output_length(const_view_t *tx_out); +/// @brief Determines the length of an output, writes it to `number` +/// @note 5 types: WPKH, WSH, PKH, SH, and OP_RETURN +/// @param tx_out The output +/// @return The length indicated by the prefix, error if invalid length +/// @warning Caller MUST check that it does not error +bool btcspv_determine_output_length(uint64_t *result, const_view_t *tx_out); /// @brief Extracts the output at a given index in the TxIns vector -/// @note Iterates over the vout. If you need to extract multiple, write a custom function +/// @note Iterates over the vout. If you need to extract multiple, +/// write a custom function /// @param vout The _vout to extract from /// @param index The 0-indexed location of the output to extract /// @return The specified output -/// @warning Caller must check that resulting view loc is not null, and/or len !=0. -byte_view_t btcspv_extract_output_at_index(const_view_t *vout, uint64_t index); +/// @warning Caller must check that resulting view loc is not null, +/// and/or len !=0. +txout_t btcspv_extract_output_at_index(const_vout_t *vout, uint64_t index); /// @brief Extracts the output script length /// @note Indexes the length prefix on the pk_script -/// @param output The output +/// @param tx_out The output /// @return The 1 byte length prefix -uint32_t btcspv_extract_output_script_len(const_view_t *tx_out); +uint32_t btcspv_extract_output_script_len(const_txout_t *tx_out); /// @brief Extracts the value bytes from the output in a tx /// @note Value is an 8-byte little-endian number -/// @param output The output +/// @param tx_out The output /// @return The output value as LE bytes -byte_view_t btcspv_extract_value_le(const_view_t *tx_out); +byte_view_t btcspv_extract_value_le(const_txout_t *tx_out); /// @brief Extracts the value from the output in a tx /// @note Value is an 8-byte little-endian number -/// @param output The output +/// @param tx_out The output /// @return The output value -uint64_t btcspv_extract_value(const_view_t *tx_out); +uint64_t btcspv_extract_value(const_txout_t *tx_out); /// @brief Extracts the data from an op return output -/// @note Returns hex"" if no data or not an op return -/// @param output The output -/// @return Any data contained in the opreturn output, null if not an op return -/// @warning Caller must check that resulting view loc is not null, and/or len !=0. -byte_view_t btcspv_extract_op_return_data(const_view_t *tx_out); +/// @note May return a null view, in case of parsing error +/// @param tx_out The output +/// @return Any data contained in the opreturn output, null if not an +/// op return +/// @warning Caller must check that resulting view loc is not null, +/// and/or len !=0. +op_return_t btcspv_extract_op_return_data(const_txout_t *tx_out); + +/// @brief Extracts the data from an op return output +/// @note May return a null view, in case of parsing error +/// @param tx_out The output +/// @return The script pubkey +/// @warning Caller must check that resulting view loc is not null, +/// and/or len !=0. +script_pubkey_t btcspv_extract_script_pubkey(const_txout_t *tx_out); /// @brief Extracts the hash from the output script /// @note Determines type by the length prefix and validates format -/// @param output The output +/// @param tx_out The output /// @return The hash committed to by the pk_script, or null for errors -/// @warning Caller must check that resulting view loc is not null, and/or len !=0. -byte_view_t btcspv_extract_hash(const_view_t *tx_out); +/// @warning Caller must check that resulting view loc is not null, +/// and/or len !=0. +byte_view_t btcspv_extract_hash(const_txout_t *tx_out); /* * --- tx-validation Functions --- @@ -271,42 +326,47 @@ byte_view_t btcspv_extract_hash(const_view_t *tx_out); /// @note Consider a vin with a valid vout in its scriptsig /// @param vin Raw bytes length-prefixed input vector /// @return True if it represents a validly formatted vin -bool btcspv_validate_vin(const_view_t *vin); +bool btcspv_validate_vin(const_vin_t *vin); /// @brief Checks that the vin passed up is properly formatted /// @note Consider a vin with a valid vout in its scriptsig /// @param vout Raw bytes length-prefixed output vector /// @return True if it represents a validly formatted bout -bool btcspv_validate_vout(const_view_t *vout); +bool btcspv_validate_vout(const_vout_t *vout); /* * --- header Functions --- */ - /// @brief Extracts the transaction merkle root from a block header - /// @note Use verifyHash256Merkle to verify proofs with this root - /// @param header The header - /// @return The merkle root (little-endian) -byte_view_t btcspv_extract_merkle_root_le(const_view_t *header); +/// @brief Extracts the transaction merkle root from a block header +/// @note Use verifyHash256Merkle to verify proofs with this root +/// @param header The header +/// @return The merkle root (little-endian) +byte_view_t btcspv_extract_merkle_root_le(const_header_t *header); /// @brief Extracts the target from a block header -/// @note Target is a 256 bit number encoded as a 3-byte mantissa and 1 byte exponent +/// @note Target is a 256 bit number encoded as a 3-byte mantissa and +/// 1 byte exponent /// @param header The header /// @warning overwrites `target` with the LE target -/// @warning caller must ensure `target` is allocated and can hold 32 bytes -void btcspv_extract_target_le(uint256 target, const_view_t *header); - +/// @warning caller must ensure `target` is allocated and can hold 32 +/// bytes +void btcspv_extract_target_le(uint256 target, const_header_t *header); /// @brief Extracts the target from a block header -/// @note Target is a 256 bit number encoded as a 3-byte mantissa and 1 byte exponent +/// @note Target is a 256 bit number encoded as a 3-byte mantissa and +/// 1 byte exponent /// @param header The header /// @warning overwrites `target` with the BE target -/// @warning caller must ensure `target` is allocated and can hold 32 bytes -void btcspv_extract_target(uint256 target, const_view_t *header); +/// @warning caller must ensure `target` is allocated and can hold 32 +/// bytes +void btcspv_extract_target(uint256 target, const_header_t *header); -/// @brief Calculate difficulty from the difficulty 1 target and current target +/// @brief Calculate difficulty from the difficulty 1 target and +/// current target /// @note Difficulty 1 is 0x1d00ffff on mainnet and testnet -/// @note Difficulty 1 is a 256 bit number encoded as a 3-byte mantissa and 1 byte exponent +/// @note Difficulty 1 is a 256 bit number encoded as a 3-byte +/// mantissa and 1 byte exponent /// @param target The current target /// @return The block difficulty (bdiff) /// @warning Caller should check that output is non-0 @@ -316,50 +376,57 @@ uint64_t btcspv_calculate_difficulty(uint256 target); /// @note Block headers do NOT include block number :( /// @param header The header /// @return The previous block's hash (little-endian) -byte_view_t btcspv_extract_prev_block_hash_le(const_view_t *header); +byte_view_t btcspv_extract_prev_block_hash_le(const_header_t *header); /// @brief Extracts the timestamp from a block header /// @note Time is not 100% reliable /// @param header The header /// @return The timestamp (little-endian bytes) -byte_view_t btcspv_extract_timestamp_le(const_view_t *header); +byte_view_t btcspv_extract_timestamp_le(const_header_t *header); /// @brief Extracts the timestamp from a block header /// @note Time is not 100% reliable /// @param header The header /// @return The timestamp (uint) -uint32_t btcspv_extract_timestamp(const_view_t *header); +uint32_t btcspv_extract_timestamp(const_header_t *header); /// @brief Extracts the expected difficulty from a block header /// @note Does NOT verify the work /// @param header The header /// @return The difficulty as an integer /// @warning Caller should check that output is non-0 -uint64_t btcspv_extract_difficulty(const_view_t *header); +uint64_t btcspv_extract_difficulty(const_header_t *header); /// @brief Concatenates and hashes two inputs for merkle proving /// @param a The first hash /// @param b The second hash /// @warning overwrites `result` with the next merkle step -/// @warning caller must ensure `result` is allocated and can hold 32 bytes -void btcspv_hash256_merkle_step(uint8_t *result, const_view_t *a, - const_view_t *b); +/// @warning caller must ensure `result` is allocated and can hold 32 +/// bytes +void btcspv_hash256_merkle_step(uint8_t *result, const_merkle_node_t *a, + const_merkle_node_t *b); /// @brief Verifies a Bitcoin-style merkle tree /// @note Leaves are 0-indexed. -/// @param proof The proof. Tightly packed LE sha256 hashes. The last hash is the root +/// @param proof The proof. Tightly packed LE sha256 hashes. The last hash +/// is the root /// @param index The index of the leaf /// @return true if the proof is valid, else false -/// @warning `index` is not a reliable indicator of location within a block. +/// @warning `index` is not a reliable indicator of location within a +/// block. bool btcspv_verify_hash256_merkle(const_view_t *proof, uint32_t index); /// @brief performs the bitcoin difficulty retarget /// @note implements the Bitcoin algorithm precisely /// @param previousTarget the target of the previous period -/// @param firstTimestamp the timestamp of the first block in the difficulty period -/// @param secondTimestamp the timestamp of the last block in the difficulty period -/// @warning overwrites `new_target` with the updated difficulty target -/// @warning caller must ensure `new_target` is allocated and can hold 32 bytes +/// @param firstTimestamp the timestamp of the first block in the difficulty +/// period +/// @param secondTimestamp the timestamp of the last block in the difficulty +/// period +/// @warning overwrites `new_target` with the updated difficulty +/// target +/// @warning caller must ensure `new_target` is allocated and can +/// hold 32 bytes void btcspv_retarget_algorithm(uint256 new_target, const uint256 previous_target, uint32_t first_timestamp, diff --git a/c/csrc/check_btcspv.c b/c/csrc/check_btcspv.c index 44b77b84..d1720348 100644 --- a/c/csrc/check_btcspv.c +++ b/c/csrc/check_btcspv.c @@ -65,7 +65,7 @@ START_TEST(prove) { uint8_t *nodes_buf; const uint32_t nodes_len = pos_as_hex_buf(&nodes_buf, intermediate_nodes_pos); - const_view_t nodes = {nodes_buf, nodes_len}; + const_merkle_array_t nodes = {nodes_buf, nodes_len}; uint32_t idx = pos_as_long(idx_pos); @@ -92,11 +92,11 @@ START_TEST(calculate_txid) { uint8_t *vin_buf; const uint32_t vin_len = pos_as_hex_buf(&vin_buf, vin_pos); - const_view_t vin = {vin_buf, vin_len}; + const_vin_t vin = {vin_buf, vin_len}; uint8_t *vout_buf; const uint32_t vout_len = pos_as_hex_buf(&vout_buf, vout_pos); - const_view_t vout = {vout_buf, vout_len}; + const_vout_t vout = {vout_buf, vout_len}; uint8_t *locktime_buf; const uint32_t locktime_len = pos_as_hex_buf(&locktime_buf, locktime_pos); @@ -154,7 +154,7 @@ START_TEST(validate_header_prev_hash) { uint8_t *header_buf; uint32_t header_len = pos_as_hex_buf(&header_buf, header_pos); - const_view_t header_view = {header_buf, header_len}; + const_header_t header_view = {header_buf, header_len}; uint8_t *prev_hash_buf; pos_as_hex_buf(&prev_hash_buf, prev_hash_pos); @@ -176,7 +176,7 @@ START_TEST(validate_header_chain) { uint8_t *headers_buf; uint32_t headers_len = token_as_hex_buf(&headers_buf, input_tok); - const_view_t headers_view = {headers_buf, headers_len}; + const_header_array_t headers_view = {headers_buf, headers_len}; uint64_t expected = token_as_long(output_tok); uint64_t actual = evalspv_validate_header_chain(&headers_view); @@ -194,7 +194,7 @@ START_TEST(validate_header_chain_errors) { uint8_t *headers_buf; uint32_t headers_len = token_as_hex_buf(&headers_buf, input_tok); - const_view_t headers_view = {headers_buf, headers_len}; + const_header_array_t headers_view = {headers_buf, headers_len}; size_t c_error_pos = val_pos_by_key(case_pos, "cError"); @@ -215,7 +215,7 @@ START_TEST(test_determine_var_int_data_length) { TEST_LOOP_START("determineVarIntDataLength") uint8_t input = token_as_long(input_tok); - uint8_t actual = btcspv_determine_var_int_data_length(input); + uint8_t actual = btcspv_determine_compact_int_data_length(input); uint8_t expected = token_as_long(output_tok); @@ -278,7 +278,8 @@ START_TEST(is_legacy_input) { const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); const_view_t input = {input_buf, input_len}; - bool actual = btcspv_is_legacy_input(&input); + const_txin_t txin = VIEW_FROM_VIEW(input); + bool actual = btcspv_is_legacy_input(&txin); bool expected = token_as_bool(output_tok); ck_assert(actual == expected); @@ -294,13 +295,13 @@ START_TEST(extract_sequence_le_witness) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t txin = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); const_view_t expected = {expected_buf, expected_len}; - const_view_t actual = btcspv_extract_sequence_le_witness(&input); + const_view_t actual = btcspv_extract_sequence_le_witness(&txin); ck_assert(btcspv_view_eq(&actual, &expected)); @@ -316,11 +317,11 @@ START_TEST(extract_sequence_witness) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t txin = {input_buf, input_len}; uint32_t expected = token_as_long(output_tok); - uint32_t actual = btcspv_extract_sequence_witness(&input); + uint32_t actual = btcspv_extract_sequence_witness(&txin); ck_assert_int_eq(actual, expected); @@ -335,17 +336,20 @@ START_TEST(extract_script_sig_len) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; size_t output1 = val_pos_by_key(case_pos, "output") + 1; size_t output2 = after(output1); uint32_t expected_var_int_len = pos_as_long(output1); uint32_t expected_script_sig_len = pos_as_long(output2); - script_sig_t actual = btcspv_extract_script_sig_len(&input); - ck_assert_int_eq(actual.var_int_len, expected_var_int_len); - ck_assert_int_eq(actual.script_sig_len, expected_script_sig_len); + uint64_t actual = 0; + bool success = btcspv_extract_script_sig_len(&actual, &input); + + ck_assert(success); + ck_assert_int_eq(btcspv_compact_int_length(actual) - 1, expected_var_int_len); + ck_assert_int_eq(actual, expected_script_sig_len); TEST_LOOP_END } @@ -356,15 +360,14 @@ START_TEST(extract_script_sig) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); const_view_t expected = {expected_buf, expected_len}; - const_view_t actual = btcspv_extract_script_sig(&input); - - ck_assert(btcspv_view_eq(&actual, &expected)); + const_scriptsig_t actual = btcspv_extract_script_sig(&input); + ck_assert(btcspv_buf_eq(actual.loc, actual.len, expected.loc, expected.len)); free(input_buf); free(expected_buf); @@ -378,7 +381,7 @@ START_TEST(extract_sequence_le_legacy) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -400,7 +403,7 @@ START_TEST(extract_sequence_legacy) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint32_t expected = token_as_long(output_tok); @@ -419,12 +422,14 @@ START_TEST(determine_input_length) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint32_t expected = token_as_long(output_tok); - uint32_t actual = btcspv_determine_input_length(&input); + uint64_t actual = 0; + bool success = btcspv_determine_input_length(&actual, &input); + ck_assert(success); ck_assert_int_eq(actual, expected); free(input_buf); @@ -443,7 +448,7 @@ START_TEST(extract_input_at_index) { uint8_t *vin_buf; const uint32_t vin_buf_len = token_as_hex_buf(&vin_buf, &test_vec_tokens[vin_val_pos]); - const_view_t vin_view = {vin_buf, vin_buf_len}; + const_vin_t vin_view = {vin_buf, vin_buf_len}; uint8_t input_index = token_as_long(&test_vec_tokens[idx_pos]); @@ -451,9 +456,9 @@ START_TEST(extract_input_at_index) { const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); const_view_t expected = {expected_buf, expected_len}; - const_view_t actual = btcspv_extract_input_at_index(&vin_view, input_index); + const_txin_t actual = btcspv_extract_input_at_index(&vin_view, input_index); - ck_assert(btcspv_view_eq(&actual, &expected)); + ck_assert(btcspv_view_eq_buf(&expected, actual.loc, actual.len)); free(vin_buf); free(expected_buf); @@ -472,11 +477,11 @@ START_TEST(extract_input_at_index_error) { uint8_t *vin_buf; const uint32_t vin_buf_len = token_as_hex_buf(&vin_buf, &test_vec_tokens[vin_val_pos]); - const_view_t vin_view = {vin_buf, vin_buf_len}; + const_vin_t vin_view = {vin_buf, vin_buf_len}; uint8_t input_index = token_as_long(&test_vec_tokens[idx_pos]); - const_view_t actual = btcspv_extract_input_at_index(&vin_view, input_index); + const_txin_t actual = btcspv_extract_input_at_index(&vin_view, input_index); ck_assert(actual.loc == NULL); ck_assert_int_eq(actual.len, 0); @@ -492,16 +497,15 @@ START_TEST(extract_outpoint) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); const_view_t expected = {expected_buf, expected_len}; - const_view_t actual = btcspv_extract_outpoint(&input); - - ck_assert(btcspv_view_eq(&actual, &expected)); + const_outpoint_t actual = btcspv_extract_outpoint(&input); + ck_assert(btcspv_buf_eq(actual.loc, actual.len, expected.loc, expected.len)); free(input_buf); free(expected_buf); @@ -514,7 +518,7 @@ START_TEST(extract_input_tx_id_le) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -536,7 +540,7 @@ START_TEST(extract_tx_index_le) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -558,7 +562,7 @@ START_TEST(extract_tx_index) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txin_t input = {input_buf, input_len}; uint32_t expected = token_as_long(output_tok); @@ -581,8 +585,10 @@ START_TEST(determine_output_length) { uint32_t expected = token_as_long(output_tok); - uint32_t actual = btcspv_determine_output_length(&input); + uint64_t actual = 0; + bool success = btcspv_determine_output_length(&actual, &input); + ck_assert(success); ck_assert_int_eq(actual, expected); free(input_buf); @@ -601,7 +607,7 @@ START_TEST(extract_output_at_index) { uint8_t *vout_buf; const uint32_t vout_buf_len = token_as_hex_buf(&vout_buf, &test_vec_tokens[vout_val_pos]); - const_view_t vout_view = {vout_buf, vout_buf_len}; + const_vout_t vout_view = {vout_buf, vout_buf_len}; uint8_t input_index = token_as_long(&test_vec_tokens[idx_pos]); @@ -609,11 +615,12 @@ START_TEST(extract_output_at_index) { const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); const_view_t expected = {expected_buf, expected_len}; - const_view_t actual = btcspv_extract_output_at_index(&vout_view, input_index); + const_txout_t actual = + btcspv_extract_output_at_index(&vout_view, input_index); ck_assert(actual.loc != NULL); ck_assert_int_ne(actual.len, 0); - ck_assert(btcspv_view_eq(&actual, &expected)); + ck_assert(btcspv_buf_eq(actual.loc, actual.len, expected.loc, expected.len)); free(vout_buf); free(expected_buf); @@ -622,7 +629,6 @@ START_TEST(extract_output_at_index) { } END_TEST - START_TEST(extract_output_at_index_error) { TEST_LOOP_START("extractOutputAtIndexError") @@ -633,10 +639,11 @@ START_TEST(extract_output_at_index_error) { uint8_t *vout_buf; const uint32_t vout_buf_len = token_as_hex_buf(&vout_buf, &test_vec_tokens[vout_val_pos]); - const_view_t vout_view = {vout_buf, vout_buf_len}; + const_vout_t vout_view = {vout_buf, vout_buf_len}; uint8_t output_index = token_as_long(&test_vec_tokens[idx_pos]); - const_view_t actual = btcspv_extract_output_at_index(&vout_view, output_index); + const_txout_t actual = + btcspv_extract_output_at_index(&vout_view, output_index); ck_assert(actual.loc == NULL); ck_assert_int_eq(actual.len, 0); @@ -652,7 +659,7 @@ START_TEST(extract_value_le) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txout_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -674,7 +681,7 @@ START_TEST(extract_value) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txout_t input = {input_buf, input_len}; uint32_t expected = token_as_long(output_tok); @@ -692,17 +699,17 @@ START_TEST(extract_op_return_data) { TEST_LOOP_START("extractOpReturnData") uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txout_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); const_view_t expected = {expected_buf, expected_len}; - const_view_t actual = btcspv_extract_op_return_data(&input); + const_op_return_t actual = btcspv_extract_op_return_data(&input); ck_assert(actual.loc != NULL); ck_assert_int_ne(actual.len, 0); - ck_assert(btcspv_view_eq(&actual, &expected)); + ck_assert(btcspv_buf_eq(actual.loc, actual.len, expected.loc, expected.len)); free(input_buf); free(expected_buf); @@ -715,9 +722,9 @@ START_TEST(extract_op_return_data_error) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txout_t input = {input_buf, input_len}; - const_view_t actual = btcspv_extract_op_return_data(&input); + const_op_return_t actual = btcspv_extract_op_return_data(&input); ck_assert(actual.loc == NULL); ck_assert_int_eq(actual.len, 0); @@ -732,7 +739,7 @@ START_TEST(extract_hash) { TEST_LOOP_START("extractHash") uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txout_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -752,7 +759,7 @@ START_TEST(extract_hash_error) { TEST_LOOP_START("extractHashError") uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_txout_t input = {input_buf, input_len}; const_view_t actual = btcspv_extract_hash(&input); @@ -770,7 +777,7 @@ START_TEST(validate_vin) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_vin_t input = {input_buf, input_len}; bool actual = btcspv_validate_vin(&input); bool expected = token_as_bool(output_tok); @@ -788,7 +795,7 @@ START_TEST(validate_vout) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_vout_t input = {input_buf, input_len}; bool actual = btcspv_validate_vout(&input); bool expected = token_as_bool(output_tok); @@ -805,7 +812,7 @@ START_TEST(extract_target) { TEST_LOOP_START("extractTarget") uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_header_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -832,7 +839,7 @@ START_TEST(extract_timestamp) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_header_t input = {input_buf, input_len}; uint32_t expected = token_as_long(output_tok); @@ -851,7 +858,7 @@ START_TEST(extract_merkle_root_le) { uint8_t *input_buf; const uint32_t input_len = token_as_hex_buf(&input_buf, input_tok); - const_view_t input = {input_buf, input_len}; + const_header_t input = {input_buf, input_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -877,11 +884,11 @@ START_TEST(hash256_merkle_step) { uint8_t *a_buf; const uint32_t a_len = token_as_hex_buf(&a_buf, &test_vec_tokens[a_val_pos]); - const_view_t a = {a_buf, a_len}; + const_merkle_node_t a = {a_buf, a_len}; uint8_t *b_buf; const uint32_t b_len = token_as_hex_buf(&b_buf, &test_vec_tokens[b_val_pos]); - const_view_t b = {b_buf, b_len}; + const_merkle_node_t b = {b_buf, b_len}; uint8_t *expected_buf; const uint32_t expected_len = token_as_hex_buf(&expected_buf, output_tok); @@ -937,7 +944,8 @@ START_TEST(retarget_algorithm) { uint8_t *first_header_raw_buf; const uint32_t first_header_raw_len = token_as_hex_buf( &first_header_raw_buf, &test_vec_tokens[first_header_raw_pos]); - const_view_t first_header_raw = {first_header_raw_buf, first_header_raw_len}; + const_header_t first_header_raw = {first_header_raw_buf, + first_header_raw_len}; size_t second_timestamp_pos = val_pos_by_key(second_header, "timestamp"); uint32_t second_timestamp = pos_as_long(second_timestamp_pos); @@ -946,7 +954,8 @@ START_TEST(retarget_algorithm) { uint8_t *third_header_raw_buf; const uint32_t third_header_raw_len = token_as_hex_buf( &third_header_raw_buf, &test_vec_tokens[third_header_raw_pos]); - const_view_t third_header_raw = {third_header_raw_buf, third_header_raw_len}; + const_header_t third_header_raw = {third_header_raw_buf, + third_header_raw_len}; uint256 previous = {0}; btcspv_extract_target(previous, &first_header_raw); diff --git a/c/csrc/evalspv.c b/c/csrc/evalspv.c index 037cd519..e19352c9 100644 --- a/c/csrc/evalspv.c +++ b/c/csrc/evalspv.c @@ -5,7 +5,7 @@ const uint64_t BTCSPV_ERR_INVALID_CHAIN = 0xfffffffffffffffe; const uint64_t BTCSPV_ERR_LOW_WORK = 0xfffffffffffffffd; bool evalspv_prove(const uint256 txid, const uint256 root, - const_view_t *intermediate_nodes, uint32_t index) { + const_merkle_array_t *intermediate_nodes, uint32_t index) { const uint32_t nodes_len = intermediate_nodes->len; if (UINT256_EQ(txid, root) && index == 0 && nodes_len == 0) { return true; @@ -26,7 +26,7 @@ bool evalspv_prove(const uint256 txid, const uint256 root, } void evalspv_calculate_txid(uint256 txid, const_view_t *version, - const_view_t *vin, const_view_t *vout, + const_vin_t *vin, const_vout_t *vout, const_view_t *locktime) { uint32_t tx_size = (4 + vin->len + vout->len + 4); @@ -55,13 +55,13 @@ bool evalspv_validate_header_work(const uint256 digest, const uint256 target) { return (UINT256_LT(digest_be, target)); } -bool evalspv_validate_header_prev_hash(const_view_t *header, +bool evalspv_validate_header_prev_hash(const_header_t *header, const uint256 prev_hash) { const_view_t actual = btcspv_extract_prev_block_hash_le(header); return btcspv_view_eq_buf(&actual, prev_hash, 32); } -uint64_t evalspv_validate_header_chain(const_view_t *headers) { +uint64_t evalspv_validate_header_chain(const_header_array_t *headers) { if (headers->len % 80 != 0) { return BTCSPV_ERR_BAD_LENGTH; } @@ -71,7 +71,7 @@ uint64_t evalspv_validate_header_chain(const_view_t *headers) { uint32_t num_headers = headers->len / 80; for (int i = 0; i < num_headers; i++) { offset = i * 80; - const_view_t header = {headers->loc + offset, 80}; + const_header_t header = {headers->loc + offset, 80}; // skip on first header if (i != 0 && !evalspv_validate_header_prev_hash(&header, digest)) { @@ -82,7 +82,8 @@ uint64_t evalspv_validate_header_chain(const_view_t *headers) { btcspv_extract_target(target, &header); uint64_t header_work = btcspv_calculate_difficulty(target); - btcspv_hash256(digest, &header); + const_view_t preimage = {header.loc, header.len}; + btcspv_hash256(digest, &preimage); if (header_work == 0 || !(evalspv_validate_header_work(digest, target))) { return BTCSPV_ERR_LOW_WORK; } diff --git a/c/csrc/evalspv.h b/c/csrc/evalspv.h index ba508a37..550d378e 100644 --- a/c/csrc/evalspv.h +++ b/c/csrc/evalspv.h @@ -25,7 +25,7 @@ const uint64_t BTCSPV_ERR_LOW_WORK; /// /// @return true if a valid proof, otherwise false. bool evalspv_prove(const uint256 txid, const uint256 root, - const_view_t *intermediate_nodes, uint32_t index); + const_merkle_array_t *intermediate_nodes, uint32_t index); /// @brief Hashes transaction to get txid /// @note Supports Legacy and Witness @@ -36,7 +36,7 @@ bool evalspv_prove(const uint256 txid, const uint256 root, /// @warning overwrites `txid` with the transaction ID /// @warning caller must ensure `txid` is allocated and can hold 32 bytes void evalspv_calculate_txid(uint256 txid, const_view_t *version, - const_view_t *vin, const_view_t *vout, + const_vin_t *vin, const_vout_t *vout, const_view_t *locktime); /// @brief Checks validity of header work @@ -52,7 +52,7 @@ bool evalspv_validate_header_work(const uint256 header_digest, /// @param _header The raw bytes header /// @param _prevHeaderDigest The previous header's digest /// @return true if the connection is valid, false otherwise -bool evalspv_validate_header_prev_hash(const_view_t *header, +bool evalspv_validate_header_prev_hash(const_header_t *header, const uint256 prev_hash); /// @brief Checks validity of header chain @@ -60,7 +60,8 @@ bool evalspv_validate_header_prev_hash(const_view_t *header, /// @param _headers Raw byte array of header chain /// @return The total accumulated difficulty of the header chain, /// or an error code -/// @warning Caller must check response to ensure it is not an error code -uint64_t evalspv_validate_header_chain(const_view_t *headers); +/// @warning Caller must check response to ensure it is not an error +/// code +uint64_t evalspv_validate_header_chain(const_header_array_t *headers); #endif /* SUMMA_CKB_EVALSPV_H_ */ diff --git a/c/csrc/swap-demo.c b/c/csrc/swap-demo.c index 1a54f517..ddba6d08 100644 --- a/c/csrc/swap-demo.c +++ b/c/csrc/swap-demo.c @@ -85,49 +85,50 @@ bool load_args(uint8_t *view) { return true; } -byte_view_t extract_headers(const_view_t *wit) { +header_array_t extract_headers(const_view_t *wit) { uint8_t num_headers = wit->loc[0]; // drop the length prefix - byte_view_t headers = {wit->loc + 1, 80 * num_headers}; + const_header_array_t headers = {wit->loc + 1, 80 * num_headers}; return headers; } -byte_view_t extract_intermediate_nodes(const_view_t *wit, uint32_t offset) { +const_merkle_array_t extract_intermediate_nodes(const_view_t *wit, + uint32_t offset) { uint32_t num_nodes = wit->loc[offset]; // drop the length prefix - byte_view_t nodes = {wit->loc + 1 + offset, 32 * num_nodes}; + const_merkle_array_t nodes = {wit->loc + 1 + offset, 32 * num_nodes}; return nodes; } -byte_view_t extract_vin(const_view_t *wit, uint32_t offset) { - const_view_t tmp_vin = {wit->loc + offset, wit->len - offset}; +const_vin_t extract_vin(const_view_t *wit, uint32_t offset) { + const_vin_t tmp_vin = {wit->loc + offset, wit->len - offset}; const uint8_t num_ins = tmp_vin.loc[0]; uint32_t vin_bytes = 1; for (int i = 0; i < num_ins; i++) { - const_view_t input = btcspv_extract_input_at_index(&tmp_vin, i); + const_txin_t input = btcspv_extract_input_at_index(&tmp_vin, i); vin_bytes += input.len; } - byte_view_t vin = {wit->loc + offset, vin_bytes}; + const_vin_t vin = {wit->loc + offset, vin_bytes}; return vin; } -byte_view_t extract_vout(const_view_t *wit, uint32_t offset) { - const_view_t tmp_vout = {wit->loc + offset, wit->len - offset}; +const_vout_t extract_vout(const_view_t *wit, uint32_t offset) { + const_vout_t tmp_vout = {wit->loc + offset, wit->len - offset}; const uint8_t num_outs = tmp_vout.loc[0]; uint32_t vout_bytes = 1; for (int i = 0; i < num_outs; i++) { - const_view_t output = btcspv_extract_output_at_index(&tmp_vout, i); + const_txout_t output = btcspv_extract_output_at_index(&tmp_vout, i); vout_bytes += output.len; } - byte_view_t vout = {wit->loc + offset, vout_bytes}; + const_vout_t vout = {wit->loc + offset, vout_bytes}; return vout; } @@ -175,10 +176,11 @@ int main() { uint32_t offset = 0; const_view_t wit = {witness + 20, witness_len - 20 - 8}; - const_view_t headers = extract_headers(&wit); + const_header_array_t headers = extract_headers(&wit); offset += 1 + headers.len; - const_view_t intermediate_nodes = extract_intermediate_nodes(&wit, offset); + const_merkle_array_t intermediate_nodes = + extract_intermediate_nodes(&wit, offset); offset += 1 + intermediate_nodes.len; // offset 0x01d2 == -46 @@ -190,13 +192,21 @@ int main() { offset += 4; // offset 0x01da == -38 - const_view_t vin = extract_vin(&wit, offset); + const_vin_t vin = extract_vin(&wit, offset); offset += vin.len; + if (!btcspv_validate_vin(&vin)) { + return ERROR_BAD_VIN; + } + // offset 0x22d == 45 - const_view_t vout = extract_vout(&wit, offset); + const_vout_t vout = extract_vout(&wit, offset); offset += vout.len; + if (!btcspv_validate_vout(&vout)) { + return ERROR_BAD_VOUT; + } + // offset 0x28b == -117 const_view_t locktime = {wit.loc + offset, 4}; offset += 4; @@ -205,14 +215,6 @@ int main() { return ERROR_BAD_WITNESS; } - if (!btcspv_validate_vin(&vin)) { - return ERROR_BAD_VIN; - } - - if (!btcspv_validate_vout(&vout)) { - return ERROR_BAD_VOUT; - } - // // CHECK BTC TX INCLUSION // @@ -220,7 +222,8 @@ int main() { uint256 txid; evalspv_calculate_txid(txid, &version, &vin, &vout, &locktime); - const_view_t root = btcspv_extract_merkle_root_le(&headers); + const_header_t first = {headers.loc, 80}; + const_view_t root = btcspv_extract_merkle_root_le(&first); bool valid = evalspv_prove(txid, root.loc, &intermediate_nodes, tx_index); if (!valid) { return ERROR_INVALID_MERKLE_PROOF; @@ -272,10 +275,11 @@ int main() { mol_seg_t payee = MolReader_Script_get_code_hash(&output_script); // check that this output pays the right person - const_view_t second_output = btcspv_extract_output_at_index(&vout, 1); - const_view_t expected_payee = btcspv_extract_op_return_data(&second_output); + const_txout_t second_output = btcspv_extract_output_at_index(&vout, 1); + const_op_return_t expected_payee = + btcspv_extract_op_return_data(&second_output); - uint32_t comparison_len = 20; // NB: BAD HACKS. Don't do in prod + uint32_t comparison_len = 20; // NB: BAD HACKS. Don't do in prod if (memcmp(payee.ptr, expected_payee.loc, comparison_len) != 0) { return ERROR_WRONG_PAYEE; } diff --git a/testVectors.json b/testVectors.json index 7bd7f4a4..165559a4 100644 --- a/testVectors.json +++ b/testVectors.json @@ -117,13 +117,6 @@ 0, 1 ] - }, - { - "input": "0x1746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba3000000000FF0000000000000000ffffffff", - "output": [ - 8, - 0 - ] } ], "extractSequenceLELegacy": [ @@ -172,10 +165,6 @@ { "input": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd040000000900000000000000000000000000", "output": 50 - }, - { - "input": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd04000000fdff0000000000", - "output": 298 } ], "extractInputAtIndex": [ @@ -337,16 +326,6 @@ { "input": "0x1746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000001eeffffffff", "output": "0x01ee" - }, - { - "input": "0x1746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba3000000000fd0100eeffffffff", - "output": "0xfd0100ee", - "solidityError": "Non-minimal var int" - }, - { - "input": "0x1746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba3000000000fe01000000eeffffffff", - "output": "0xfe01000000ee", - "solidityError": "Non-minimal var int" } ], "extractScriptSigError": [ @@ -455,12 +434,6 @@ { "input": "0x0000000000000000fc", "output": 261 - }, - { - "comment": "Non-minimal VarInt encoding", - "input": "0x0000000000000000FFfc00000000000000", - "output": 269, - "solidityError": "Non-minimal var int" } ], "determineOutputLengthError": [ @@ -821,11 +794,6 @@ "input": "0x011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffffff", "output": true }, - { - "comment": "Non-minimal VarInt encoding", - "input": "0xFF01000000000000001746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffffff", - "output": true - }, { "input": "0xFF1746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffffff", "output": false @@ -862,11 +830,6 @@ "input": "0x024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211", "output": true }, - { - "comment": "Non-minimal VarInt Encoding", - "input": "0xFF02000000000000004897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211", - "output": true - }, { "input": "0xFF4897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211", "output": false