diff --git a/Makefile b/Makefile index 128f60b20..2fa9dcda8 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,8 @@ endif include $(BOLOS_SDK)/Makefile.defines +ENABLE_PENDING_REVIEW_SCREEN = 1 + # TODO: compile with the right path restrictions # Application allowed derivation curves. @@ -33,7 +35,7 @@ APP_LOAD_PARAMS += --path_slip21 "LEDGER-Wallet policy" # Application version APPVERSION_M = 2 APPVERSION_N = 1 -APPVERSION_P = 5 +APPVERSION_P = 6 APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" APP_STACK_SIZE = 3072 diff --git a/doc/qtum.md b/doc/qtum.md index 10781d318..614bd0069 100644 --- a/doc/qtum.md +++ b/doc/qtum.md @@ -20,7 +20,7 @@ The main commands use `CLA = 0xE1`, unlike the legacy Qtum application that used | E1 | 04 | SIGN_PSBT | Sign a PSBT with a registered or default wallet | | E1 | 05 | GET_MASTER_FINGERPRINT | Return the fingerprint of the master public key | | E1 | 10 | SIGN_MESSAGE | Sign a message with a key from a BIP32 path (Qtum Message Signing) | -| E1 | 81 | SIGN_SENDER_PSBT | Sign an op_sender output | +| E1 | 81 | SIGN_SENDER_PSBT | Sign a contract sender output (op_sender) | The `CLA = 0xF8` is used for framework-specific (rather than app-specific) APDUs; at this time, only one command is present. @@ -330,6 +330,67 @@ The digest being signed is the double-SHA256 of the message, after prefixing the The client must respond to the `GET_PREIMAGE`, `GET_MERKLE_LEAF_PROOF` and `GET_MERKLE_LEAF_INDEX` queries for the Merkle tree of the list of chunks in the message. +### SIGN_SENDER_PSBT + +Given a PSBTv2 and a registered wallet (or a standard one), sign a contract sender output using a wallet address. + +#### Encoding + +**Command** + +| *CLA* | *INS* | +|-------|-------| +| E1 | 81 | + +**Input data** + +| Length | Name | Description | +|---------|------------------------|-------------| +| `1` | `n` | Number of derivation steps (maximum 8) | +| `4` | `bip32_path[0]` | First derivation step (big endian) | +| `4` | `bip32_path[1]` | Second derivation step (big endian) | +| | ... | | +| `4` | `bip32_path[n-1]` | `n`-th derivation step (big endian) | +| `` | `global_map_size` | The number of key/value pairs of the global map of the psbt | +| `32` | `global_map_keys_root` | The Merkle root of the keys of the global map | +| `32` | `global_map_vals_root` | The Merkle root of the values of the global map | +| `` | `n_inputs` | The number of inputs of the psbt | +| `32` | `inputs_maps_root` | The Merkle root of the vector of Merkleized map commitments for the input maps | +| `` | `n_outputs` | The number of outputs of the psbt | +| `32` | `outputs_maps_root` | The Merkle root of the vector of Merkleized map commitments for the output maps | +| `32` | `wallet_id` | The id of the wallet | +| `32` | `wallet_hmac` | The hmac of a registered wallet, or exactly 32 0 bytes | + +**Output data** + +The signature is returned using the YIELD client command. + +#### Description + +Using the information in the PSBT and the wallet description, this command verifies what inputs are internal and what outputs match the pattern for a change address. After validating all the external outputs and the transaction fee with the user, it sign the contract sender output with the `bip32_path` address; the signature is sent to the client using the YIELD command, in the format described below. + +The results yielded via the YIELD command respect the following format: ` `, where: +- `output_index` is a Qtum style varint, the index of the output being signed (starting from 0); +- `pubkey_augm_len` is an unsigned byte equal to the length of `pubkey_augm`; +- `pubkey_augm` is the legacy `pubkey` used for signing; +- `signature` is the returned signature, possibly concatenated with the sighash byte (as it would be pushed on the stack). + +If `P2` is `0` (version `0` of the protocol), `pubkey_augm_len` and `pubkey_augm` are omitted in the YIELD messages. + +For a registered wallet, the hmac must be correct. + +For a default wallet, `hmac` must be equal to 32 bytes `0`. + +#### Client commands + +`GET_PREIMAGE` must know and respond for the full serialized wallet policy whose sha256 hash is `wallet_id`; moreover, it must know and respond for the sha256 hash of its descriptor template. + +The client must respond to the `GET_PREIMAGE`, `GET_MERKLE_LEAF_PROOF` and `GET_MERKLE_LEAF_INDEX` queries for all the Merkle trees in the input, including each of the Merkle trees for keys and values of the Merkleized map commitments of each of the inputs/outputs maps of the psbt. + +The `GET_MORE_ELEMENTS` command must be handled. + +The `YIELD` command must be processed in order to receive the signature. + ## Client commands reference This section documents the commands that the Hardware Wallet can request to the client when returning with a `SW_INTERRUPTED_EXECUTION` status word. @@ -346,7 +407,7 @@ This section documents the commands that the Hardware Wallet can request to the **Command code**: 0x10 -The `YIELD` client command is sent to the client to communicate some result during the execution of a command. Currently only used during `SIGN_PSBT` in order to communicate each of the signatures. The format of the attached message is documented for each command that uses `YIELD`. +The `YIELD` client command is sent to the client to communicate some result during the execution of a command. Currently used during `SIGN_PSBT` and `SIGN_SENDER_PSBT` in order to communicate each of the signatures. The format of the attached message is documented for each command that uses `YIELD`. The client must respond with an empty message. diff --git a/src/boilerplate/dispatcher.c b/src/boilerplate/dispatcher.c index ef8b864ee..810ff1e1d 100644 --- a/src/boilerplate/dispatcher.c +++ b/src/boilerplate/dispatcher.c @@ -58,7 +58,7 @@ static void set_ui_dirty() { // TODO: refactor code in common with the main apdu loop static int process_interruption(dispatcher_context_t *dc) { command_t cmd; - int input_len; + size_t input_len = 0; // Reset structured APDU command memset(&cmd, 0, sizeof(cmd)); @@ -66,9 +66,7 @@ static int process_interruption(dispatcher_context_t *dc) { io_start_interruption_timeout(); // Receive command bytes in G_io_apdu_buffer - if ((input_len = io_exchange(CHANNEL_APDU, G_output_len)) < 0) { - return -1; - } + input_len = io_exchange(CHANNEL_APDU, G_output_len); io_clear_interruption_timeout(); @@ -138,7 +136,7 @@ void apdu_dispatcher(command_descriptor_t const cmd_descriptors[], return; } else { bool cla_found = false, ins_found = false; - command_handler_t handler; + command_handler_t handler = 0; for (int i = 0; i < n_descriptors; i++) { if (cmd_descriptors[i].cla != cmd->cla) continue; cla_found = true; @@ -149,7 +147,10 @@ void apdu_dispatcher(command_descriptor_t const cmd_descriptors[], break; } - if (!cla_found) { + if (!handler) { + io_send_sw(SW_CLA_NOT_SUPPORTED); + return; + } else if (!cla_found) { io_send_sw(SW_CLA_NOT_SUPPORTED); return; } else if (!ins_found) { diff --git a/src/common/base58.c b/src/common/base58.c index bd52d49f7..4ba7f132f 100644 --- a/src/common/base58.c +++ b/src/common/base58.c @@ -91,19 +91,6 @@ int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) { } } - // // original code for reference - // for (uint8_t i = 0; i < in_len; i++) { - // if (in[i] >= sizeof(BASE58_TABLE)) { - // return -1; - // } - - // tmp[i] = BASE58_TABLE[(int) in[i]]; - - // if (tmp[i] == 0xFF) { - // return -1; - // } - // } - while ((zero_count < in_len) && (tmp[zero_count] == 0)) { ++zero_count; } diff --git a/src/common/bip32.c b/src/common/bip32.c index ecae86f98..2a51b2c39 100644 --- a/src/common/bip32.c +++ b/src/common/bip32.c @@ -36,7 +36,7 @@ bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out size_t offset = 0; for (size_t i = 0; i < out_len; i++) { - if (offset > in_len) { + if (offset + 4 > in_len) { return false; } out[i] = read_u32_be(in, offset); @@ -195,4 +195,4 @@ int get_bip44_purpose(int address_type) { default: return -1; } -} \ No newline at end of file +} diff --git a/src/common/buffer.c b/src/common/buffer.c index 9f1679f72..eff3b7cd7 100644 --- a/src/common/buffer.c +++ b/src/common/buffer.c @@ -133,6 +133,10 @@ bool buffer_read_u64(buffer_t *buffer, uint64_t *value, endianness_t endianness) } bool buffer_read_varint(buffer_t *buffer, uint64_t *value) { + if (buffer->ptr == NULL) { + return false; + } + int length = varint_read(buffer->ptr + buffer->offset, buffer->size - buffer->offset, value); if (length < 0) { diff --git a/src/common/merkle.c b/src/common/merkle.c index a4520fe41..c5bd5a7f6 100644 --- a/src/common/merkle.c +++ b/src/common/merkle.c @@ -38,22 +38,6 @@ void merkle_compute_element_hash(const uint8_t *in, size_t in_len, uint8_t out[s crypto_hash_digest(&hash.header, out, 32); } -// void merkle_combine_hashes(const uint8_t left[static 32], -// const uint8_t right[static 32], -// uint8_t out[static 32]) { -// PRINT_STACK_POINTER(); - -// cx_sha256_t hash; -// cx_sha256_init(&hash); - -// // H(0x01 | left | right) -// crypto_hash_update_u8(&hash.header, 0x01); -// crypto_hash_update(&hash.header, left, 32); -// crypto_hash_update(&hash.header, right, 32); - -// crypto_hash_digest(&hash.header, out, 32); -// } - // implementation using the cxram section, in order to save ram void merkle_combine_hashes(const uint8_t left[static 32], const uint8_t right[static 32], @@ -103,4 +87,4 @@ int merkle_get_ith_direction(size_t size, size_t index, size_t i) { } return -1; -} \ No newline at end of file +} diff --git a/src/common/script.c b/src/common/script.c index a411e064c..6aef43d94 100644 --- a/src/common/script.c +++ b/src/common/script.c @@ -84,7 +84,8 @@ int get_script_type(const uint8_t script[], size_t script_len) { #ifndef SKIP_FOR_CMOCKA -// TODO: add unit tests +// crypto.c is disabled in unit tests by Bitcoin, which is needed for get_script_address +// unit tests should be added for script address when it is enabled int get_script_address(const uint8_t script[], size_t script_len, char *out, size_t out_len) { int script_type = get_script_type(script, script_len); int addr_len; diff --git a/src/common/script.h b/src/common/script.h index 5ea64ad9e..7a8c202ba 100644 --- a/src/common/script.h +++ b/src/common/script.h @@ -158,10 +158,10 @@ typedef enum { SCRIPT_TYPE_P2WSH = 0x03, SCRIPT_TYPE_P2TR = 0x04, SCRIPT_TYPE_UNKNOWN_SEGWIT = 0xFF, // a valid but undefined segwit script - SCRIPT_TYPE_CREATE_SENDER, - SCRIPT_TYPE_CALL_SENDER, - SCRIPT_TYPE_CREATE, - SCRIPT_TYPE_CALL, + SCRIPT_TYPE_CREATE_SENDER = 0x100, + SCRIPT_TYPE_CALL_SENDER = 0x101, + SCRIPT_TYPE_CREATE = 0x102, + SCRIPT_TYPE_CALL = 0x103, } script_type_e; static inline bool is_p2wpkh(const uint8_t script[], size_t script_len) { diff --git a/src/handler/get_extended_pubkey.c b/src/handler/get_extended_pubkey.c index 2258a3f08..98b1a3700 100644 --- a/src/handler/get_extended_pubkey.c +++ b/src/handler/get_extended_pubkey.c @@ -63,8 +63,12 @@ static bool is_path_safe_for_pubkey_export(const uint32_t bip32_path[], break; case 45: // BIP-45 prescribes simply length 1, but we instead support existing deployed - // use cases with path "m/45'/coin_type'/account' - hardened_der_len = 3; + // use cases with path "m/45'/coin_type'/account' or "m/45'/coin_type'/account + if (bip32_path[2] < 0x80000000) { + hardened_der_len = 2; + } else { + hardened_der_len = 3; + } break; case 48: hardened_der_len = 4; diff --git a/src/handler/lib/check_merkle_tree_sorted.c b/src/handler/lib/check_merkle_tree_sorted.c index 3c1f16f15..b0dc7fbf6 100644 --- a/src/handler/lib/check_merkle_tree_sorted.c +++ b/src/handler/lib/check_merkle_tree_sorted.c @@ -16,8 +16,6 @@ int call_check_merkle_tree_sorted_with_callback(dispatcher_context_t *dispatcher size_t size, merkle_tree_elements_callback_t callback, const merkleized_map_commitment_t *map_commitment) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - int prev_el_len = 0; uint8_t prev_el[MAX_CHECK_MERKLE_TREE_SORTED_PREIMAGE_SIZE]; @@ -76,4 +74,4 @@ static int compare_byte_arrays(const uint8_t array1[], } return memcmp_result; -} \ No newline at end of file +} diff --git a/src/handler/lib/get_merkle_leaf_element.c b/src/handler/lib/get_merkle_leaf_element.c index 5ab4b041b..1afa2765c 100644 --- a/src/handler/lib/get_merkle_leaf_element.c +++ b/src/handler/lib/get_merkle_leaf_element.c @@ -9,8 +9,6 @@ int call_get_merkle_leaf_element(dispatcher_context_t *dispatcher_context, uint32_t leaf_index, uint8_t *out_ptr, size_t out_ptr_len) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - uint8_t leaf_hash[32]; int res = call_get_merkle_leaf_hash(dispatcher_context, diff --git a/src/handler/lib/get_merkle_leaf_hash.c b/src/handler/lib/get_merkle_leaf_hash.c index 0da741073..32bc50097 100644 --- a/src/handler/lib/get_merkle_leaf_hash.c +++ b/src/handler/lib/get_merkle_leaf_hash.c @@ -17,8 +17,6 @@ int call_get_merkle_leaf_hash(dispatcher_context_t *dc, uint32_t tree_size, uint32_t leaf_index, uint8_t out[static 32]) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - PRINT_STACK_POINTER(); { // make sure memory is deallocated as soon as possible diff --git a/src/handler/lib/get_merkle_leaf_index.c b/src/handler/lib/get_merkle_leaf_index.c index 66897781f..9d1b5a3fd 100644 --- a/src/handler/lib/get_merkle_leaf_index.c +++ b/src/handler/lib/get_merkle_leaf_index.c @@ -9,8 +9,6 @@ int call_get_merkle_leaf_index(dispatcher_context_t *dispatcher_context, size_t size, const uint8_t root[static 32], const uint8_t leaf_hash[static 32]) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - { // free memory as soon as possible uint8_t request[1 + 32 + 32]; request[0] = CCMD_GET_MERKLE_LEAF_INDEX; diff --git a/src/handler/lib/get_merkle_preimage.c b/src/handler/lib/get_merkle_preimage.c index f7106b351..319c57cf5 100644 --- a/src/handler/lib/get_merkle_preimage.c +++ b/src/handler/lib/get_merkle_preimage.c @@ -17,8 +17,6 @@ int call_get_merkle_preimage(dispatcher_context_t *dispatcher_context, const uint8_t hash[static 32], uint8_t *out_ptr, size_t out_ptr_len) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - PRINT_STACK_POINTER(); uint8_t cmd = CCMD_GET_PREIMAGE; diff --git a/src/handler/lib/get_merkleized_map.c b/src/handler/lib/get_merkleized_map.c index 1e6e7a914..ad9da6f6c 100644 --- a/src/handler/lib/get_merkleized_map.c +++ b/src/handler/lib/get_merkleized_map.c @@ -14,8 +14,6 @@ int call_get_merkleized_map_with_callback(dispatcher_context_t *dispatcher_conte int index, merkle_tree_elements_callback_t callback, merkleized_map_commitment_t *out_ptr) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - uint8_t raw_output[9 + 2 * 32]; // maximum size of serialized result (9 bytes for the varint, // and the 2 Merkle roots) @@ -42,4 +40,4 @@ int call_get_merkleized_map_with_callback(dispatcher_context_t *dispatcher_conte out_ptr->size, callback, out_ptr); -} \ No newline at end of file +} diff --git a/src/handler/lib/get_merkleized_map_value.c b/src/handler/lib/get_merkleized_map_value.c index f9baf2fe9..680f7eb0e 100644 --- a/src/handler/lib/get_merkleized_map_value.c +++ b/src/handler/lib/get_merkleized_map_value.c @@ -11,8 +11,6 @@ int call_get_merkleized_map_value(dispatcher_context_t *dispatcher_context, int key_len, uint8_t *out, int out_len) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - uint8_t key_merkle_hash[32]; merkle_compute_element_hash(key, key_len, key_merkle_hash); @@ -30,4 +28,4 @@ int call_get_merkleized_map_value(dispatcher_context_t *dispatcher_context, index, out, out_len); -} \ No newline at end of file +} diff --git a/src/handler/lib/get_merkleized_map_value_hash.c b/src/handler/lib/get_merkleized_map_value_hash.c index 29ee4b08b..3f638d644 100644 --- a/src/handler/lib/get_merkleized_map_value_hash.c +++ b/src/handler/lib/get_merkleized_map_value_hash.c @@ -10,8 +10,6 @@ int call_get_merkleized_map_value_hash(dispatcher_context_t *dispatcher_context, const uint8_t *key, int key_len, uint8_t out[static 32]) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - uint8_t key_merkle_hash[32]; merkle_compute_element_hash(key, key_len, key_merkle_hash); diff --git a/src/handler/lib/get_preimage.c b/src/handler/lib/get_preimage.c index b5f5f5cd7..0183beaf4 100644 --- a/src/handler/lib/get_preimage.c +++ b/src/handler/lib/get_preimage.c @@ -10,8 +10,6 @@ int call_get_preimage(dispatcher_context_t *dispatcher_context, const uint8_t hash[static 32], uint8_t *out, size_t out_len) { - // LOG_PROCESSOR(__FILE__, __LINE__, __func__); - uint8_t cmd = CCMD_GET_PREIMAGE; dispatcher_context->add_to_response(&cmd, 1); uint8_t zero = 0; diff --git a/src/handler/sign_psbt.c b/src/handler/sign_psbt.c index e1686f60f..65f704d21 100644 --- a/src/handler/sign_psbt.c +++ b/src/handler/sign_psbt.c @@ -1369,7 +1369,7 @@ static bool read_outputs(dispatcher_context_t *dc, for (unsigned int cur_output_index = 0; cur_output_index < st->n_outputs; cur_output_index++) { output_info_t output; memset(&output, 0, sizeof(output)); - bool isOpSender = false; + bool signOpSender = false; output_keys_callback_data_t callback_data = {.output = &output, .placeholder_info = placeholder_info}; @@ -1449,13 +1449,35 @@ static bool read_outputs(dispatcher_context_t *dc, } } + // check sender signature present for contract output + if (!dry_run && + is_opsender(output.in_out.scriptPubKey, output.in_out.scriptPubKey_len)) { + uint8_t *sig = 0; + unsigned int sigSize = 0; + if (get_sender_sig(output.in_out.scriptPubKey, + output.in_out.scriptPubKey_len, + &sig, + &sigSize)) { + if (hash) { + // sender signature present, incorrect data for sign sender + SEND_SW(dc, SW_INCORRECT_DATA); + return false; + } + } else { + if (hash) { + // the sender need to be signed + signOpSender = true; + } else { + // sender signature not present, incorrect data for sign tranaction + SEND_SW(dc, SW_INCORRECT_DATA); + return false; + } + } + } + if (!dry_run && !display_output(dc, st, cur_output_index, external_outputs_count, &output)) return false; - if (hash) { - isOpSender = - is_opsender(output.in_out.scriptPubKey, output.in_out.scriptPubKey_len); - } } else if (!dry_run) { // valid change address, nothing to show to the user @@ -1463,7 +1485,7 @@ static bool read_outputs(dispatcher_context_t *dc, ++st->outputs.n_change; } - if (isOpSender) { + if (signOpSender) { { cx_sha256_t sighash_context; diff --git a/src/main.c b/src/main.c index eee5986ea..db3e7a7ee 100644 --- a/src/main.c +++ b/src/main.c @@ -112,7 +112,7 @@ const command_descriptor_t COMMAND_DESCRIPTORS[] = { void app_main() { for (;;) { // Length of APDU command received in G_io_apdu_buffer - int input_len = 0; + size_t input_len = 0; // Structured APDU command command_t cmd; @@ -123,11 +123,6 @@ void app_main() { input_len = io_exchange(CHANNEL_APDU | IO_ASYNCH_REPLY, 0); - if (input_len < 0) { - PRINTF("=> io_exchange error\n"); - return; - } - // Reset structured APDU command memset(&cmd, 0, sizeof(cmd)); // Parse APDU command from G_io_apdu_buffer @@ -315,7 +310,6 @@ static void swap_library_main_helper(struct libargs_s *args) { USB_power(0); USB_power(1); - // ui_idle(); PRINTF("USB power ON/OFF\n"); #ifdef HAVE_BLE // grab the current plane mode setting diff --git a/src/swap/swap_lib_calls.h b/src/swap/swap_lib_calls.h index d41d37ab9..6fe63ce6f 100644 --- a/src/swap/swap_lib_calls.h +++ b/src/swap/swap_lib_calls.h @@ -35,7 +35,6 @@ typedef struct get_printable_amount_parameters_s { bool is_fee; // OUT char printable_amount[30]; - // int result; } get_printable_amount_parameters_t; typedef struct create_transaction_parameters_s { diff --git a/src/ui/display.h b/src/ui/display.h index b66159ded..59da1c554 100644 --- a/src/ui/display.h +++ b/src/ui/display.h @@ -57,7 +57,7 @@ typedef struct { char address_or_description[MAX(MAX_ADDRESS_LENGTH_STR + 1, MAX_OPRETURN_OUTPUT_DESC_SIZE_SHORT)]; char amount[MAX_AMOUNT_LENGTH + 1]; - char staker_fee[sizeof("99 #")]; + char staker_fee[sizeof("999 %")]; } ui_validate_output_state_t; typedef struct { diff --git a/src/ui/display_utils.c b/src/ui/display_utils.c index 30ce98c14..c460aef1d 100644 --- a/src/ui/display_utils.c +++ b/src/ui/display_utils.c @@ -57,19 +57,12 @@ void format_sats_amount(const char *coin_name, char *amount_str = out + coin_name_len + 1; - // HACK: avoid __udivmoddi4 - // uint64_t integral_part = amount / 100000000; - // uint32_t fractional_part = (uint32_t) (amount % 100000000); uint64_t integral_part = div100000000(amount); uint32_t fractional_part = (uint32_t) (amount - integral_part * 100000000); // format the integral part, starting from the least significant digit size_t integral_part_digit_count = n_digits(integral_part); for (unsigned int i = 0; i < integral_part_digit_count; i++) { - // HACK: avoid __udivmoddi4 - // amount_str[integral_part_digit_count - 1 - i] = '0' + (integral_part % 10); - // integral_part /= 10; - uint64_t tmp_quotient = div10(integral_part); char tmp_remainder = (char) (integral_part - 10 * tmp_quotient); amount_str[integral_part_digit_count - 1 - i] = '0' + tmp_remainder; diff --git a/unit-tests/test_script.c b/unit-tests/test_script.c index 1361d49ea..c745a3084 100644 --- a/unit-tests/test_script.c +++ b/unit-tests/test_script.c @@ -270,6 +270,153 @@ static void test_format_opscript_script_invalid(void **state) { CHECK_INVALID_TESTCASE(input_extra_push2); } +static void test_format_contract_script_valid(void **state) { + (void) state; + + // check valid create contract + uint8_t op_create[] = {0x01, 0x04, 0x03, 0xa0, 0x25, 0x26, 0x01, 0x28, 0x02, 0x60, 0x80, 0xc1}; + assert_int_equal(get_script_type(op_create, sizeof(op_create)), SCRIPT_TYPE_CREATE); + + // check valid call contract + uint8_t op_call[] = {0x01, 0x04, 0x03, 0x90, 0xd0, 0x03, 0x01, 0x28, 0x44, 0xa9, 0x05, 0x9c, + 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x25, 0x8d, 0x11, 0xe4, 0x16, 0x1c, 0x74, 0x7d, 0x84, 0x1f, + 0x32, 0x9b, 0xf4, 0x3f, 0x41, 0xad, 0x86, 0x46, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, + 0x46, 0x09, 0x13, 0xd0, 0x00, 0x00, 0x14, 0xf2, 0xa6, 0x13, 0x71, 0x7a, + 0x2c, 0x44, 0xbc, 0x2d, 0xfb, 0x84, 0x2e, 0x48, 0xdc, 0x16, 0x3e, 0x71, + 0x27, 0x27, 0x83, 0xc2}; + assert_int_equal(get_script_type(op_call, sizeof(op_call)), SCRIPT_TYPE_CALL); + + // check valid create sender contract + uint8_t op_create_sender[] = {0x01, 0x01, 0x14, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, 0xdf, + 0x5c, 0xda, 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, 0x91, + 0x19, 0x4c, 0x6b, 0x6a, 0x47, 0x30, 0x44, 0x02, 0x20, 0x1b, 0x9a, + 0x7a, 0x0a, 0x7c, 0xde, 0xed, 0x52, 0xd8, 0x6f, 0x1c, 0xff, 0xf4, + 0xa9, 0x10, 0x57, 0xcb, 0x9a, 0xc9, 0x59, 0x63, 0xdb, 0x90, 0x08, + 0x40, 0xf2, 0xc0, 0x59, 0x53, 0x73, 0x70, 0x0e, 0x02, 0x20, 0x41, + 0x2b, 0xc3, 0x02, 0x96, 0x81, 0x10, 0x3c, 0xa8, 0x67, 0x9d, 0xce, + 0x56, 0xa9, 0x56, 0xb6, 0x65, 0xc9, 0xc9, 0x17, 0x13, 0x02, 0xec, + 0x86, 0xbd, 0xbe, 0x94, 0x6f, 0x4b, 0x0d, 0x0f, 0xce, 0x01, 0x21, + 0x03, 0xe6, 0xd0, 0xdd, 0xf1, 0x4b, 0xc1, 0xb0, 0x58, 0x9c, 0x64, + 0x19, 0x0d, 0xb1, 0xcc, 0xd7, 0x03, 0x69, 0x3a, 0xdc, 0xc4, 0x0f, + 0xd6, 0x03, 0x50, 0xa9, 0xff, 0xe5, 0xf2, 0x89, 0x98, 0x82, 0x2f, + 0xc4, 0x01, 0x04, 0x03, 0xa0, 0x25, 0x26, 0x01, 0x28, 0x02, 0x60, + 0x80, 0xc1}; + assert_int_equal(get_script_type(op_create_sender, sizeof(op_create_sender)), SCRIPT_TYPE_CREATE_SENDER); + + // check valid call sender contract + uint8_t op_call_sender[] = {0x01, 0x01, 0x14, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, 0xdf, + 0x5c, 0xda, 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, 0x91, + 0x19, 0x4c, 0x6b, 0x6a, 0x47, 0x30, 0x44, 0x02, 0x20, 0x10, 0x04, + 0x31, 0x79, 0x72, 0xb7, 0xec, 0x0c, 0x89, 0x2c, 0x71, 0x3b, 0x1c, + 0x4f, 0x43, 0x78, 0xad, 0x58, 0xb4, 0xb3, 0xe8, 0xc2, 0x55, 0x91, + 0x72, 0xf1, 0x70, 0xb9, 0x43, 0x9e, 0x0c, 0xd8, 0x02, 0x20, 0x7e, + 0x18, 0x9b, 0x59, 0xab, 0x77, 0x30, 0xbc, 0x56, 0x24, 0xd3, 0x23, + 0x45, 0x32, 0xf6, 0x6f, 0x6d, 0x51, 0x98, 0xfd, 0xe9, 0x35, 0xc4, + 0xa1, 0x1c, 0x95, 0xee, 0x50, 0x75, 0xe9, 0xe8, 0xc1, 0x01, 0x21, + 0x03, 0xe6, 0xd0, 0xdd, 0xf1, 0x4b, 0xc1, 0xb0, 0x58, 0x9c, 0x64, + 0x19, 0x0d, 0xb1, 0xcc, 0xd7, 0x03, 0x69, 0x3a, 0xdc, 0xc4, 0x0f, + 0xd6, 0x03, 0x50, 0xa9, 0xff, 0xe5, 0xf2, 0x89, 0x98, 0x82, 0x2f, + 0xc4, 0x01, 0x04, 0x03, 0x90, 0xd0, 0x03, 0x01, 0x28, 0x44, 0xa9, + 0x05, 0x9c, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, + 0xdf, 0x5c, 0xda, 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, + 0x91, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x00, + 0x00, 0x14, 0xf2, 0xa6, 0x13, 0x71, 0x7a, 0x2c, 0x44, 0xbc, 0x2d, + 0xfb, 0x84, 0x2e, 0x48, 0xdc, 0x16, 0x3e, 0x71, 0x27, 0x27, 0x83, + 0xc2}; + assert_int_equal(get_script_type(op_call_sender, sizeof(op_call_sender)), SCRIPT_TYPE_CALL_SENDER); + + uint8_t sender_script[] = {0x19, 0x76, 0xa9, 0x14, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, + 0xdf, 0x5c, 0xda, 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, + 0x91, 0x19, 0x88, 0xac}; + + // check valid sender script for create sender contract + uint8_t script_code[26]; + memset(script_code, 0, sizeof(script_code)); + assert_int_equal(get_script_sender_address(op_create_sender, sizeof(op_create_sender), script_code), 1); + assert_int_equal(strncmp((char*)script_code, (char*)sender_script, sizeof(script_code)), 0); + + // check valid sender script for call sender contract + memset(script_code, 0, sizeof(script_code)); + assert_int_equal(get_script_sender_address(op_call_sender, sizeof(op_call_sender), script_code), 1); + assert_int_equal(strncmp((char*)script_code, (char*)sender_script, sizeof(script_code)), 0); + + // check valid sender signature for create sender contract + uint8_t *sig = 0; + unsigned int sigSize = 0; + assert_int_equal(get_sender_sig(op_create_sender, sizeof(op_create_sender), &sig, &sigSize), 1); + assert_int_equal(sigSize, 107); + + // check valid sender signature for call sender contract + sig = 0; + sigSize = 0; + assert_int_equal(get_sender_sig(op_call_sender, sizeof(op_call_sender), &sig, &sigSize), 1); + assert_int_equal(sigSize, 107); +} + +static void test_format_contract_script_invalid(void **state) { + (void) state; + + // check invalid create contract + uint8_t op_create[] = {0x01, 0x04, 0x03, 0xa0, 0x26, 0x01, 0x28, 0x02, 0x60, 0x80, 0xc1}; + assert_int_equal(get_script_type(op_create, sizeof(op_create)), -1); + + // check invalid call contract + uint8_t op_call[] = {0x01, 0x04, 0x03, 0x90, 0x03, 0x01, 0x28, 0x44, 0x71, 0x27, 0x27, 0x83, 0xc2}; + assert_int_equal(get_script_type(op_call, sizeof(op_call)), -1); + + // check invalid create sender contract + uint8_t op_create_sender[] = {0x01, 0x14, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, 0xdf, 0x5c, + 0xda, 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, 0x91, 0x19, + 0x4c, 0x6b, 0x6a, 0x47, 0x30, 0x44, 0x02, 0x20, 0x1b, 0x9a, 0x7a, + 0x03, 0x69, 0x3a, 0xdc, 0xc4, 0x0f, 0xd6, 0x03, 0x50, 0xa9, 0xff, + 0x0a, 0x7c, 0xde, 0xed, 0x52, 0xd8, 0x19, 0x0d, 0xb1, 0xcc, 0xd7, + 0xe5, 0xf2, 0x89, 0x98, 0x82, 0x2f, 0xc4, 0x01, 0x04, 0x03, 0xa0, + 0x25, 0x26, 0x01, 0x28, 0x02, 0x60, 0xc1}; + assert_int_equal(get_script_type(op_create_sender, sizeof(op_create_sender)), -1); + + // check invalid call sender contract + uint8_t op_call_sender[] = {0x01, 0x14, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, 0xdf, 0x5c, 0xda, + 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, 0x91, 0x19, 0x4c, 0x6b, + 0x6a, 0x47, 0x30, 0x44, 0x02, 0x20, 0x10, 0x04, 0x31, 0x79, 0x72, 0xb7, + 0xec, 0x0c, 0x89, 0x2c, 0x19, 0x0d, 0xb1, 0xcc, 0xd7, 0x03, 0x69, 0x3a, + 0xdc, 0xc4, 0x0f, 0xd6, 0x03, 0x50, 0xa9, 0xff, 0xe5, 0xf2, 0x89, 0x98, + 0x82, 0x2f, 0xc4, 0x01, 0x04, 0x03, 0x90, 0xd0, 0x03, 0x01, 0x28, 0x44, + 0xa9, 0x05, 0x9c, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0xdc, 0x89, 0x49, 0xa4, 0x5c, 0x94, 0xdf, + 0x5c, 0xda, 0xed, 0x2b, 0x6e, 0x14, 0x4d, 0x90, 0x57, 0xdd, 0x91, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x15, 0x8e, 0x46, 0x09, 0x13, 0xd0, 0x00, 0x00, 0x14, 0xf2, 0xa6, 0x13, + 0x71, 0x7a, 0x2c, 0x44, 0xbc, 0x2d, 0xfb, 0x84, 0x2e, 0x48, 0xdc, 0x16, + 0x3e, 0x71, 0x27, 0x27, 0xc2}; + assert_int_equal(get_script_type(op_call_sender, sizeof(op_call_sender)), -1); + + // check invalid sender script for create sender contract + uint8_t script_code[26]; + memset(script_code, 0, sizeof(script_code)); + assert_int_equal(get_script_sender_address(op_create_sender, sizeof(op_create_sender), script_code), 0); + + // check invalid sender script for call sender contract + memset(script_code, 0, sizeof(script_code)); + assert_int_equal(get_script_sender_address(op_call_sender, sizeof(op_call_sender), script_code), 0); + + // check invalid sender signature for create sender contract + uint8_t *sig = 0; + unsigned int sigSize = 0; + assert_int_equal(get_sender_sig(op_create_sender, sizeof(op_create_sender), &sig, &sigSize), 0); + + // check invalid sender signature for call sender contract + sig = 0; + sigSize = 0; + assert_int_equal(get_sender_sig(op_call_sender, sizeof(op_call_sender), &sig, &sigSize), 0); +} + int main() { const struct CMUnitTest tests[] = { cmocka_unit_test(test_get_push_script_size), @@ -277,6 +424,8 @@ int main() { cmocka_unit_test(test_get_script_type_invalid), cmocka_unit_test(test_format_opscript_script_valid), cmocka_unit_test(test_format_opscript_script_invalid), + cmocka_unit_test(test_format_contract_script_valid), + cmocka_unit_test(test_format_contract_script_invalid), }; return cmocka_run_group_tests(tests, NULL, NULL);