From 5b29d8e00d8dca752149bc2b8d7d568a09da1974 Mon Sep 17 00:00:00 2001 From: Ariel Mendelzon Date: Thu, 4 Apr 2024 12:06:08 +1300 Subject: [PATCH 1/2] Implemented authorised segwit transaction signing - Implemented signer changes to allow for both legacy and segwit sighash computation and signing - Implemented new signing operation and changes in existing signing operations in middleware to support ledger changes - Added ledger tests support for segwit signing tests - Added new ledger test cases covering multiple segwit signing use cases - Added new and updated existing middleware unit tests - Updated protocol documentation to reflect new signing operation and changes in existing signing operations --- docs/protocol.md | 57 +- ledger/src/signer/src/auth.h | 2 + ledger/src/signer/src/auth_tx.c | 232 +++++- ledger/src/signer/src/auth_tx.h | 29 + ledger/test/cases/sign_auth.py | 37 +- .../resources/203-get-after-sign-iris.json | 16 + ...son => 204-heartbeat-after-sign-iris.json} | 0 .../resources/210-advance-for-segwit.json | 13 + ledger/test/resources/211-update-segwit.json | 8 + .../212-get-after-advance-segwit.json | 10 + .../test/resources/213-sign-segwit-t1i0.json | 15 + .../test/resources/214-sign-segwit-t1i1.json | 15 + .../test/resources/215-sign-segwit-t2i0.json | 13 + .../test/resources/216-sign-segwit-t2i1.json | 15 + .../299-advance-cap-for-partial-check.json | 12 + ledger/test/resources/300-update.json | 2 + .../test/resources/301-get-after-update.json | 4 +- ledger/test/resources/303-get-after-sign.json | 4 +- .../resources/305-get-after-sign-noauth.json | 4 +- .../resources/306-heartbeat-after-sign.json | 2 +- .../resources/308-get-after-reconnection.json | 4 +- .../309-heartbeat-after-reconnection.json | 2 +- .../test/resources/401-get-before-sign.json | 4 +- .../resources/499-get-after-failed-sign.json | 4 +- .../509-get-after-failed-advance.json | 4 +- .../test/resources/601-get-before-sign.json | 4 +- .../resources/617-get-after-success-sign.json | 4 +- .../test/resources/620-sign-segwit-basic.json | 17 + .../621-sign-segwit-with-many-inputs.json | 17 + .../622-sign-segwit-long-witnessscript.json | 17 + middleware/comm/bitcoin.py | 31 + middleware/comm/protocol.py | 46 +- middleware/comm/utils.py | 17 + middleware/ledger/hsm2dongle.py | 87 +- middleware/ledger/protocol.py | 12 +- middleware/tests/comm/test_protocol.py | 348 +++++++- .../hsm2dongle_cmds/test_signer_heartbeat.py | 4 +- .../hsm2dongle_cmds/test_ui_heartbeat.py | 4 +- middleware/tests/ledger/test_hsm2dongle.py | 772 ++---------------- .../test_hsm2dongle_sign_auth_legacy.py | 272 ++++++ .../test_hsm2dongle_sign_auth_segwit.py | 149 ++++ middleware/tests/ledger/test_protocol.py | 356 +++++++- 42 files changed, 1852 insertions(+), 813 deletions(-) create mode 100644 ledger/test/resources/203-get-after-sign-iris.json rename ledger/test/resources/{203-heartbeat-after-sign-iris.json => 204-heartbeat-after-sign-iris.json} (100%) create mode 100644 ledger/test/resources/210-advance-for-segwit.json create mode 100644 ledger/test/resources/211-update-segwit.json create mode 100644 ledger/test/resources/212-get-after-advance-segwit.json create mode 100644 ledger/test/resources/213-sign-segwit-t1i0.json create mode 100644 ledger/test/resources/214-sign-segwit-t1i1.json create mode 100644 ledger/test/resources/215-sign-segwit-t2i0.json create mode 100644 ledger/test/resources/216-sign-segwit-t2i1.json create mode 100644 ledger/test/resources/299-advance-cap-for-partial-check.json create mode 100644 ledger/test/resources/620-sign-segwit-basic.json create mode 100644 ledger/test/resources/621-sign-segwit-with-many-inputs.json create mode 100644 ledger/test/resources/622-sign-segwit-long-witnessscript.json create mode 100644 middleware/tests/ledger/test_hsm2dongle_sign_auth_legacy.py create mode 100644 middleware/tests/ledger/test_hsm2dongle_sign_auth_segwit.py diff --git a/docs/protocol.md b/docs/protocol.md index 46a078fd..c9fd7f60 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -37,25 +37,60 @@ For this operation, depending on the `keyId` parameter, there's two possible for ##### Authorized format -This format is only valid for the BTC and tBTC key ids (see corresponding section for details). +This format is only valid for the BTC and tBTC key ids (see corresponding section for +details). In addition, there are two different sub-formats that can be used for authorized +signing: legacy and segwit. These are shown in the following subsections. + +###### Legacy BTC transactions + +This sub-format is to be used when signing legacy (i.e., non-segwit) Bitcoin transaction +inputs. ``` { "command": "sign", "keyId": "xxxxx", // (*) "message": { - tx: "hhhh", // (**) - input: i // (***) + "sighashComputationMode": "legacy", + "tx": "hhhh", // (**) + "input": i // (***) }, "auth": { - receipt: "hhhh", - receipt_merkle_proof: [ + "receipt": "hhhh", + "receipt_merkle_proof": [ "hhhh", "hhhh", ..., "hhhh" ] }, "version": 4 } +``` +###### Segwit BTC transactions + +This sub-format is to be used when signing segwit Bitcoin transaction inputs. + +``` +{ + "command": "sign", + "keyId": "xxxxx", // (*) + "message": { + "sighashComputationMode": "segwit", + "tx": "hhhh", // (**) + "input": i, // (***) + "witnessScript": "hhhh", // (x) + "outpointValue": i // (xx) + }, + "auth": { + "receipt": "hhhh", + "receipt_merkle_proof": [ + "hhhh", "hhhh", ..., "hhhh" + ] + }, + "version": 4 +} +``` + +``` // (*) the given string must be the // BIP44 path of the key to use for signing. // See valid BIP44 paths below (BTC and tBTC for this format). @@ -63,6 +98,9 @@ This format is only valid for the BTC and tBTC key ids (see corresponding sectio // that needs to be signed. // (***) the input index of the BTC transaction // that needs to be signed. +// (x) the witness script for the input that needs to be signed. +// (xx) the outpoint value (i.e., amount of the UTXO) for the input +// that needs to be signed. // // For the signing process to be successful, the computed receipts trie root // must match the device's current 'ancestor_receipts_root'. @@ -72,14 +110,15 @@ This format is only valid for the BTC and tBTC key ids (see corresponding sectio ##### Non-authorized format -This format is only valid for the RSK, MST, tRSK and tMST key ids (see corresponding section for details). +This format is only valid for the RSK, MST, tRSK and tMST key ids (see corresponding +section for details). ``` { "command": "sign", "keyId": "xxxxx", // (*) "message": { - hash: "hhhh", // (**) + "hash": "hhhh", // (**) }, "version": 4 } @@ -290,7 +329,7 @@ This operation can return `0` and generic errors. See the error codes section fo ``` { "command": "signerHeartbeat", - "udValue: "hhhh" (*), + "udValue": "hhhh" (*), "version": 4 } @@ -324,7 +363,7 @@ This operation can return `0`, `-301` and generic errors. See the error codes se ``` { "command": "uiHeartbeat", - "udValue: "hhhh" (*), + "udValue": "hhhh" (*), "version": 4 } diff --git a/ledger/src/signer/src/auth.h b/ledger/src/signer/src/auth.h index 65620b39..644b465c 100644 --- a/ledger/src/signer/src/auth.h +++ b/ledger/src/signer/src/auth.h @@ -74,6 +74,8 @@ typedef enum { ERR_AUTH_RECEIPT_HASH_MISMATCH = 0x6A94, ERR_AUTH_NODE_CHAINING_MISMATCH = 0x6A95, ERR_AUTH_RECEIPT_ROOT_MISMATCH = 0x6A96, + ERR_AUTH_INVALID_SIGHASH_COMPUTATION_MODE = 0x6A97, + ERR_AUTH_INVALID_EXTRADATA_SIZE = 0x6A98, } err_code_sign_t; #define AUTH_MAX_EXCHANGE_SIZE RLP_BUFFER_SIZE diff --git a/ledger/src/signer/src/auth_tx.c b/ledger/src/signer/src/auth_tx.c index cfab409f..ceb3b073 100644 --- a/ledger/src/signer/src/auth_tx.c +++ b/ledger/src/signer/src/auth_tx.c @@ -27,6 +27,7 @@ #include "auth.h" #include "svarint.h" #include "mem.h" +#include "memutil.h" #include "dbg.h" @@ -122,6 +123,106 @@ static void btctx_cb(const btctx_cb_event_t event) { } } +static void btctx_cb_segwit(const btctx_cb_event_t event) { + // Update txhash + sha256_update(&auth.tx.tx_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size); + + if (event == BTCTX_EV_VERSION) { + sha256_update( + &auth.tx.sig_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size); + } + + if (event == BTCTX_EV_VIN_COUNT) { + sha256_init(&auth.tx.prevouts_hash_ctx); + sha256_init(&auth.tx.sequence_hash_ctx); + auth.tx.aux_offset = 0; + } + + if (event == BTCTX_EV_VIN_TXH_DATA || event == BTCTX_EV_VIN_TXIX) { + sha256_update( + &auth.tx.prevouts_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size); + + if (auth.tx.ctx.inout_current == auth.input_index_to_sign) { + SAFE_MEMMOVE(auth.tx.ip_prevout, + sizeof(auth.tx.ip_prevout), + auth.tx.aux_offset, + auth.tx.ctx.raw, + sizeof(auth.tx.ctx.raw), + MEMMOVE_ZERO_OFFSET, + auth.tx.ctx.raw_size, + THROW(ERR_AUTH_INVALID_DATA_SIZE)); + auth.tx.aux_offset += auth.tx.ctx.raw_size; + } + } + + if (event == BTCTX_EV_VIN_SEQNO) { + sha256_update( + &auth.tx.sequence_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size); + + if (auth.tx.ctx.inout_current == auth.input_index_to_sign) { + SAFE_MEMMOVE(auth.tx.ip_seqno, + sizeof(auth.tx.ip_seqno), + MEMMOVE_ZERO_OFFSET, + auth.tx.ctx.raw, + sizeof(auth.tx.ctx.raw), + MEMMOVE_ZERO_OFFSET, + auth.tx.ctx.raw_size, + THROW(ERR_AUTH_INVALID_DATA_SIZE)); + } + } + + if (event == BTCTX_EV_VOUT_COUNT) { + sha256_final(&auth.tx.prevouts_hash_ctx, auth.tx.aux_hash); + sha256_init(&auth.tx.prevouts_hash_ctx); + sha256_update(&auth.tx.prevouts_hash_ctx, + auth.tx.aux_hash, + sizeof(auth.tx.aux_hash)); + sha256_final(&auth.tx.prevouts_hash_ctx, auth.tx.aux_hash); + sha256_update( + &auth.tx.sig_hash_ctx, auth.tx.aux_hash, sizeof(auth.tx.aux_hash)); + + sha256_final(&auth.tx.sequence_hash_ctx, auth.tx.aux_hash); + sha256_init(&auth.tx.sequence_hash_ctx); + sha256_update(&auth.tx.sequence_hash_ctx, + auth.tx.aux_hash, + sizeof(auth.tx.aux_hash)); + sha256_final(&auth.tx.sequence_hash_ctx, auth.tx.aux_hash); + sha256_update( + &auth.tx.sig_hash_ctx, auth.tx.aux_hash, sizeof(auth.tx.aux_hash)); + + // Previously saved outpoint of input to sign + sha256_update(&auth.tx.sig_hash_ctx, + auth.tx.ip_prevout, + sizeof(auth.tx.ip_prevout)); + + sha256_init(&auth.tx.outputs_hash_ctx); + } + + if (event == BTCTX_EV_VOUT_VALUE || event == BTCTX_EV_VOUT_SLENGTH || + event == BTCTX_EV_VOUT_SCRIPT_DATA) { + sha256_update( + &auth.tx.outputs_hash_ctx, auth.tx.ctx.raw, auth.tx.ctx.raw_size); + } + + if (event == BTCTX_EV_LOCKTIME) { + sha256_final(&auth.tx.outputs_hash_ctx, auth.tx.outputs_hash); + sha256_init(&auth.tx.outputs_hash_ctx); + sha256_update(&auth.tx.outputs_hash_ctx, + auth.tx.outputs_hash, + sizeof(auth.tx.outputs_hash)); + sha256_final(&auth.tx.outputs_hash_ctx, auth.tx.outputs_hash); + + SAFE_MEMMOVE(auth.tx.lock_time, + sizeof(auth.tx.lock_time), + MEMMOVE_ZERO_OFFSET, + auth.tx.ctx.raw, + sizeof(auth.tx.ctx.raw), + MEMMOVE_ZERO_OFFSET, + auth.tx.ctx.raw_size, + THROW(ERR_AUTH_INVALID_DATA_SIZE)); + } +} + /* * Implement the BTC tx parsing and calculations portion * of the signing authorization protocol. @@ -137,48 +238,106 @@ unsigned int auth_sign_handle_btctx(volatile unsigned int rx) { THROW(ERR_AUTH_INVALID_STATE); } - // Read little endian TX length - // (part of the legacy protocol, includes this length) - if (auth.tx.remaining_bytes == 0) { - for (uint8_t i = 0; i < BTCTX_LENGTH_SIZE; i++) { - auth.tx.remaining_bytes += APDU_DATA_PTR[i] << (8 * i); + if (!auth.tx.segwit_processing_extradata) { + // Read little endian TX length + // (part of the legacy protocol, includes this length) + if (auth.tx.remaining_bytes == 0) { + for (uint8_t i = 0; i < BTCTX_LENGTH_SIZE; i++) { + auth.tx.remaining_bytes += APDU_DATA_PTR[i] << (8 * i); + } + // BTC tx length includes the length of the length + // and the length of the sighash computation mode and + // extradata length + auth.tx.remaining_bytes -= + BTCTX_LENGTH_SIZE + SIGHASH_COMP_MODE_SIZE + EXTRADATA_SIZE; + // Init both hash operations + sha256_init(&auth.tx.tx_hash_ctx); + sha256_init(&auth.tx.sig_hash_ctx); + apdu_offset = BTCTX_LENGTH_SIZE; + // Following three bytes indicate the sighash computation + // mode (1 byte) and extradata size (2 bytes LE, for segwit) + auth.tx.sighash_computation_mode = APDU_DATA_PTR[apdu_offset++]; + auth.tx.segwit_processing_extradata = false; + auth.tx.segwit_extradata_size = 0; + auth.tx.segwit_extradata_size += APDU_DATA_PTR[apdu_offset++]; + auth.tx.segwit_extradata_size += APDU_DATA_PTR[apdu_offset++] << 8; + // Validate computation mode and init tx parsing context + switch (auth.tx.sighash_computation_mode) { + case SIGHASH_COMPUTE_MODE_LEGACY: + btctx_init(&auth.tx.ctx, &btctx_cb); + break; + case SIGHASH_COMPUTE_MODE_SEGWIT: + btctx_init(&auth.tx.ctx, &btctx_cb_segwit); + if (!auth.tx.segwit_extradata_size) { + LOG("[E] Invalid extradata size for segwit"); + THROW(ERR_AUTH_INVALID_EXTRADATA_SIZE); + } + break; + default: + LOG("[E] Invalid sighash computation mode\n"); + THROW(ERR_AUTH_INVALID_SIGHASH_COMPUTATION_MODE); + } } - // BTC tx length includes the length of the length - auth.tx.remaining_bytes -= BTCTX_LENGTH_SIZE; - // Init tx parsing context - btctx_init(&auth.tx.ctx, &btctx_cb); - // Init both hash operations - sha256_init(&auth.tx.tx_hash_ctx); - sha256_init(&auth.tx.sig_hash_ctx); - apdu_offset = BTCTX_LENGTH_SIZE; - } - - auth.tx.remaining_bytes -= btctx_consume(APDU_DATA_PTR + apdu_offset, - APDU_DATA_SIZE(rx) - apdu_offset); - if (btctx_result() < 0) { - LOG("[E] Error parsing BTC tx: %d\n", btctx_result()); - // To comply with the legacy implementation - THROW(ERR_AUTH_TX_HASH_MISMATCH); - } + auth.tx.remaining_bytes -= btctx_consume( + APDU_DATA_PTR + apdu_offset, APDU_DATA_SIZE(rx) - apdu_offset); - if (btctx_result() == BTCTX_ST_DONE) { - if (auth.tx.remaining_bytes > 0) { - LOG("[E] Error parsing BTC tx: more bytes reported " - "than actual tx bytes\n"); + if (btctx_result() < 0) { + LOG("[E] Error parsing BTC tx: %d\n", btctx_result()); // To comply with the legacy implementation - THROW(ERR_AUTH_INVALID_DATA_SIZE); + THROW(ERR_AUTH_TX_HASH_MISMATCH); + } + + if (btctx_result() == BTCTX_ST_DONE) { + if (auth.tx.remaining_bytes > 0) { + LOG("[E] Error parsing BTC tx: more bytes reported " + "than actual tx bytes\n"); + // To comply with the legacy implementation + THROW(ERR_AUTH_INVALID_DATA_SIZE); + } + + // Finalize TX hash computation + sha256_final(&auth.tx.tx_hash_ctx, auth.tx_hash); + sha256_init(&auth.tx.tx_hash_ctx); + sha256_update(&auth.tx.tx_hash_ctx, auth.tx_hash, 32); + sha256_final(&auth.tx.tx_hash_ctx, auth.tx_hash); + for (int j = 0; j < 16; j++) { + uint8_t aux = auth.tx_hash[j]; + auth.tx_hash[j] = auth.tx_hash[31 - j]; + auth.tx_hash[31 - j] = aux; + } + + // Segwit? + if (auth.tx.sighash_computation_mode == + SIGHASH_COMPUTE_MODE_SEGWIT) { + auth.tx.segwit_processing_extradata = true; + auth.tx.remaining_bytes = + (uint32_t)auth.tx.segwit_extradata_size; + } else { + auth.tx.finalise = true; + } } + } else { + // Hash extradata + sha256_update(&auth.tx.sig_hash_ctx, APDU_DATA_PTR, APDU_DATA_SIZE(rx)); + auth.tx.remaining_bytes -= APDU_DATA_SIZE(rx); + if (auth.tx.remaining_bytes == 0) { + auth.tx.finalise = true; + } + } - // Finalize TX hash computation - sha256_final(&auth.tx.tx_hash_ctx, auth.tx_hash); - sha256_init(&auth.tx.tx_hash_ctx); - sha256_update(&auth.tx.tx_hash_ctx, auth.tx_hash, 32); - sha256_final(&auth.tx.tx_hash_ctx, auth.tx_hash); - for (int j = 0; j < 16; j++) { - uint8_t aux = auth.tx_hash[j]; - auth.tx_hash[j] = auth.tx_hash[31 - j]; - auth.tx_hash[31 - j] = aux; + if (auth.tx.finalise) { + if (auth.tx.sighash_computation_mode == SIGHASH_COMPUTE_MODE_SEGWIT) { + // Remaining tx items to hash for segwit + sha256_update(&auth.tx.sig_hash_ctx, + auth.tx.ip_seqno, + sizeof(auth.tx.ip_seqno)); + sha256_update(&auth.tx.sig_hash_ctx, + auth.tx.outputs_hash, + sizeof(auth.tx.outputs_hash)); + sha256_update(&auth.tx.sig_hash_ctx, + auth.tx.lock_time, + sizeof(auth.tx.lock_time)); } // Add SIGHASH_ALL hash type at the end @@ -204,7 +363,6 @@ unsigned int auth_sign_handle_btctx(volatile unsigned int rx) { } if (auth.tx.remaining_bytes == 0) { - LOG("[E] Error parsing BTC tx: no more bytes should " "remain but haven't finished parsing\n"); // To comply with the legacy implementation diff --git a/ledger/src/signer/src/auth_tx.h b/ledger/src/signer/src/auth_tx.h index 861ae102..9766b9a8 100644 --- a/ledger/src/signer/src/auth_tx.h +++ b/ledger/src/signer/src/auth_tx.h @@ -32,16 +32,45 @@ #include "btcscript.h" #define BTCTX_LENGTH_SIZE 4 +#define SIGHASH_COMP_MODE_SIZE 1 +#define EXTRADATA_SIZE 2 #define SIGHASH_ALL_SIZE 4 #define SIGHASH_ALL_BYTES \ { 0x01, 0x00, 0x00, 0x00 } +enum { + SIGHASH_COMPUTE_MODE_LEGACY, + SIGHASH_COMPUTE_MODE_SEGWIT, +}; + typedef struct { uint32_t remaining_bytes; + bool finalise; btctx_ctx_t ctx; btcscript_ctx_t script_ctx; SHA256_CTX tx_hash_ctx; SHA256_CTX sig_hash_ctx; + + uint8_t sighash_computation_mode; + + // Specifically for segwit + // sighash computation mode + bool segwit_processing_extradata; + uint16_t segwit_extradata_size; + union { + SHA256_CTX prevouts_hash_ctx; + SHA256_CTX outputs_hash_ctx; + uint8_t lock_time[BTCTX_LOCKTIME_SIZE]; + }; + SHA256_CTX sequence_hash_ctx; + union { + uint8_t aux_hash[BTCTX_HASH_SIZE]; + uint8_t outputs_hash[BTCTX_HASH_SIZE]; + }; + uint8_t aux_offset; + uint8_t ip_prevout[BTCTX_HASH_SIZE + BTCTX_INPUT_INDEX_SIZE]; + uint8_t ip_seqno[BTCTX_INPUT_SEQNO_SIZE]; + uint8_t redeemscript_found; } btctx_auth_ctx_t; diff --git a/ledger/test/cases/sign_auth.py b/ledger/test/cases/sign_auth.py index 041793ac..2bc68d2e 100644 --- a/ledger/test/cases/sign_auth.py +++ b/ledger/test/cases/sign_auth.py @@ -24,7 +24,9 @@ from .case import TestCase, TestCaseError from .sign_helpers import assert_signature from comm.bip32 import BIP32Path -from comm.bitcoin import get_signature_hash_for_p2sh_input +from comm.bitcoin import \ + get_signature_hash_for_p2sh_input, get_signature_hash_for_p2sh_p2wsh_input +from ledger.hsm2dongle import SighashComputationMode from misc.tcpsigner_admin import TcpSignerAdmin @@ -45,6 +47,15 @@ def __init__(self, spec): self.receipt = spec["receipt"] self.receipt_mp = spec["receiptMp"] self.fake_ancestor_receipts_root = spec.get("fake_ancestor_receipts_root", None) + self.sighash_computation_mode = SighashComputationMode( + spec.get("txType", "legacy") + ) + self.witness_script = None + self.outpoint_value = None + + if self.sighash_computation_mode == SighashComputationMode.SEGWIT: + self.witness_script = spec["witnessScript"] + self.outpoint_value = spec["outpointValue"] super().__init__(spec) @@ -79,8 +90,17 @@ def run(self, dongle, debug, run_args): # Sign debug(f"Signing with {path}") - signature = dongle.sign_authorized(path, self.receipt, self.receipt_mp, - self.btc_tx, self.btc_tx_input) + + signature = dongle.sign_authorized( + key_id=path, + rsk_tx_receipt=self.receipt, + receipt_merkle_proof=self.receipt_mp, + btc_tx=self.btc_tx, + input_index=self.btc_tx_input, + sighash_computation_mode=self.sighash_computation_mode, + witness_script=self.witness_script, + outpoint_value=self.outpoint_value + ) debug(f"Dongle replied with {signature}") if not signature[0]: if dongle.last_comm_exception is not None: @@ -103,8 +123,15 @@ def run(self, dongle, debug, run_args): "signing but got a successful signature") # Validate the signature - sighash = get_signature_hash_for_p2sh_input(self.btc_tx, - self.btc_tx_input) + if self.sighash_computation_mode == SighashComputationMode.SEGWIT: + sighash = get_signature_hash_for_p2sh_p2wsh_input( + self.btc_tx, self.btc_tx_input, self.witness_script, + self.outpoint_value + ) + else: + sighash = get_signature_hash_for_p2sh_input( + self.btc_tx, self.btc_tx_input + ) assert_signature(pubkey, sighash, signature[1]) # Did we fake the ancestor receipts root? Reset it diff --git a/ledger/test/resources/203-get-after-sign-iris.json b/ledger/test/resources/203-get-after-sign-iris.json new file mode 100644 index 00000000..4de26898 --- /dev/null +++ b/ledger/test/resources/203-get-after-sign-iris.json @@ -0,0 +1,16 @@ +{ + "name": "Get blockchain state after sign for Iris", + "operation": "getState", + "expected": { + "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", + "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "ancestor_block": "f899db73b9fa342be52307f44bb390e0c4bfe85317a94d791d6fc402dee640c3", + "ancestor_receipts_root": "fecd2dbfdfef790e31c92fe06f06875805edea30ca1ef51b179e24fd7fe04263", + "updating.in_progress": true, + "updating.newest_valid_block": "9f7bab65727e2e699e99517e1171463d5254bc769299d951b4b3249dd70d33f4", + "updating.best_block": "0000000000000000000000000000000000000000000000000000000000000000", + "updating.next_expected_block": "8da96f2120c2c4653969776aa2388242c4d247620d6866fecf2a2b277e6bfd6c", + "updating.total_difficulty": 45, + "updating.already_validated": false + } +} diff --git a/ledger/test/resources/203-heartbeat-after-sign-iris.json b/ledger/test/resources/204-heartbeat-after-sign-iris.json similarity index 100% rename from ledger/test/resources/203-heartbeat-after-sign-iris.json rename to ledger/test/resources/204-heartbeat-after-sign-iris.json diff --git a/ledger/test/resources/210-advance-for-segwit.json b/ledger/test/resources/210-advance-for-segwit.json new file mode 100644 index 00000000..b8150683 --- /dev/null +++ b/ledger/test/resources/210-advance-for-segwit.json @@ -0,0 +1,13 @@ +{ + "name": "Advance blockchain state, for segwit testing", + "operation": "advanceBlockchain", + "chunkSize": 10, + "expected": true, + "resetBefore": true, + "blocks": [ + "f902bda04f44cc5394589ef42f9322ae86129b6e66d867df0b3e021a1e7fb2bb40170239a0c3f3e6d602ffa6b067a91dff63b63df2959c9b61b34dde09d6e9cfb52b9e6f81945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba041b97d56831b94fd0553add35330e2d44bb5192694a2cad4200e18ad537edea8b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000648204b58367c28080845f35c01f92d1018f504150595255532d6566613161306480000380b850711101000000000000000000000000000000000000000000000000000000000000000000b3e6080faaf8b0ec7ee8adf2dbad7b7d9fd8b8cc6c47190d94be603751d6846b22c0355fffff7f210000013b80b8860000000000000400f1f2c62bc5bfded2c12c1696ff5ecd3d8ee4867bf5b0b5d42b8ed2433fe4dec552534b424c4f434b3aa265994f28f9528601949c0d3325896593401cb60000000000000000000004b5ffffffff0100f2052a01000000232103afcefd7798b549c7d178bac0ecb93c270f39d688a439a642a6b2458962e5cd65ac00000000", + "f902bda0b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4ba0c3f3e6d602ffa6b067a91dff63b63df2959c9b61b34dde09d6e9cfb52b9e6f81945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba08da082d1994c05368d672ad77d60d4c149b1364e05e0dc4e9fd48f5244d74a58b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000648204b48367c28080845f35c01f92d1018f504150595255532d6566613161306480000380b850711101000000000000000000000000000000000000000000000000000000000000000000a0582c31e071e17bfe3936cd2a3e252ec21e7073cedf7cea2bce97bc656288ed22c0355fffff7f210000001880b8860000000000000400f1f2c62bc5bfded2c12c1696ff5ecd3d8ee4867bf5b0b5d42b8ed2433fe4dec552534b424c4f434b3ab0165a2dda5fa8d4d73ae54980ed2766f1dc07520000000000000000000004b4ffffffff0100f2052a01000000232103afcefd7798b549c7d178bac0ecb93c270f39d688a439a642a6b2458962e5cd65ac00000000", + "f902bda0c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fca0c3f3e6d602ffa6b067a91dff63b63df2959c9b61b34dde09d6e9cfb52b9e6f81945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba00ca39505332c19cb775a942f72c34d21fe139b0edbc50894da20f2c930889d14b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000648204b38367c28080845f35c01f92d1018f504150595255532d6566613161306480000380b850711101000000000000000000000000000000000000000000000000000000000000000000f1e99eff203afaa145877eb739e6d8401b8f9e76c717a8e585b8f50c80d3b46122c0355fffff7f210000006e80b8860000000000000400f1f2c62bc5bfded2c12c1696ff5ecd3d8ee4867bf5b0b5d42b8ed2433fe4dec552534b424c4f434b3aaea7e6195f1130f33de72e618307684841fa14180000000000000000000004b3ffffffff0100f2052a01000000232103afcefd7798b549c7d178bac0ecb93c270f39d688a439a642a6b2458962e5cd65ac00000000" + ], + "brothers": [[], [], []] +} diff --git a/ledger/test/resources/211-update-segwit.json b/ledger/test/resources/211-update-segwit.json new file mode 100644 index 00000000..d97bb63a --- /dev/null +++ b/ledger/test/resources/211-update-segwit.json @@ -0,0 +1,8 @@ +{ + "name": "Update ancestor for Segwit", + "operation": "updateAncestor", + "expected": true, + "blocks": [ + "f90234a0c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fca0c3f3e6d602ffa6b067a91dff63b63df2959c9b61b34dde09d6e9cfb52b9e6f81945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba00ca39505332c19cb775a942f72c34d21fe139b0edbc50894da20f2c930889d14b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000648204b38367c28080845f35c01f92d1018f504150595255532d6566613161306480000380b850711101000000000000000000000000000000000000000000000000000000000000000000f1e99eff203afaa145877eb739e6d8401b8f9e76c717a8e585b8f50c80d3b46122c0355fffff7f210000006e" + ] +} \ No newline at end of file diff --git a/ledger/test/resources/212-get-after-advance-segwit.json b/ledger/test/resources/212-get-after-advance-segwit.json new file mode 100644 index 00000000..2f4951ff --- /dev/null +++ b/ledger/test/resources/212-get-after-advance-segwit.json @@ -0,0 +1,10 @@ +{ + "name": "Get blockchain state after advance for segwit", + "operation": "getState", + "expected": { + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", + "ancestor_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "ancestor_receipts_root": "0ca39505332c19cb775a942f72c34d21fe139b0edbc50894da20f2c930889d14" + } +} diff --git a/ledger/test/resources/213-sign-segwit-t1i0.json b/ledger/test/resources/213-sign-segwit-t1i0.json new file mode 100644 index 00000000..6135531e --- /dev/null +++ b/ledger/test/resources/213-sign-segwit-t1i0.json @@ -0,0 +1,15 @@ +{ + "name": "Sign segwit tx input 0", + "operation": "signAuthorized", + "expected": true, + "txType": "segwit", + "btcTx": "01000000024c62cd14e351b13b960e76502307cf26fc1dcf8681e4b0d9d1bb6ca69156d90f03000000220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54fffffffffd55c4e8a3a616fedc40187a6214775006fdda1155544b6fd3486374ace05ce4050000002200200fbed83438cf3f837ab484b29426bb643fd36b1c800fb37076a3e9d50fbf3845ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "btcTxInput": 0, + "witnessScript": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "outpointValue": 111222, + "receipt": "f902030180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f8faf87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a0da7798413171679dfd1a891745bcf860a4527c78531918c83eca9dd51451f9bda000000000000000000000000085845342b0d5260d516d6da5280a7df9a96d1925f87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a040d8c164003187087002850e7e79d9a47d59b2a00b29a776d24a82bd087a16c6a00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "70060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b0003882670060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203fdd705" + ] +} diff --git a/ledger/test/resources/214-sign-segwit-t1i1.json b/ledger/test/resources/214-sign-segwit-t1i1.json new file mode 100644 index 00000000..b52a834f --- /dev/null +++ b/ledger/test/resources/214-sign-segwit-t1i1.json @@ -0,0 +1,15 @@ +{ + "name": "Sign segwit tx input 1", + "operation": "signAuthorized", + "expected": true, + "txType": "segwit", + "btcTx": "01000000024c62cd14e351b13b960e76502307cf26fc1dcf8681e4b0d9d1bb6ca69156d90f03000000220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54fffffffffd55c4e8a3a616fedc40187a6214775006fdda1155544b6fd3486374ace05ce4050000002200200fbed83438cf3f837ab484b29426bb643fd36b1c800fb37076a3e9d50fbf3845ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "btcTxInput": 1, + "witnessScript": "532103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b54ae", + "outpointValue": 333444, + "receipt": "f902030180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f8faf87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a0da7798413171679dfd1a891745bcf860a4527c78531918c83eca9dd51451f9bda000000000000000000000000085845342b0d5260d516d6da5280a7df9a96d1925f87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a040d8c164003187087002850e7e79d9a47d59b2a00b29a776d24a82bd087a16c6a00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "70060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b0003882670060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203fdd705" + ] +} diff --git a/ledger/test/resources/215-sign-segwit-t2i0.json b/ledger/test/resources/215-sign-segwit-t2i0.json new file mode 100644 index 00000000..8d358d4e --- /dev/null +++ b/ledger/test/resources/215-sign-segwit-t2i0.json @@ -0,0 +1,13 @@ +{ + "name": "Sign mixed tx input 0 (legacy)", + "operation": "signAuthorized", + "expected": true, + "txType": "legacy", + "btcTx": "0200000002fe6eb00ec7d203273c0ab79dac3cf49292f86f11e56f7b988e1ed25ed1bc340b00000000fd0001000000004cfa20163db71aed39845d4ba481160c0c8d5329386e96ed0bc2f4b9d44cea41c3012775645221024c759affafc5589872d218ca30377e6d97211c039c375672c169ba76ce7fad6a21031f4aa4943fa2b731cd99c551d6992021555877b3b32c125385600fbc1b89c2a92103767a0994daa8babee7215b2371916d09fc1158de3c23feeefaae2dfe5baf483053670132b275522102132685d71b0109fecef0160f1efcab0187eff916f4d472289741bff2666d0e1c2102ed498022f9d618a96f272b1990a640d9f24fb97d2648f8716f9ee22dc008eba721036f66639295ca8e4294c24d63e3fbc11247f6ba6a27b6b4de9a3492f414152d9b5368aeffffffff13f050792d5a5397a10571730991f3d2cdfc9f6b3dc8f547d0b05b35f72afaf401000000220020ef720c94392b642ff1418a4f216c677ef5a16ffaedf3a5809fedcffd15b99179ffffffff0250e24a00000000001976a9140a4f09cbd39d5d8072b24385e1a9eb1c84ae544688acc035310b0000000017a914ba053351893c7495e0c75d5abacb3ed886cf1ff88700000000", + "btcTxInput": 0, + "receipt": "f902030180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f8faf87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a0da7798413171679dfd1a891745bcf860a4527c78531918c83eca9dd51451f9bda000000000000000000000000085845342b0d5260d516d6da5280a7df9a96d1925f87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a040d8c164003187087002850e7e79d9a47d59b2a00b29a776d24a82bd087a16c6a00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "70060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b0003882670060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203fdd705" + ] +} diff --git a/ledger/test/resources/216-sign-segwit-t2i1.json b/ledger/test/resources/216-sign-segwit-t2i1.json new file mode 100644 index 00000000..c5dde90d --- /dev/null +++ b/ledger/test/resources/216-sign-segwit-t2i1.json @@ -0,0 +1,15 @@ +{ + "name": "Sign mixed tx input 1 (segwit)", + "operation": "signAuthorized", + "expected": true, + "txType": "segwit", + "btcTx": "0200000002fe6eb00ec7d203273c0ab79dac3cf49292f86f11e56f7b988e1ed25ed1bc340b00000000fd0001000000004cfa20163db71aed39845d4ba481160c0c8d5329386e96ed0bc2f4b9d44cea41c3012775645221024c759affafc5589872d218ca30377e6d97211c039c375672c169ba76ce7fad6a21031f4aa4943fa2b731cd99c551d6992021555877b3b32c125385600fbc1b89c2a92103767a0994daa8babee7215b2371916d09fc1158de3c23feeefaae2dfe5baf483053670132b275522102132685d71b0109fecef0160f1efcab0187eff916f4d472289741bff2666d0e1c2102ed498022f9d618a96f272b1990a640d9f24fb97d2648f8716f9ee22dc008eba721036f66639295ca8e4294c24d63e3fbc11247f6ba6a27b6b4de9a3492f414152d9b5368aeffffffff13f050792d5a5397a10571730991f3d2cdfc9f6b3dc8f547d0b05b35f72afaf401000000220020ef720c94392b642ff1418a4f216c677ef5a16ffaedf3a5809fedcffd15b99179ffffffff0250e24a00000000001976a9140a4f09cbd39d5d8072b24385e1a9eb1c84ae544688acc035310b0000000017a914ba053351893c7495e0c75d5abacb3ed886cf1ff88700000000", + "btcTxInput": 1, + "witnessScript": "512103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b52ae", + "outpointValue": 555666, + "receipt": "f902030180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f8faf87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a0da7798413171679dfd1a891745bcf860a4527c78531918c83eca9dd51451f9bda000000000000000000000000085845342b0d5260d516d6da5280a7df9a96d1925f87b940000000000000000000000000000000001000006f843a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc79080a040d8c164003187087002850e7e79d9a47d59b2a00b29a776d24a82bd087a16c6a00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "70060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b0003882670060075fd226597da0032cb4799dee62be2ea10deca39b629b2ccbaf98ea18bb54fa7000203fdd705" + ] +} diff --git a/ledger/test/resources/299-advance-cap-for-partial-check.json b/ledger/test/resources/299-advance-cap-for-partial-check.json new file mode 100644 index 00000000..d583092d --- /dev/null +++ b/ledger/test/resources/299-advance-cap-for-partial-check.json @@ -0,0 +1,12 @@ +{ + "name": "Advance blockchain state cap difficulty #2", + "operation": "advanceBlockchain", + "expected": true, + "partial": true, + "blocks": [ + "f902bfa0168cf499378b06bd73fb730f377fe74b1ee004bfacf0b2aa28c7624f87b4f63ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000080000000020000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000040000000010000000000000040001000000800000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000100000000000000802000000800000001000000000000000000000000000000000020010000000000200000000010000000000000180000000000000200000000000002000401000000000000000000000000000000000001000000000000000000000000000000000008205dc8204e48367c28080845f35c01f92d1018f504150595255532d6566613161306480008080b8507111010000000000000000000000000000000000000000000000000000000000000000006d9caa0a3568361b34cc6f9bcf534a26bb06b4f7fac45183ad33c5b6fda09ac722c0355fffff7f210000055880b8860000000000000400f1f2c62bc5bfded2c12c1696ff5ecd3d8ee4867bf5b0b5d42b8ed2433fe4dec552534b424c4f434b3a73435914f9c6540ad497bdced5b106bf7aba84680000000000000000000004e4ffffffff0100f2052a01000000232103afcefd7798b549c7d178bac0ecb93c270f39d688a439a642a6b2458962e5cd65ac00000000", + "f902bda09f14fc3c850c51e59835a610fe079f60b119c2208ebe562c2364e8aa71a50d6ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba00000000000000000000000000000000000000000000000000000000000000000b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000058204e38367c28080845f35c01f92d1018f504150595255532d6566613161306480008080b8507111010000000000000000000000000000000000000000000000000000000000000000003f428e74aae45da6ba1784173f986352e4deca79af3f489d251da7750e8f983922c0355fffff7f210000000680b8860000000000000400f1f2c62bc5bfded2c12c1696ff5ecd3d8ee4867bf5b0b5d42b8ed2433fe4dec552534b424c4f434b3a40bcf15cfbfd2805841ea4becd6e30a26f05dfc30000000000000000000004e3ffffffff0100f2052a01000000232103afcefd7798b549c7d178bac0ecb93c270f39d688a439a642a6b2458962e5cd65ac00000000", + "f902bfa08da96f2120c2c4653969776aa2388242c4d247620d6866fecf2a2b277e6bfd6ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000080000000020000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000040000000010000000000000040001000000800000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000100000000000000802000000800000001000000000000000000000000000000000020010000000000200000000010000000000000180000000000000200000000000002000401000000000000000000000000000000000001000000000000000000000000000000000008203e88204e28367c28080845f35c01f92d1018f504150595255532d6566613161306480008080b8507111010000000000000000000000000000000000000000000000000000000000000000007ae9dff61f189ce30ee75e1b2019e437d97c89caa230751436fffb36a488d95922c0355fffff7f210000011380b8860000000000000400f1f2c62bc5bfded2c12c1696ff5ecd3d8ee4867bf5b0b5d42b8ed2433fe4dec552534b424c4f434b3a9637b9e5d6f0eaa034ea195be216a882a96a04de0000000000000000000004e2ffffffff0100f2052a01000000232103afcefd7798b549c7d178bac0ecb93c270f39d688a439a642a6b2458962e5cd65ac00000000" + ], + "brothers": [[], [], []] +} \ No newline at end of file diff --git a/ledger/test/resources/300-update.json b/ledger/test/resources/300-update.json index 736668ef..4f794349 100644 --- a/ledger/test/resources/300-update.json +++ b/ledger/test/resources/300-update.json @@ -3,6 +3,8 @@ "operation": "updateAncestor", "expected": true, "blocks": [ + "f90234a0c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fca0c3f3e6d602ffa6b067a91dff63b63df2959c9b61b34dde09d6e9cfb52b9e6f81945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba00ca39505332c19cb775a942f72c34d21fe139b0edbc50894da20f2c930889d14b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000648204b38367c28080845f35c01f92d1018f504150595255532d6566613161306480000380b850711101000000000000000000000000000000000000000000000000000000000000000000f1e99eff203afaa145877eb739e6d8401b8f9e76c717a8e585b8f50c80d3b46122c0355fffff7f210000006e", + "f90234a0f899db73b9fa342be52307f44bb390e0c4bfe85317a94d791d6fc402dee640c3a0c3f3e6d602ffa6b067a91dff63b63df2959c9b61b34dde09d6e9cfb52b9e6f81945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba0fecd2dbfdfef790e31c92fe06f06875805edea30ca1ef51b179e24fd7fe04263b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000038204b28367c28080845f35c01f92d1018f504150595255532d6566613161306480000380b8507111010000000000000000000000000000000000000000000000000000000000000000003ffadf855194306850a15e3850d164ac2942094d725405e06cba9e6537af0f3122c0355fffff7f2100000005", "f90234a0b97fa23609748fb56d357f5f41cd94258a21d97286b4c8a0f64df2f4640f72efa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945b2427729a74667f6cc9ece0b850004c1b75c636a025a798601329c5547a9625b421196d6bcf2f4a2b02e1f504feb0daeb4daaaf48a098124835d3fdb6388f0ec7850ffbe2c2518d6e1a3c1b59ce1c4aa8274309f9fba0fecd2dbfdfef790e31c92fe06f06875805edea30ca1ef51b179e24fd7fe04263b9010000000000000000000000000000000008000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000004000000001000000000000004000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000080200000080000000100000000000000000000000000000000002001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000100000000000000000000000000000000000018204b18367c28080845f35c01f92d1018f504150595255532d6566613161306480008080b850711101000000000000000000000000000000000000000000000000000000000000000000f3adeef5b9b0988daba97be00517797aba253218e07d044b38ae00f080dd848822c0355fffff7f2100000001", "f90234a0bd4c1890c2f7706afb45e7748cafcd0b05497aa697718398ab725c06bc36e817a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945b2427729a74667f6cc9ece0b850004c1b75c636a0343535de6abddc739afb98425c0d15c7c1bb7eff8ff8e6d7b6a8f1ad5d44f56aa00b4c1de02aab877b09aaba702180fba7a1094c9ebf61b882d4a9958298d70336a015149b412ee0658425423a435b02d17e86a38e5de3ce69bb9e3e5ef49b3d4b94b9010000000000000000000000000000000000000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000001000000000000000000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000000200000080000000100000000000000000000000000000000000001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000000000000000000000000000000000000000018204b08367c28080845f35c8ff92d1018f504150595255532d6566613161306480008080b850711101000000000000000000000000000000000000000000000000000000000000000000fc43a03e05354a54a5fb0814dcf379ecb7809345b793f7ae0c1a48c4d99614e1edc8355fffff7f21250e0000", "f90234a0629a335ed54b40ec7a9346ad0a3e6fffbfc8f967dfdbbd91054ce968c795b369a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945b2427729a74667f6cc9ece0b850004c1b75c636a02117890afa0c4265d3c77f171a2909645bbfc8bfa96ca8d98baf2f97f48be5cfa0d50d617d09b2c94b0816443a54da1e47a15d82de05f35583e09bd8b9bb82690fa0bb8b25c88bc1a8262880dacd5518cdfec15701bde10c56c8d3406a4c5a0c395fb9010000000000000000000000000000000000000000002000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000001000000000000000000100000080000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000010000000000000000200000080000000100000000000000000000000000000000000001000000000020000000001000000000000018000000000000020000000000000200040100000000000000000000000000000000000000000000000000000000000000000000000018204af8367c28080845f35c8fe92d1018f504150595255532d6566613161306480008080b850711101000000000000000000000000000000000000000000000000000000000000000000718ab69218d7b12a7f09c79398d769187a2a8848319dfd6de85d6266df6a44faedc8355fffff7f21220e0000", diff --git a/ledger/test/resources/301-get-after-update.json b/ledger/test/resources/301-get-after-update.json index 94c27304..6c090384 100644 --- a/ledger/test/resources/301-get-after-update.json +++ b/ledger/test/resources/301-get-after-update.json @@ -2,8 +2,8 @@ "name": "Get blockchain state after update", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/303-get-after-sign.json b/ledger/test/resources/303-get-after-sign.json index 32e2e389..b8b2c748 100644 --- a/ledger/test/resources/303-get-after-sign.json +++ b/ledger/test/resources/303-get-after-sign.json @@ -2,8 +2,8 @@ "name": "Get blockchain state after signing", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/305-get-after-sign-noauth.json b/ledger/test/resources/305-get-after-sign-noauth.json index 32e2e389..b8b2c748 100644 --- a/ledger/test/resources/305-get-after-sign-noauth.json +++ b/ledger/test/resources/305-get-after-sign-noauth.json @@ -2,8 +2,8 @@ "name": "Get blockchain state after signing", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/306-heartbeat-after-sign.json b/ledger/test/resources/306-heartbeat-after-sign.json index 7a2f7a17..82ef6d35 100644 --- a/ledger/test/resources/306-heartbeat-after-sign.json +++ b/ledger/test/resources/306-heartbeat-after-sign.json @@ -3,7 +3,7 @@ "operation": "heartbeat", "udValue": "ccccccccccdddddddddd000000000011", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", "last_tx": "f8281941928796ae" } } diff --git a/ledger/test/resources/308-get-after-reconnection.json b/ledger/test/resources/308-get-after-reconnection.json index 9c97cf3d..1bca9a9d 100644 --- a/ledger/test/resources/308-get-after-reconnection.json +++ b/ledger/test/resources/308-get-after-reconnection.json @@ -3,8 +3,8 @@ "operation": "getState", "runOn": "dongle", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/309-heartbeat-after-reconnection.json b/ledger/test/resources/309-heartbeat-after-reconnection.json index 7a2f7a17..82ef6d35 100644 --- a/ledger/test/resources/309-heartbeat-after-reconnection.json +++ b/ledger/test/resources/309-heartbeat-after-reconnection.json @@ -3,7 +3,7 @@ "operation": "heartbeat", "udValue": "ccccccccccdddddddddd000000000011", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", "last_tx": "f8281941928796ae" } } diff --git a/ledger/test/resources/401-get-before-sign.json b/ledger/test/resources/401-get-before-sign.json index dbe8f17c..3857d025 100644 --- a/ledger/test/resources/401-get-before-sign.json +++ b/ledger/test/resources/401-get-before-sign.json @@ -2,8 +2,8 @@ "name": "Get blockchain state before signing", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/499-get-after-failed-sign.json b/ledger/test/resources/499-get-after-failed-sign.json index 4ff5ad1c..3250b134 100644 --- a/ledger/test/resources/499-get-after-failed-sign.json +++ b/ledger/test/resources/499-get-after-failed-sign.json @@ -2,8 +2,8 @@ "name": "Get blockchain state after failed signing", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": false, diff --git a/ledger/test/resources/509-get-after-failed-advance.json b/ledger/test/resources/509-get-after-failed-advance.json index 6fb61825..ee1c6cef 100644 --- a/ledger/test/resources/509-get-after-failed-advance.json +++ b/ledger/test/resources/509-get-after-failed-advance.json @@ -2,8 +2,8 @@ "name": "Get blockchain state after failed advance", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": false, diff --git a/ledger/test/resources/601-get-before-sign.json b/ledger/test/resources/601-get-before-sign.json index f69a4298..0caf6d1e 100644 --- a/ledger/test/resources/601-get-before-sign.json +++ b/ledger/test/resources/601-get-before-sign.json @@ -2,8 +2,8 @@ "name": "Get blockchain state before signing", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/617-get-after-success-sign.json b/ledger/test/resources/617-get-after-success-sign.json index 9f04e68d..4899acf1 100644 --- a/ledger/test/resources/617-get-after-success-sign.json +++ b/ledger/test/resources/617-get-after-success-sign.json @@ -2,8 +2,8 @@ "name": "Get blockchain state after successful signing", "operation": "getState", "expected": { - "best_block": "c38ac27d1f9021e358b0e633536bd381bf586f244d83417116c1d2040fa879fc", - "newest_valid_block": "df875149b0cf41c6d97bfdac376c044db6477ad071df61cc847d89a6bcdcc4f9", + "best_block": "b4d586b2c2f51d6dcb6e7870df329e780d8e4b85517b91a822dc52c04d801f4b", + "newest_valid_block": "05cf1ca3197482828cfa5d1777350996593bdd62f73ce388a280a9e08b69dac9", "ancestor_block": "a91f702314c77a8965b53d1dc8a44301a0bf915023de4258e80a47415a787349", "ancestor_receipts_root": "21f82fa21d141a345338733fa871efb61cecd716f9f6c46341d9990ea678f7cd", "updating.in_progress": true, diff --git a/ledger/test/resources/620-sign-segwit-basic.json b/ledger/test/resources/620-sign-segwit-basic.json new file mode 100644 index 00000000..4fb3d628 --- /dev/null +++ b/ledger/test/resources/620-sign-segwit-basic.json @@ -0,0 +1,17 @@ +{ + "name": "Sign with a basic segwit tx", + "operation": "signAuthorized", + "runOn": "tcpsigner", + "expected": true, + "fake_ancestor_receipts_root": "eabbc877b16f4f484beadb403a9a65e04a9c8c8a5cea57315f729ceedbc0d6a7", + "txType": "segwit", + "btcTx": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e01000000220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "btcTxInput": 0, + "witnessScript": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "outpointValue": 987654321, + "receipt": "f901a60180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f89df89b940000000000000000000000000000000001000006f863a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc790a0d12c37848294c3fb1cea441f6a1061f9304828fb22c324c27716c291ce37dec9a02b3e258436902c2b1323ea665d6c755b0d84b9ea9a16343c47259956318324ffa00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "700600658216d3195af5e1498d49cba4422aeffa4be66b29b84efd6ad76f0bc5a44f89000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b00038826700600658216d3195af5e1498d49cba4422aeffa4be66b29b84efd6ad76f0bc5a44f89000203fdd705" + ] +} diff --git a/ledger/test/resources/621-sign-segwit-with-many-inputs.json b/ledger/test/resources/621-sign-segwit-with-many-inputs.json new file mode 100644 index 00000000..f03ea364 --- /dev/null +++ b/ledger/test/resources/621-sign-segwit-with-many-inputs.json @@ -0,0 +1,17 @@ +{ + "name": "Sign with a segwit BTC tx with many inputs", + "operation": "signAuthorized", + "runOn": "tcpsigner", + "expected": true, + "fake_ancestor_receipts_root": "b550311160cd59f4d912fa7246a21332dda29ba54ff782a76d19fe26ed2ab533", + "txType": "segwit", + "btcTx": "01000000fc208393eed3f5e4952ee809fb8976dea39994d35f8af16a683df073b7e37acb91300000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffdb5a526cf8e20fb00cfe88ed01041bd49992a6426005fe8a53629f8b154b0fb0a00000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3bec19bab0ad389adb52d339350858f6d3ea81d566dfa324d228d2abb4b08abf7d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff96b616b29a71b829d04e0781410304f70b4f931d776eb5db157d3816d7b76a6db60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9bdcd1bd9e53354bcefaaf576a5c9999120300a704e08d6c0b33bb2190fad933610000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff838a64df688e1baf0bcf479f6cde3dd2a577f9f736ad7f0dc96dc016c0274690080000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff866c236c1c98e8a7d63571cd038a90d4f15da63b04113263793ec62fd066aa961f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc466796d245040ac945d7af542caf15a400c2d5b98523aa8fd2ff771524e39d6530000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff001ff1969be15958052e6220409b7a029c89eaa82647b76e29f36c2a7bf158ecaa0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3c46742a75f2f5d9b9a2543126e642f5cdbad20b92e53a6d43fa66f07723f552440000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff20c1613672f0ef9c036094a4992a4d322313756cb50a995470f532c3fbc5f06dd00000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff650a2885dce9423544c4357da26fe18bcc88ce0fdcfc01c8a21a2ea5ba218277fd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff60f7d77c55d6f79e0e6949804c76a39487345d6b3ef276e88b9e670925ae8238160000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbbe42315ea341105c82525f00ac05855f925698007e4dbb754b5bcb36322b245250000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff38af7d4a9a3690ef73262b8e22fc500e65ff1251980aea9cdea03d62f540843a650000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd5f1439ce8454383576e9b69b9cf336b74eb188f4402c035e02cc4cd01353d29960000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff22314b7a254e524341eb316d46f3e5873068944bc06d284d8a8898713591847adf0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff896dfcb27e791349f9e06db7da8d8bc1b1680d8833b1e28499de7270bac2566e1e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffdb372b56cd12c68480ee5e59820b3332694b0cac3f3c2b7182b9ffaab9b2327320000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff740c8461bf9b6363582a189b91fa5c3f8138ddaa5a339103bd011591d033bbfb800000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff35db4c8ba2d9bb613a293b4ee3cc08e8500025db4a0fa2f17659317827e987ac6b0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9dc5116c995bb6fe84272f8bbadce7a6bf6b693172b992206390a00c7a75e49c1a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffddbf801ed39cecdc9c6cf996800af267b41808892aa977d53b193d64bfb2256f060000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff456f97e065b1753f391ee02c83230a1057245dac74772456f2b56b3418ceb705580000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff13a8f80f436b2aaf316b3ee4f58244427ead812185a94ce6c13d5569004a5985a50000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb5c8f337cee7db201a08ac70699d3719c2ec8e2078f7eed9249ea54ff45ad3540c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff99cd6885a17c488c35d678a1f92a5bcc8fbf9585f2c867c44a14a677b6012d78980000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffec59b4bde6c6e2c595c8134adc0c40511352d26287bf2c1cbd3eb9f2a1b901bffb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8b200973ad2e0e972a717bce05afb64cd86a22f32615b1ab38d1326ac11c7baf030000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe171a0d2bc948086a6320c3238c13520f8e5d855ec679a413a0078b9fb1bc1cbdf0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4df6ec68787280d39f7f39359b9c9612f3d1fe90de3cb9c694227682b328c6ec610000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1d0586fa7fd353a85daf4603bb860ca6ad4c64e7ac9824164a4790507930d3ca7b0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbfc42cc9bc57409d7d4bef67780f356099c39a1593927a31cf14c59fafdac03c960000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe2011fe5121c24404ad283e8b8f1a3c67b79e023d27f82dbece3e5afdfeb23984e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff196e09ec1779825794584bdf7c199bd20d300808c3120727ffa9a51bc427f021fb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff6ba1a69b1505784e1c427811cb2b0a35947cfcddd7d1fdc766789e2e78e6d1a3660000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7ce8b8ef8bbedce1a5316427f55802a7adae296b88c23474e589366ef634c7ce770000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7f1a54fa0f655a29f3bf25bde2a9cca21c22430fc15c1b29d81913c4118face3cb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1db0fcf2e89271b01a76ed5ff3cd40fe8107eb7cee9587d055d02976e6411c793e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4d41dc4a74e6077cf458e88e558197f13428c05a0e1daa0c1cedb4155b8cd56d580000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd22dc3dd384c29c78bfaf29b1569b06e0d40e457e4e7df9ad6ed6b5958a32208280000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff5ca7b28fa1960c50a235a6c607fa3c1410da9d257c1aba0c6822012989ea4fb2720000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1ed49ca83c361450e315ae838118647c7b9e84a60d21f4eb45dc33a4b305d2a04a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffda9de78bbc5e2fada30737d3b2205a434c0f76cbd9c67d233dfa5f843d4cbf1b7f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff3c0f8dbd1fc94169494a82aeb9cecb79233fa731aa2c08e420f560138176a397d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1057bad591f1d4a70d0405cac990a410413d0ea21db42290a4f7230103edefe8c60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff2a73adac6022bc9d4a71359c2a8d1cd451dd83d87bb6f77c7145be2e4aef1f641d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8b8ff7cded51f82b63a1e5197eaecdaf82a206367d6c210a8c90d46515bca3f4130000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffeb4eb038bd905fa80aa73298f0e49fddc10091ae9389620bbcc6ce2b812d8c891e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff29a703b214c8344a872525f7891017490b6bc9458ec28edfef99c9b1a50279e9fd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff17fac595af95dc63c7462f6237db65afbd6604463e9be05aed9bb9f44b26f34ba0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff736f6a72fff2dbf5884d51865603602075573838fea94ea59e0cd76e3560659dbc0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff2c5c164ad75c9fb44e5f9a97dec2a572a0870b4cb663c6f46858386057e03dfb4e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff653eeeca3885c3e817ff24b3f6ad2362a6250ad7dd65195d90e16c55df0e3086440000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffed3a0db1bd0246b7c9c8dd364ed69c090af7376c18c18773c0ea271c8903e19e460000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff479c3c9dd2881a9faa0fcb3a68b8712cc4cbe9aa83eda397747c24aff40c6ee99a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff3d4b6e3ddfa7fbe6cb13ede7ef64a2c5e3b625dea41d5fab6d5c44471c87bff8c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb2a1982f068a6d23f29aee03232089039700406e66f288ef56371a6a39538b98230000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbd97f7b700e6c4f217f2cd0a48fb02b05e9a42357a0fb81b415adea0051ab3ae8d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7dc21621497ea6dd9945ecaa736e0cb63168976b927049512f59bab60b2c7ce5270000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff121c0b5b496ea58e76d0d046b39e35c91b64038fad04634dd3020b1cf795bcd0350000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff057ec13725eff2b2acb6bf365dae44793a8492c465e1500c68ccd16fd8fb90e3510000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4bd56b93479cf637025d2320ab0d66081ccfde7e539550d2ae13211b5b62d6d12e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff0ae0170d070b4ea71d7fa03d6bd46729080a1fd3e8796d94aa40f5815b67ba58710000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffbdeb7a9f470dd5a6357be1f379c1de00dce811e73cd7640410bc13acafcad6ac80000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa1dfaa00fb5aa769d37d940de7c385ad896fbb7a5b0af4cc79497dbf94b229be400000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff01f51183c1a3e60dece5a0634252c64c70bf896df3ff847c802aff175fd13d1abe0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7a84b406f84e3109d250237d7b304e26240ca9d6743b437f4e2fd50caeded2c7f30000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbcd044d34a17aaf896e79771d15a1757e94249c40ef6a4bf2c1b27fdeaecc098550000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff12c15ad37d90ae705fe5eca57fe71b99f1ee08045505418067152b0abae9d5ae340000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff5fb3e4109340eaba35353f53547930785c182e9aaf12cfbeb3f0b84d03bc9828dd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff27dad98494ace9d31d66b43becdeb0c396163219a7c50ef19d7eeba684fcf2733e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbf07ca17e2ba3aa2ec9141d1445d6ccb97ed0c4b3c324c8b28875f03c1511cbe210000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff5aea78368739a0a99f857e5bd5c67c9d15fdbcab20c969efc88ba16fbb59ecb0470000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff06eb17c61530b9e3de4229da3d1cc547764fa1ac8cf8daebcafb464c32a75c061d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa2140ce19132cf7568ed66a5a5d350beba936d8ef721ab5cacd9da48b99fd589040000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8d8ae25e2a9d51a963590c6a150370dcfd21c6192d01263c9cbc2a26bbbef50f3c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7da660dfd104f937379df3f91b3247dd6a1e7350c9b4e97ae31860fb4b2e50124e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffdc949413250fc8d658faab45657294ea5e17e6847068d5507f079439886f4377050000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffddff8fd77acf3e36c9a17e77438c2f5dd24cdd979d00d0cd6f24b81b52d429c8550000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff093eea44d3c6ec664086b486554f5d9522ec793a6e23de8023e43cdc8e5437e1a10000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff399cb648d0f1694b739c85e4a07d1669e99a13b895ad36771197c8820c5e85df8b0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff7f58cfe7ccd2e7c1ee7c2949e420676946c98b0131fbc70701a1d833084de6ccd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc2c42c7c0cf0c0e4bc36e3bd82d462c199300980c53d41985ed51323a1fcf86c2a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff34fda05b7bafb0552d3c799afbc8aa1b7df48fece6ada860239551ea13dd8e6a5f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa0e7b50a187982a5e9cba03d66dbc11da899133a7886d5340fd3beedd9020c777a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3131971adc42849302cf6b5be0389b6d6d6455801dc14d0fe8435ce87ba0cd2c0f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff62be83b2e045735ec20553c098461810e6b377bdf985f4e96a79358f0add9000130000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8f8049bea641d8c7c5aba8d89f92068c1cd8ee0b2239191e958e1ff2164bce73db0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffddb7c635dbb40c34f604f59f560b329eaadeed0f6d5d34cbf6721d8c68a5cc0eea0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3aede035a47c0f3923907bf4e4d8ecc5fbb9287451b25c42fe6f1adf134f37b2610000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1b0480c8356857bf411b4c35efad3590f836f884671fac09fc34791e8f37b2f85f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc6a4ae2abcac03e78a6d3f3fe4d07c6e75e2e344064434c161085a7388be4caa270000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbc01556be797cee3e8e6a853e982a0394310731c7956a97dd3ea5f71692bc3caee0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9ac04670b00019b03de7cf83c8de383331e700138fa9d455c46317f2c7fd307d170000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc94a3ca138dc2c7c9b5ea6c668ab6267b406e4646090428cc583c6347bfeb63ef80000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbffc8f36f7033f3b8f1f6961d29c3ccbff789b4f6906c3f899f97ab39e2cc379a80000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff15b58d5689af77e10425f2233b8c7862533340115cd12dc5ca8be3819f049862950000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff40695075ec0801bab187809ae2e4f4f05fef896ef4812dc6fa4884a15c58a5aa270000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb8a4fac59edcff10f95f85d8cb3bd08cbc5e6b3a32d89404c57be162d12fdcf83e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffabfda27b2402c3cdd9ced27c1ad817f7b9d66e5fe4bda3a730076cd1b390b62fcd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffdba1b98014583c1910127ca0b2bebc866c182e7cbd625e057fec48993ae3a15f50000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe6c07631157d0a59de4f709f851cdf011ffc791e97458b979c47e803b17c8ac6120000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa33560970083ead4a1b4811637d0214821d273f76689063e70e5cdad445619f02a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa6cb6b5fa8e7e16a17a939a589ba56509968b0a4a0a524edca1ff5f87135a485ce0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff10c18592edfa0527921870c43d6c370184c8d736bf8f257a3a26af640ff0c70f00000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7c95becdfe24c0056b283539c9512d9b59ab4abef612719af64257fcd6f59ff19d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe05fb119dadb6b81b8767f90b29c5cbc1c9b37df7eb8635fe594f76a9df1b7bc7b0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4aa7ce52246e6ca1b666a04e2f169bb9ce7c4fee96f0791e1a8e41019079aa90640000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff21e8dac66a09014c42dc6c266529600ed2ab2dccecaa690c54adfa970a3751c1ba0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa0831ab9fa3d2280dd94e560baec4e9907516120c7369f56ecfbb61c0c4a9d471f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff5e875a75162a6f06c2218d0abb277d23960b405990d27bff155bb1027595556a230000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9dfc50f70c5c8d258e56570dc733db059352c95623d05ec5f77b6abaaf5b1187340000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffad8b8d49010eadd905bcbf59d230631b8765a14cd446207515483a36ecdf69d8510000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff937375a0ade6fe025dc44b396cc677dc95a99c338c0a5a7d2e10cbf3bfaa6bf0400000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff2cc3f50dcc03f09710caebf7b40a9eca8ead0244e985a935149d5d3bf92313a55e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff0d1bfda37d4880a1a7ace585032e15baa213695ea80c4e0da1bce4cd3d3708ef880000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff766fe1d4c5d50e67483550a292f5613dbbd87873f1b348a41a4591a13f006174e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe41089062699c802bc7d1f1ee7a7edece087712b2c4d96195df1bd1c1b96e22f600000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe95821ec6aba3bb2bc2bbb05fbdcf5938c45151474378e284c0da71a4f42598eb40000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc8de2b8165f3d6452e554f210c3feee78752f6458a7cc8e686c50409b09ba0868d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff658979574f78b48840529d0c2f724fdc5d468874d3f2d7b42efb06d51be79ac8580000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff87386d3df36046fce4fbb1761623957ae37bcc9a3c12e5783827114e0e15d9ae750000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1b93848666086943a90ed8c967a00d02a79518e217937446492d875cc4ead94a570000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff066539257aa490471a6c92cb88d76e3a2ce44da361c3a02ea394b4427f72d187bb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff35fecb7b00b46107feaee3c7431ea0f4d9a3bfae636a002b24f0c7981b9867b2b90000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa609c79b9cbbd4a229dd68c3a360d496641d0919cd16844523e2779eb16a002daf0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff11ddd213375e285b5e0ec3f122c222a5f6044ec48b91f1b5129c83b10abbbc33230000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff73a4e7b67d767ce5fbb7ffff25b7aa8483cc30d3dfd891c3df468ea42ec5a397ca0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd72d23504dd108821a7c6c5f87be28d93dab7fdc6f73736d4543fb6e3af6d2b5ab0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1e7bae5194b0ad5eff39e3102c8e27087f6a28aa21f3b5f54c851123a94ad080500000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa6c895b0a5814030aed7c533dd64186b13706e35edfb4b753e564b6e25c901d84e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff732a838db68e72ea526a3341d74156a36434a062c736be243da3d5321f05365de80000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff7044be9391252383eba2d3686b152860c841bc794e32f2d09ae1008f73c411abb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff86343b3ea233c734e288cacd24fb16e284f41c4a2840005edb0f7616453f90bdcd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa1673b717368f55044cbf7aa905c9e94bee6189d90fccfeaabe344ee8e8cea35d60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff64c6711bc20592a7d4d11a3834c2eada42e9c4130021a9a605741fd78187543c890000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff04440deb058de4cf02aaedea0df9fb51111afecd302b51c79fe5ece595c1efbac80000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff4888a894b93e63c3f322a4b1cf767e2168f745cfa688a120e0e9ab5b8815718b60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffcf5afa912658236776d2c0c0a5ab5c1bd36b6e45ec7fbb6790d015393c507af74c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa9251b1e5918f1dc5e41a113f44740045d056ecb38840bcad2b696a3aab5400d200000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff46c0ff4f35db23c80747afa0d0347170f95c82a5f58992fca5201db8f6b39c0b060000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff56e8d4d6b0903a742ab7e629570388c2cc57b49ec8bcef9891dbea2ee583393ef80000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffb602c59d9e49411395df5a1036baaccbbc6a9c99ebeb94d1cd3c3fcbaab0f2be30000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff011df19242e8394e8e0f59e722dd6e5001e9d7d6a7bcb0afa5a4a45839c1a5092f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa2c3fcb925ac5b1a6f3969db050d37fca5bd1f14cac21711efc8b0349628993e900000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff55296d17f30d6b0f5bb8ded480fba5bc3cec9b0d7aab7d58a924d297bba47cbf6a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc1372c52f283db9be8bfc7e0c4a676b60acf7aaf108de882c37452ea9889f47a6d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff2667fdb89d372cae0b1a732b999dadf4360eefebebeaf5eec18bb6aad91bbc3d8e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3eafc1cc4c6a1f472a0ffd2eb14bb39e839f7822c83f9f60eca70367ab4126d6410000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff0b60c29a454b56d100bca2f067401786a14ff1b61115477a1356e1732814c364980000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc943b46f31be09eae4ec7379eae7c2cef2b0e431dbb32fcca7d70d4044894952b50000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff25a3307443653a547c7e9c5c917bc8c10135e083c5e90b887550922a94fadae0ba0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa075b8b1a76eb6ac9f790f012cd7a1b3eb10bee63fd9a45eac8bed012231c4d0e60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff666c31c166baf98d1004aac77b6e7738b24a9cce6fd8a9fb25835e172c3c2bb9ec0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff330bdc880a3907b41e2604ea4d0edc098912fe160ff79a5a5e4ef6f7e84bbef4110000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa143a820c2a986105fe0056b11ac272f738248fd452bc979ab1e05ac7cbcdb26800000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff09133953681a83e6f3a1d7add885b2ecd4f6fe07aa81077e0397a62cf580f6d3e70000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7d631e6eb195c1c9ddaa905d047a0700c95e2943f6756c23b5597e28dd7a172ecd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff89ee6813e36ea1f17985c7bf474a0f1a26179f1c1eb2df9e6ad44a8e22dc6388140000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff769e39ae93cd03f91da1e9cb59be9bf388394093900d7eec5a32d59859d0401f880000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff70cec86733e88836b3f8cacb80b77ebc42dfd20c3186146ebd10ab5ecf19c978580000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1ebd4a8fa281fc7a7f9f1101f40fa2466c186845640ec7da27184908d43ed6ca660000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff450e1e903149f8af45c366c8a5a8920de1b9bc28c6f4362d3d959a60e5bd5331d20000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4c5551d18e239587f9c4a0ffa515277c26550beea8a3ad21b1b3c4bd68f93be64c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff98fd89a096ee7d8febae4754f8eaaabc94d506f1a16e28d3d257a15125364638fb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff705616163ab8db57d2d8fc321c12021facb1a0b9b8293780e1e2854d116f4c6b920000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1f50ee861556453ed783244d4baed5d53186124aef77f48f6442199039662ddb890000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd7ec31327e91053b58397ffdd19687466a0f860261a7de2dd4150ade69fb16462b0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3fc4af9492022016d044e77bf12caac4248e1dca7b5c3a1d0a326d6e79617013bd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7a15115f4eb2c7ca210b49d84a6491ec05ee9522fd49efdeea6b291a4d227d2cb90000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8568a729013377a77f394f63e360f7b68c7ae05fe3ecb0b251b0c18a50a42add830000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff77a0ac19aa26fca7e41dc9503d693d4272f7a99da1f18cc42e4610fa30c02073b20000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb9d2a85be9b5719db843aeee052c0c4a42bbda7029cb9c14be9f048a0f8cd657ce0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9a45c65122d733d7414d6da55660b573965022b8c805fb2de18e089dee64c541690000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe3150e7c9285ebab6f2fb38a6b4230bb4a29a166f27e4799820c2ba80a37502a600000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff2668542ddbd3eb274f0942dd08355037c4e86cc63f59d31fb8bc691acf31b91f5e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff7243209b521af3c507936a23a790f14a607b04cf4139743506c087cce1b48cceed0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff0b6e7adc897c76239cfa3fde80ebc16af3beb7115708654e3d68b758aa51385d810000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe2b7b21c7c14b76fcbf2b89bab64f57bb82a1cbcb73e2cbfde7fdf1991c66ea9c40000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe76bd641d6bbcda5e29a9880476b82dd1f5ab032712faea0e68bc471a930ffa1b40000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff34a9b23e5d925c69b71c8389bb96ddb7f6475de49138371a22c28808588c7f9b70000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff0ed81d34e4fc687428608e594d00aa4494d59d355e3a0e5d681030ab5e0c750f900000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa9ecca8db8de629150aa73a7c12185540944acc0fc8074c7814780cd5cabace6450000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa1562da4f71ad7e41f1292193cf0c0b243f064bbc42ae0986ea42cebd0d5a36bd90000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb9002847e3cd484a5d834a9d664ab88a2a6532088393d3a2a65c33387d8b985d390000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3431c0425294130ef249fb3bbc596b566b28d45276b8120d0d2893de3477f7bb110000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3fb87db0e818689642abeefb072c4e13c87cd6fda97fbc83600ccec766a624d7e00000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff80109b5dea29ca9e215ebaa99929363c09da6f9fbc7277c34dcb27d0c933b8fa8a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8064c913a942915da588d99c2092d879701e0794438bd3b9e36e93566ac1d710fb0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc58fcb1c1ddd6dd38a824585bf092a78fb00ab3d5c7198108973c2a78c9a29ab3e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffa24f6316ff87defa5149e203cb545ba79371f04c2a153c2518061b378a72f53400000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff999131d946bcfaacf1b0a24d669a25cb671628795bb4d4db664f3bf7222a76d0090000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffcb41716a0cfca06cb09590d747182916314b048e19f3c7700eab1df8b25c5dce03000000220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff4d781feff1859672d0f80feac9e895d993caf874f9bf2d62362486c6342bcf84a70000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb1d25a7fe98f2f4c9d81f9a35beaf00b43bead9cda052024a91e422f2ed792055f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff06da95d2b9b2224dcfc4f587c8f2fe696e5e1b1247074ca9bcf20fbdd159a2b5ee0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffeabb91e256e87701fea35640b71802e926ed2ae617145d4a611acb77d19027b0560000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc7c14e744714e5b754c7b672fb65129b5b7c1ed6fc12c2d4d1204a4cfb3e6555b30000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffba88cf31bfe8ee6cf07925cf39c6af02bc423e98a534ed70d7254e44727023903e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc105ef3019d2571b41e4f6ccaeb7dc254e9226367bd9076fe403d1c07b05e59db20000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe50ffec58b9c04949ef5910f1f6a248c136aeb48b69c6f92e58c84d4bf391779ac0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc4f4ecd79da3b21e14f651a8062969d6f3f69ea50245dee2a03318fb5058c6c4960000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff52d43b8f47e68d820ee8b68d3d332432c73be1aab9b3394d393592af8e545fa87e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc72b0fd30a1785da55fe010ec282cec03597a366afb5b2aa67adbb0afb92a83a630000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff7b13b76f78d9567de203227f1fe09ba3e471c86584393d0eeca1ebd066e3a6f3b0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffea0712a840628959e9576aecf15c498524bbb56e8f5ec496e505fc317e2a933f220000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9a2491a8aa83c4752b468dc9ca3bcc475b8586c18549f12a706a9cb1780bac74960000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3e25171ac386191d41066e5d8760f839af0a89883106e9894bbfd4b984fbcde81f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff6736d107bde130e24f84c157f5d8967e4080538aa0277ffdc6f1043870bd6c38bd0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd7acd128d7c1756753c2a1eeebdf97b5e47ff40a5f83c17df4f801abb42d2308b10000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd84b5b41ca9d3ba5fe7fafaf7efca767b64c36de5714aa11f38f5f220c36c347380000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3548d078005be576bb9b5f8d2fbcdd6fa0c27286a6e522c5d91bd502b4ff3a99d00000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffd3e15289c42ad461d1791ba1a50fdd5b844c5cd6e7b6f1c6f0c9fe7a372bcec8180000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff917cbe4c538efb0f2348cbe0282edd9b1a67b0feef5944a095971ecbd6eab72fa60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4eaaa576284a09c388574ce550eebfd93c4c6d251cb27ee11c51e55515ebad15f90000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff2e3e3b81183132b5643f2a9d483a19741eb58d997b96e7046b6597323725a6db350000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff235d54a505af5e77a3c75893c5e57f928ef3968af59df3fa7d70e0ecc62be0a7050000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4e99d4e158d1d52939c2a0ba3986c8edd46e4571c4c7da41eb63f7243e10f022520000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffb973d2616685d733b183cd4ed765b543719305177a5347078414d4cb4feb3466af0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1c6f0f2438d67821dbb5585557be11412ea37e0e6ec895b8de729f8cb459e7218e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff68b4a9f79b204506acd50d9c79bf3c504b6eb51a47f00b2a3b59690d93ff2bc1770000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff4acccc270a088eb03bd2c2ebd6b9d37570a896da273dbf1aa7b3d4ba2397563e9f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff124f52ee6b8a5c434b62ffeff07f42207017d66e1ebfc041ef195b7e33657e16a70000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9abca12e1fdca561e4b2632d933c68f0a5d672bad44be66d203a0b826ee35a600f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff834c0cbaf07167439a51be3615f6eede4eb106d5c9d6e920c10368287aa0ddd45e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff1bbdc148b5d18ba6fd32bedd6ffa23aa63e67cecd8d31970396d91f9e50cf48c5e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff9969cc1ff45869d37ef23ca0c9bbc3785c3fbedbf1804c330487eafd9380de0da40000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffdb01b777bcb2812383c5f2f59eb95c315a6e3759ee5dfc62554e981727f8d3a8140000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffbf22d9e4eaf0dbeef0df490c937535bf09d39be26d34007ee35a7000e1137be16f0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffca77d7274bd39338de909a99746b716376a599bfb8e894dcab8cc019b83ea4e96e0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff6d43cdb90f143f81e0121c3386465d89ccd0d9c6e4f69997f84e3cbbad59ab39b20000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff34851160b061363ea69a2c408a9c249f5297999aea9f74475be06a5542bbc936030000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffa9a08f1cbc433fcc1651ffe3c3fc02048dffeb8e454198fb40d5aae89ebe713e360000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffff9fde960a912cae0955275cc0e6bd7acc14fc7778111f5846c5b20a61173e8160c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff3dd9c6e61ea7d8b720669d521fa93c3eac92f9ca152000b066e2e599257ba135a00000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffade6bbdddd7e73edb548b25b85dd2014d743115c132789656385fd11ac71b20180000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff58d2337c847d7c14a83ab53b6267f13a88838b482f8c372401b26f5c63e808b85c0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff51d759c2f8a914f888ae67b9eba783ef2fcd5848a48a094ece8d3081fe1310fd590000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff8b80b65479565e15862c5d73be3af4f4b0541f4672ad1f14aaea665d38c4d8d85a0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffce8a6b9d9bb7fbb3abd518eab3b1790cd46949e45d7b1fd56e9405f0ffd120e12d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffe75a609dae169296b8fd7dd844d2f8bc5b24db0c2ed21ef9057b8af2b422d3bb30000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff6db7b6f7c68468c2680a986b385ac4efa0049914cf018d6878560b2a02caa940c60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aefffffffffa5de771814369a1025d7cae422d08273b4e86bdcc97f7c9eec22d9b6577e9b9080000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe66a96c77d47f4e9190110f968c3e292b9621973a28afedbd7c417f58de0242f3d0000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff83487c0ebe945a8914f080d45e226c98a9c07a0f3315d1cc0580be053c8cdd45750000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffe39be6805d17388b820fe2d2b29c004c3199b06e75c7acc680af4df0af16b764e50000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc7bfd02e083bbc913286c99806a4605999abade373bb165f9708b71409e902ebb60000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff27864946b0965ee2b98fff35b6929c8ca96ed0237ed8410c861d743f5c459c1ab10000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff98935aea579000241d855c51fd2e2e1ce402e035de14e6c94096b70b4db575d7160000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff45b436e28e5e7f2190b1b4f0d3533b2a0cf9d3ed6198a634f8c832ffa4a92185450000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffffc4faec9d54317455d805e0e25d60d9c7568b241384b1fd451eeb3ed278c18360d70000006e0000004c69522102cd53fc53a07f211641a677d250f6de99caf620e8e77071e811a28b3bcddf0be1210362634ab57dae9cb373a5d536e66a8c4f67468bbcfb063809bab643072d78a1242103c5946b3fbae03a654237da863c9ed534e0878657175b132b8ca630f245df04db53aeffffffff02b011e111000000001976a91447a5bfd415108c37e918e8b114b83f8d5ae9834988ac00bbeea00000000017a914896ed9f3446d51b5510f7f0b6ef81b2bde55140e8700000000", + "btcTxInput": 193, + "witnessScript": "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae", + "outpointValue": 987654321, + "receipt": "f901a60180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f89df89b940000000000000000000000000000000001000006f863a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc790a0d12c37848294c3fb1cea441f6a1061f9304828fb22c324c27716c291ce37dec9a07eb032d4999d4d86eb6d4c2ee9b69eb9c826b1953756771a66d992d2603137dba00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "7006000bd4f128bd4b6876147d6770f3d65fb8e6c5b6e7b397b86f57b94924d2bfa72d000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b000388267006000bd4f128bd4b6876147d6770f3d65fb8e6c5b6e7b397b86f57b94924d2bfa72d000203fdd705" + ] +} diff --git a/ledger/test/resources/622-sign-segwit-long-witnessscript.json b/ledger/test/resources/622-sign-segwit-long-witnessscript.json new file mode 100644 index 00000000..bbae348c --- /dev/null +++ b/ledger/test/resources/622-sign-segwit-long-witnessscript.json @@ -0,0 +1,17 @@ +{ + "name": "Sign with a segwit BTC tx with a long witness script", + "operation": "signAuthorized", + "runOn": "tcpsigner", + "expected": true, + "fake_ancestor_receipts_root": "8d87508bb18f696e2e41fef9abe7d25555899ff69e8310c706aaaa35ff7443bd", + "txType": "segwit", + "btcTx": "0100000001cb41716a0cfca06cb09590d747182916314b048e19f3c7700eab1df8b25c5dce030000002200209d522c8d95b65b357ccf98b1b69da3b385329146a002e8a2e12a85ca5e1d9915ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "btcTxInput": 0, + "witnessScript": "58210271df73590582950f35db74b143d5b33d236b85c4879b843b72501ec6a2848938210235f05d65271026dee084dfa85b4e1d9003d7222e41b77ff7a9d96dde1c1df4b621021af9a946169d37fe597a8f18ae24bab5f1d67389b4969e3b2968d83a89ddb77221035a1991fea6c84847cfcd744c2b2ceda52068ee6121ab45d401d589946586399821038dab9d13aab76fb67dae25ff3444e5fa034e92abd734813193c808fef1f3c4402103a4729c1b5985797db6d6684b1d6ed20799bd2907583522a60849cad2aafa38132102d6b7d281af02859e6ba4f7e17284ea5e9ff6f8ab499ecbbc15a716497491d9be21034e85baa81c962d5649beff5c8df9f615369e7e7a107ce3e4a13b8ffbb2fbf906210271e5df883f5ba4e741c97bbcaca3d6c76c5a290db4b04554a99bbba5834ead6b2102f9994a4066656e8d31da1bdf68da7443d0160805c378ec068d03ced7b94d15142102d7582f433664a6b3ed5ad4ce8c62fb9771c1b73306faa2ea8183ff4a325193ce2102a036e410405eb60f6637e0f23507ef8414083ac4c3b1f295268b4ee96dd19411210203e012375a9cd95c9e35e1e084b567941b5961e9cc1679cde20ed2fa53af2ab521020001c24db69a74345f8b16b318b19c87da14b3e5cb7583be9a902aead6084b3321025c587d630165a13a6fe9460ea572e42f0bacf5f66270e7440c100775821d38a65fae", + "outpointValue": 456789345, + "receipt": "f901a60180b9010000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000004000000000000000200000000000000000800000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000000100000000000000100000000000080100000000000000000000000010000000000f89df89b940000000000000000000000000000000001000006f863a07a7c29481528ac8c2b2e93aee658fddd4dc15304fa723a5c2b88514557bcc790a0d12c37848294c3fb1cea441f6a1061f9304828fb22c324c27716c291ce37dec9a059732ca786048f153663b3e7f5666938a59501a04a3dac5e594ddc2a18eab149a00000000000000000000000000000000000000000000000000000000011e1a3008001", + "receiptMp": [ + "7006009bb22cf3b785f945d89ca152d3bf366ade390b92e2b35e4d073150572df85a48000203", + "4f2670060290077e20feb9ffb4ed5abdcb42dc4e034fe2496cca6b38fd479dcd598cfa151b000388267006009bb22cf3b785f945d89ca152d3bf366ade390b92e2b35e4d073150572df85a48000203fdd705" + ] +} diff --git a/middleware/comm/bitcoin.py b/middleware/comm/bitcoin.py index 13d208d3..62ebd0bb 100644 --- a/middleware/comm/bitcoin.py +++ b/middleware/comm/bitcoin.py @@ -108,6 +108,33 @@ def get_signature_hash_for_p2sh_input(raw_tx_hex, input_index): return sighash.hex() +def get_signature_hash_for_p2sh_p2wsh_input(raw_tx_hex, input_index, + witness_script_hex, amount): + # Given a raw BTC transaction, an input index, a raw witness script and + # an amount, this method computes the sighash corresponding to the given + # input index for segwit v0 + + tx = _deserialize_tx(raw_tx_hex) + + if input_index < 0 or input_index >= len(tx.vin): + raise ValueError( + "Asked for signature hash of input at index %d but only %d input(s) available" + % (input_index, len(tx.vin)) + ) + + try: + witness_script = bitcoin.core.CScript(bytes.fromhex(witness_script_hex)) + except Exception: + raise ValueError("Invalid witness script: %s" % witness_script_hex) + + sighash = bitcoin.core.script.SignatureHash( + witness_script, tx, input_index, bitcoin.core.script.SIGHASH_ALL, + amount, bitcoin.core.script.SIGVERSION_WITNESS_V0 + ) + + return sighash.hex() + + def get_block_hash_as_int(raw_block_header_hex): block_header = _deserialize_block_header(raw_block_header_hex) return int.from_bytes(block_header.GetHash(), byteorder="little", signed=False) @@ -118,6 +145,10 @@ def get_merkle_root(raw_block_header_hex): return block_header.hashMerkleRoot.hex() +def encode_varint(v): + return bitcoin.core.VarIntSerializer.serialize(v).hex() + + def _deserialize_block_header(raw_block_header_hex): try: return bitcoin.core.CBlockHeader.deserialize(bytes.fromhex(raw_block_header_hex)) diff --git a/middleware/comm/protocol.py b/middleware/comm/protocol.py index d4b59a95..df635613 100644 --- a/middleware/comm/protocol.py +++ b/middleware/comm/protocol.py @@ -22,7 +22,10 @@ import logging from .bip32 import BIP32Path -from .utils import is_nonempty_hex_string, is_hex_string_of_length +from .utils import \ + is_nonempty_hex_string, is_hex_string_of_length, \ + has_nonempty_hex_field, has_hex_field_of_length, \ + has_field_of_type LOGGER_NAME = "protocol" @@ -314,8 +317,13 @@ def _validate_message(self, request, what): # Also, it must: # - Contain exactly a "hash" element of type string (1) that must be a 32-byte hex # (what is "any" or "hash") - # - Contain exactly a "tx" element of type string that is a hex string and - # an "input" element of type int (2) + # - Contain exactly a "tx" element of type string that must be a hex string; + # an "input" element of type int; a "sighashComputationMode" element + # of type string that contains exactly either "legacy" (2a) or "segwit" (2b); + # and, if the latter contains "segwit", then additionally: + # o A "witnessScript" element of type string that must be a hex string + # o An "outpointValue" element of type int that must be greater than 0 and + # at most 0xffffffffffffffff # (what is "any" or "tx") # Validate message presence and components @@ -329,21 +337,33 @@ def _validate_message(self, request, what): if ( what in ["any", "hash"] and len(message) == 1 - and "hash" in message - and type(message["hash"]) == str - and is_hex_string_of_length(message["hash"], 32) + and has_hex_field_of_length(message, "hash", 32) ): return self.ERROR_CODE_OK - # (2)? + # (2a) if ( what in ["any", "tx"] - and len(message) == 2 - and "tx" in message - and "input" in message - and type(message["tx"]) == str - and is_nonempty_hex_string(message["tx"]) - and type(message["input"]) == int + and len(message) == 3 + and has_nonempty_hex_field(message, "tx") + and has_field_of_type(message, "input", int) + and has_field_of_type(message, "sighashComputationMode", str) + and message["sighashComputationMode"] == "legacy" + ): + return self.ERROR_CODE_OK + + # (2b) + if ( + what in ["any", "tx"] + and len(message) == 5 + and has_nonempty_hex_field(message, "tx") + and has_field_of_type(message, "input", int) + and has_field_of_type(message, "sighashComputationMode", str) + and message["sighashComputationMode"] == "segwit" + and has_nonempty_hex_field(message, "witnessScript") + and has_field_of_type(message, "outpointValue", int) + and message["outpointValue"] > 0 + and message["outpointValue"] <= 0xffffffffffffffff ): return self.ERROR_CODE_OK diff --git a/middleware/comm/utils.py b/middleware/comm/utils.py index 77045fc0..3e682fc8 100644 --- a/middleware/comm/utils.py +++ b/middleware/comm/utils.py @@ -104,6 +104,23 @@ def normalize_hex_string(value): return value +def has_nonempty_hex_field(mp, name): + return name in mp and \ + type(mp[name]) == str and \ + is_nonempty_hex_string(mp[name]) + + +def has_hex_field_of_length(mp, name, length): + return name in mp and \ + type(mp[name]) == str and \ + is_hex_string_of_length(mp[name], length) + + +def has_field_of_type(mp, name, tp): + return name in mp and \ + type(mp[name]) == tp + + # Utility functions to parse and use a list slice # from a string (in the python fashion [nn:mm]) _SLICE_REGEXP = re.compile("^(-?\\d*):(-?\\d*)$", re.ASCII) diff --git a/middleware/ledger/hsm2dongle.py b/middleware/ledger/hsm2dongle.py index 1f31d68f..91dbcb2a 100644 --- a/middleware/ledger/hsm2dongle.py +++ b/middleware/ledger/hsm2dongle.py @@ -21,7 +21,7 @@ # SOFTWARE. import struct -from enum import IntEnum, auto +from enum import IntEnum, Enum, auto from ledgerblue.comm import getDongle from ledgerblue.commException import CommException import hid @@ -35,6 +35,7 @@ get_coinbase_txn, get_block_hash, ) +from comm.bitcoin import encode_varint from comm.pow import coinbase_tx_get_hash import logging @@ -199,6 +200,8 @@ class _SignError(IntEnum): RECEIPT_HASH_MISMATCH = auto() NODE_CHAINING_MISMATCH = auto() RECEIPT_ROOT_MISMATCH = auto() + INVALID_SIGHASH_COMPUTATION_MODE = auto() + INVALID_EXTRADATA_SIZE = auto() # Get public key command errors @@ -333,6 +336,18 @@ class _Onboarding(IntEnum): TIMEOUT = 10 +# Sighash computation modes +class SighashComputationMode(Enum): + def __new__(cls, *args, **kwds): + obj = object.__new__(cls) + obj._value_ = args[0] + obj.netvalue = args[1] + return obj + + LEGACY = "legacy", 0 + SEGWIT = "segwit", 1 + + class HSM2DongleBaseError(RuntimeError): @property def message(self): @@ -620,8 +635,12 @@ def get_public_key(self, key_id): # btc_tx: hex string # receipt_merkle_proof: list # input_index: int + # sighash_computation_mode: a SighashComputationMode instance + # witness_script: hex string + # outpoint_value: int def sign_authorized( - self, key_id, rsk_tx_receipt, receipt_merkle_proof, btc_tx, input_index + self, key_id, rsk_tx_receipt, receipt_merkle_proof, btc_tx, input_index, + sighash_computation_mode, witness_script, outpoint_value ): # *** Signing protocol *** # The order in which things are required and then sent is: @@ -667,25 +686,60 @@ def sign_authorized( return (False, self.RESPONSE.SIGN.ERROR_PATH) return (False, self.RESPONSE.SIGN.ERROR_UNEXPECTED) - # Step 2. Send BTC transaction + # Step 2. Send BTC transaction and extra data # Prefix the BTC transaction with the total length of the payload encoded as a # 4 bytes little endian unsigned integer. The total length should include - # those 4 bytes. + # those 4 bytes plus 2 bytes for the length of the extradata and 1 byte for + # the sighash computation mode try: - LENGTH_PREFIX_IN_BYTES = 4 + PAYLOADLENGTH_LENGTH = 4 + SIGHASH_COMPUTATION_MODE_LENGTH = 1 + EXTRADATALENGTH_LENGTH = 2 + OUTPOINT_VALUE_LENGTH = 8 btc_tx_bytes = bytes.fromhex(btc_tx) - length_prefix_bytes = (len(btc_tx_bytes) + LENGTH_PREFIX_IN_BYTES).to_bytes( - LENGTH_PREFIX_IN_BYTES, byteorder="little", signed=False + + scm_bytes = sighash_computation_mode.netvalue.to_bytes( + SIGHASH_COMPUTATION_MODE_LENGTH, + byteorder='little', signed=False ) - data = length_prefix_bytes + btc_tx_bytes + ed_bytes = b"" + + if sighash_computation_mode == SighashComputationMode.SEGWIT: + ov_bytes = outpoint_value.to_bytes( + OUTPOINT_VALUE_LENGTH, + byteorder='little', signed=False + ) + + ws_bytes = bytes.fromhex(witness_script) + ws_length_bytes = bytes.fromhex(encode_varint(len(ws_bytes))) + + ed_bytes = ws_length_bytes + ws_bytes + ov_bytes + + edl_bytes = len(ed_bytes).to_bytes( + EXTRADATALENGTH_LENGTH, + byteorder='little', signed=False + ) + + payload_length = \ + PAYLOADLENGTH_LENGTH + \ + SIGHASH_COMPUTATION_MODE_LENGTH + \ + EXTRADATALENGTH_LENGTH + \ + len(btc_tx_bytes) + + payload_length_bytes = payload_length.to_bytes( + PAYLOADLENGTH_LENGTH, byteorder="little", signed=False + ) + + data = payload_length_bytes + scm_bytes + edl_bytes + btc_tx_bytes + ed_bytes response = self._send_data_in_chunks( command=self.CMD.SIGN, operation=self.OP.SIGN.BTC_TX, next_operations=[self.OP.SIGN.TX_RECEIPT], data=data, + expect_full_data=True, initial_bytes=bytes_requested, operation_name="sign", data_description="BTC tx", @@ -702,6 +756,8 @@ def sign_authorized( self.ERR.SIGN.DATA_SIZE, self.ERR.SIGN.TX_HASH_MISMATCH, self.ERR.SIGN.TX_VERSION, + self.ERR.SIGN.INVALID_SIGHASH_COMPUTATION_MODE, + self.ERR.SIGN.INVALID_EXTRADATA_SIZE, ]: return (False, self.RESPONSE.SIGN.ERROR_BTC_TX) return (False, self.RESPONSE.SIGN.ERROR_UNEXPECTED) @@ -713,6 +769,7 @@ def sign_authorized( operation=self.OP.SIGN.TX_RECEIPT, next_operations=[self.OP.SIGN.MERKLE_PROOF], data=bytes.fromhex(rsk_tx_receipt), + expect_full_data=True, initial_bytes=bytes_requested, operation_name="sign", data_description="tx receipt", @@ -760,6 +817,7 @@ def sign_authorized( operation=self.OP.SIGN.MERKLE_PROOF, next_operations=[self.OP.SIGN.SUCCESS], data=merkle_proof_bytes, + expect_full_data=True, initial_bytes=bytes_requested, operation_name="sign", data_description="receipts merkle proof", @@ -1365,6 +1423,7 @@ def _send_block_header( operation=op_chunk, next_operations=next_operations, data=bytes.fromhex(block), + expect_full_data=False, initial_bytes=bytes_requested, operation_name=operation_name, data_description=header_name, @@ -1393,6 +1452,7 @@ def _send_data_in_chunks( operation, next_operations, data, + expect_full_data, initial_bytes, operation_name, data_description, @@ -1438,6 +1498,17 @@ def _send_data_in_chunks( # We finish when the device requests the next piece of data finished = response[self.OFF.OP] != operation + # Have we finished but not sent all data when required to do so? + if expect_full_data and finished and total_bytes_sent < len(data): + self.logger.error( + "%s: expected to send all %d data bytes but sent %d and got %s", + operation_name.capitalize(), + len(data), + total_bytes_sent, + response.hex() + ) + return (False, response) + # How many bytes to send in the next message if not finished: bytes_requested = response[self.OFF.DATA] diff --git a/middleware/ledger/protocol.py b/middleware/ledger/protocol.py index 6ff84929..35ac427b 100644 --- a/middleware/ledger/protocol.py +++ b/middleware/ledger/protocol.py @@ -30,6 +30,7 @@ HSM2DongleTimeoutError, HSM2DongleCommError, HSM2FirmwareVersion, + SighashComputationMode, ) from comm.bitcoin import get_unsigned_tx, get_tx_hash @@ -294,10 +295,13 @@ def _sign(self, request): if message_validation < self.ERROR_CODE_OK: return (message_validation,) + # Shorthand + msg = request["message"] + # Make sure the transaction # is fully unsigned before sending. try: - unsigned_btc_tx = get_unsigned_tx(request["message"]["tx"]) + unsigned_btc_tx = get_unsigned_tx(msg["tx"]) self.logger.debug("Unsigned BTC tx: %s", get_tx_hash(unsigned_btc_tx)) except Exception as e: self.logger.error("Error unsigning BTC tx: %s", str(e)) @@ -310,7 +314,11 @@ def _sign(self, request): rsk_tx_receipt=request["auth"]["receipt"], receipt_merkle_proof=request["auth"]["receipt_merkle_proof"], btc_tx=unsigned_btc_tx, - input_index=request["message"]["input"], + input_index=msg["input"], + sighash_computation_mode=SighashComputationMode( + msg["sighashComputationMode"]), + witness_script=msg.get("witnessScript"), + outpoint_value=msg.get("outpointValue") ) except HSM2DongleTimeoutError: self.logger.error("Dongle timeout signing") diff --git a/middleware/tests/comm/test_protocol.py b/middleware/tests/comm/test_protocol.py index 37ec09f3..fd9c7ed0 100644 --- a/middleware/tests/comm/test_protocol.py +++ b/middleware/tests/comm/test_protocol.py @@ -392,7 +392,7 @@ def test_sign_message_presence_type(self): {"errorcode": -102}, ) - def test_sign_tx_input_hash_presence(self): + def test_sign_legacy_message_value(self): self.assertEqual( self.protocol.handle_request({ "version": 4, @@ -419,6 +419,23 @@ def test_sign_tx_input_hash_presence(self): "receipt_merkle_proof": ["aa"] }, "message": { + "sighashComputationMode": "legacy" + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "legacy", "tx": "001122" }, }), @@ -435,6 +452,24 @@ def test_sign_tx_input_hash_presence(self): "receipt_merkle_proof": ["aa"] }, "message": { + "sighashComputationMode": "legacy", + "input": 123 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "tx": "001122", "input": 123 }, }), @@ -451,6 +486,7 @@ def test_sign_tx_input_hash_presence(self): "receipt_merkle_proof": ["aa"] }, "message": { + "sighashComputationMode": "legacy", "tx": "001122", "input": "not-an-input" }, @@ -468,6 +504,7 @@ def test_sign_tx_input_hash_presence(self): "receipt_merkle_proof": ["aa"] }, "message": { + "sighashComputationMode": "legacy", "tx": "", "input": 123 }, @@ -485,6 +522,7 @@ def test_sign_tx_input_hash_presence(self): "receipt_merkle_proof": ["aa"] }, "message": { + "sighashComputationMode": "legacy", "tx": "not-a-hex", "input": 123 }, @@ -492,6 +530,298 @@ def test_sign_tx_input_hash_presence(self): {"errorcode": -102}, ) + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "somethingelse", + "tx": "001122", + "input": 123 + }, + }), + {"errorcode": -102}, + ) + + def test_sign_segwit_message_value(self): + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit" + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "witnessScript": "33445566", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "input": 123, + "witnessScript": "33445566", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "33445566", + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "tx": "001122", + "input": 123, + "witnessScript": "33445566", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": "not-an-input", + "witnessScript": "33445566", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "", + "input": 123, + "witnessScript": "33445566", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "not-a-hex", + "input": 123, + "witnessScript": "33445566", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "not-a-hex", + "outpointValue": 123000 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "33445566", + "outpointValue": "not-an-int" + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "33445566", + "outpointValue": "1122334455667788" + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "33445566", + "outpointValue": -5 + }, + }), + {"errorcode": -102}, + ) + + self.assertEqual( + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "auth": { + "receipt": "aabbcc", + "receipt_merkle_proof": ["aa"] + }, + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "33445566", + "outpointValue": 0xffffffffffffffff + 1 + }, + }), + {"errorcode": -102}, + ) + + def test_sign_hash_message_value(self): + self.assertEqual( self.protocol.handle_request({ "version": 4, @@ -567,6 +897,7 @@ def test_sign_notimplemented(self): "receipt_merkle_proof": ["aa"] }, "message": { + "sighashComputationMode": "legacy", "tx": "001122", "input": 123 }, @@ -592,11 +923,26 @@ def test_sign_notimplemented(self): "command": "sign", "keyId": "m/0/0/0/0/0", "message": { + "sighashComputationMode": "legacy", "tx": "001122", "input": 123 }, }) + with self.assertRaises(NotImplementedError): + self.protocol.handle_request({ + "version": 4, + "command": "sign", + "keyId": "m/0/0/0/0/0", + "message": { + "sighashComputationMode": "segwit", + "tx": "001122", + "input": 123, + "witnessScript": "3344556677", + "outpointValue": 123000, + }, + }) + with self.assertRaises(NotImplementedError): self.protocol.handle_request({ "version": 4, diff --git a/middleware/tests/ledger/hsm2dongle_cmds/test_signer_heartbeat.py b/middleware/tests/ledger/hsm2dongle_cmds/test_signer_heartbeat.py index 2f7ae1f2..64de1951 100644 --- a/middleware/tests/ledger/hsm2dongle_cmds/test_signer_heartbeat.py +++ b/middleware/tests/ledger/hsm2dongle_cmds/test_signer_heartbeat.py @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from ..test_hsm2dongle import _TestHSM2DongleBase +from ..test_hsm2dongle import TestHSM2DongleBase from ledger.hsm2dongle import HSM2DongleBaseError from ledger.signature import HSM2DongleSignature from ledgerblue.commException import CommException @@ -30,7 +30,7 @@ logging.disable(logging.CRITICAL) -class TestHSM2SignerHeartbeat(_TestHSM2DongleBase): +class TestHSM2SignerHeartbeat(TestHSM2DongleBase): SIG = "3046022100e4c30ef37a1228a2faf2a88c8fb52a1dfe006a222d0961" \ "c43792018481d0d5e2022100b206abd9c8a46336f9684a84083613fb" \ "e4d31c34f7c023e5716545a00a709318" diff --git a/middleware/tests/ledger/hsm2dongle_cmds/test_ui_heartbeat.py b/middleware/tests/ledger/hsm2dongle_cmds/test_ui_heartbeat.py index f2366135..f36615cf 100644 --- a/middleware/tests/ledger/hsm2dongle_cmds/test_ui_heartbeat.py +++ b/middleware/tests/ledger/hsm2dongle_cmds/test_ui_heartbeat.py @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from ..test_hsm2dongle import _TestHSM2DongleBase +from ..test_hsm2dongle import TestHSM2DongleBase from ledger.hsm2dongle import HSM2DongleBaseError from ledger.signature import HSM2DongleSignature from ledgerblue.commException import CommException @@ -30,7 +30,7 @@ logging.disable(logging.CRITICAL) -class TestHSM2UIHeartbeat(_TestHSM2DongleBase): +class TestHSM2UIHeartbeat(TestHSM2DongleBase): SIG = "3046022100e4c30ef37a1228a2faf2a88c8fb52a1dfe006a222d0961" \ "c43792018481d0d5e2022100b206abd9c8a46336f9684a84083613fb" \ "e4d31c34f7c023e5716545a00a709318" diff --git a/middleware/tests/ledger/test_hsm2dongle.py b/middleware/tests/ledger/test_hsm2dongle.py index 85f80341..921a386a 100644 --- a/middleware/tests/ledger/test_hsm2dongle.py +++ b/middleware/tests/ledger/test_hsm2dongle.py @@ -38,7 +38,7 @@ logging.disable(logging.CRITICAL) -class _TestHSM2DongleBase(TestCase): +class TestHSM2DongleBase(TestCase): DONGLE_EXCHANGE_TIMEOUT = 10 CHUNK_ERROR_MAPPINGS = [ @@ -82,6 +82,32 @@ def setUp(self, getDongleMock): def buf(self, size): return bytes(map(lambda b: b % 256, range(size))) + def parse_exchange_spec(self, spec, stop=None, replace=None): + rqs = [] + rps = [] + rq = True + stopped = False + for line in spec: + delim = ">" if rq else "<" + delim_pos = line.find(delim) + if delim_pos == -1: + raise RuntimeError("Invalid spec prefix") + name = line[:delim_pos].strip() + if name == stop: + if replace is not None: + (rqs if rq else rps).append(replace) + stopped = True + break + (rqs if rq else rps).append( + bytes.fromhex("80" + line[delim_pos+1:].replace(" ", "")) + ) + rq = not rq + + if stop is not None and not stopped: + raise RuntimeError(f"Invalid spec parsing: specified stop at '{stop}' " + "but exchange not found") + return {"requests": rqs, "responses": rps} + def spec_to_exchange(self, spec, trim=False): trim_length = spec[0][-1] if trim else 0 block_size = len(spec[0]) - trim_length @@ -109,12 +135,17 @@ def spec_to_exchange(self, spec, trim=False): return exchanges def assert_exchange(self, payloads, timeouts=None): + def ensure_cla(bs): + if bs[0] != 0x80: + return bytes([0x80]) + bs + return bs + if timeouts is None: timeouts = [None]*len(payloads) calls = list( map( lambda z: call( - bytes([0x80]) + bytes(z[0]), + ensure_cla(bytes(z[0])), timeout=(z[1] if z[1] is not None else self.DONGLE_EXCHANGE_TIMEOUT), ), zip(payloads, timeouts), @@ -136,14 +167,38 @@ def assert_exchange(self, payloads, timeouts=None): msg="%dth exchange failed" % (i + 1), ) + def do_sign_auth(self, spec): + return self.hsm2dongle.sign_authorized( + key_id=spec["keyid"], + rsk_tx_receipt=spec["receipt"], + receipt_merkle_proof=spec["mp"], + btc_tx=spec["tx"], + input_index=spec["input"], + sighash_computation_mode=spec["mode"], + witness_script=spec["ws"], + outpoint_value=spec["ov"], + ) + + def process_sign_auth_spec(self, spec, stop=None, replace=None): + pex = self.parse_exchange_spec(spec["exchanges"], stop=stop, replace=replace) + spec["requests"] = pex["requests"] + spec["responses"] = pex["responses"] + self.dongle.exchange.side_effect = spec["responses"] + return spec -class TestHSM2Dongle(_TestHSM2DongleBase): + +class TestHSM2Dongle(TestHSM2DongleBase): def test_dongle_error_codes(self): # Make sure enums are ok wrt signer definitions by testing a couple # of arbitrary values - self.assertEqual(27532, self.hsm2dongle.ERR.ADVANCE.RECEIPT_ROOT_INVALID.value) - self.assertEqual(27539, self.hsm2dongle.ERR.ADVANCE.MM_RLP_LEN_MISMATCH.value) - self.assertEqual(27553, self.hsm2dongle.ERR.ADVANCE.BROTHER_ORDER_INVALID.value) + self.assertEqual(0x6B8C, self.hsm2dongle.ERR.ADVANCE.RECEIPT_ROOT_INVALID.value) + self.assertEqual(0x6B93, self.hsm2dongle.ERR.ADVANCE.MM_RLP_LEN_MISMATCH.value) + self.assertEqual(0x6BA1, self.hsm2dongle.ERR.ADVANCE.BROTHER_ORDER_INVALID.value) + self.assertEqual(0x6A8F, self.hsm2dongle.ERR.SIGN.INVALID_PATH) + self.assertEqual( + 0x6A97, + self.hsm2dongle.ERR.SIGN.INVALID_SIGHASH_COMPUTATION_MODE.value + ) def test_connects_ok(self): self.assertEqual([call("a-debug-value")], self.getDongleMock.call_args_list) @@ -335,703 +390,8 @@ def test_get_public_key_other_error(self): self.assertEqual("aabbccddee", self.hsm2dongle.get_public_key(key_id)) self.assert_exchange([[0x04, 0x11, 0x22, 0x33, 0x44]]) - @patch("ledger.hsm2dongle.HSM2DongleSignature") - def test_sign_authorized_ok(self, HSM2DongleSignatureMock): - HSM2DongleSignatureMock.return_value = "the-signature" - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - bytes([0, 0, 0x08, 0x04 - ]), # Response to second chunk of receipt, request first 4 bytes of MP - bytes([0, 0, 0x08, - 0x03]), # Response to first chunk of MP, request additional 3 bytes - bytes([0, 0, 0x08, - 0x07]), # Response to second chunk of MP, request additional 7 bytes - bytes([0, 0, 0x81, 0xAA, 0xBB, 0xCC, - 0xDD]), # Response to second third of MP, sucess and signature - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (True, "the-signature"), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - [0x02, 0x08, 0x03, 0x03, 0x33, 0x44], # First chunk of MP - [0x02, 0x08, 0x55, 0x02, 0x66], # Second chunk of MP - [ - 0x02, - 0x08, - 0x77, - 0x05, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Third chunk of MP - ]) - self.assertEqual( - [call(bytes([0xAA, 0xBB, 0xCC, 0xDD]))], - HSM2DongleSignatureMock.call_args_list, - ) - - @parameterized.expand([ - ("data_size", 0x6A87, -4), - ("state", 0x6A89, -4), - ("node_version", 0x6A92, -4), - ("shared_prefix_too_big", 0x6A93, -4), - ("receipt_hash_mismatch", 0x6A94, -4), - ("node_chaining_mismatch", 0x6A95, -4), - ("receipt_root_mismatch", 0x6A96, -4), - ("unknown", 0x6AFF, -10), - ("unexpected", [0, 0, 0xFF], -10), - ]) - def test_sign_authorized_mp_invalid(self, _, device_error, expected_response): - if type(device_error) == int: - last_exchange = CommException("msg", device_error) - else: - last_exchange = bytes(device_error) - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - bytes([0, 0, 0x08, 0x04 - ]), # Response to second chunk of receipt, request first 4 bytes of MP - bytes([0, 0, 0x08, - 0x03]), # Response to first chunk of MP, request additional 3 bytes - last_exchange, # Response to second chunk of MP, request additional 7 bytes - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (False, expected_response), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - [0x02, 0x08, 0x03, 0x03, 0x33, 0x44], # First chunk of MP - [0x02, 0x08, 0x55, 0x02, 0x66], # Second chunk of MP - ]) - - @parameterized.expand([ - ("too_many_nodes", ["aa"]*256), - ("node_too_big", ["aa"]*100 + ["bb"*256]), - ]) - def test_sign_authorized_mp_too_big(self, _, receipt_merkle_proof): - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - bytes([0, 0, 0x08, 0x04 - ]), # Response to second chunk of receipt, request first 4 bytes of MP - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (False, -4), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=receipt_merkle_proof, - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - ]) - - def test_sign_authorized_mp_unexpected_exc(self): - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - bytes([0, 0, 0x08, 0x04 - ]), # Response to second chunk of receipt, request first 4 bytes of MP - bytes([0, 0, 0x08, - 0x03]), # Response to first chunk of MP, request additional 3 bytes - CommException( - "msg", - 0xFFFF), # Response to second chunk of MP, request additional 7 bytes - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - with self.assertRaises(HSM2DongleError): - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - [0x02, 0x08, 0x03, 0x03, 0x33, 0x44], # First chunk of MP - [0x02, 0x08, 0x55, 0x02, 0x66], # Second chunk of MP - ]) - - def test_sign_authorized_mp_invalid_format(self): - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - bytes([0, 0, 0x08, 0x04 - ]), # Response to second chunk of receipt, request first 4 bytes of MP - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (False, -4), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "not-a-hex"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - ]) - - @parameterized.expand([ - ("data_size", 0x6A87, -3), - ("state", 0x6A89, -3), - ("rlp", 0x6A8A, -3), - ("rlp_int", 0x6A8B, -3), - ("rlp_depth", 0x6A8C, -3), - ("unknown", 0x6AFF, -10), - ("unexpected", [0, 0, 0xFF], -10), - ]) - def test_sign_authorized_receipt_invalid(self, _, device_error, expected_response): - if type(device_error) == int: - last_exchange = CommException("msg", device_error) - else: - last_exchange = bytes(device_error) - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - last_exchange, # Response to second chunk of receipt, specific error - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (False, expected_response), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - ]) - - def test_sign_authorized_receipt_unexpected_error_exc(self): - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - bytes([ - 0, 0, 0x04, 0x04 - ]), # Response to second chunk of BTC tx, request first 4 bytes of receipt - bytes([0, 0, 0x04, 0x06 - ]), # Response to first chunk of receipt, request additional 6 bytes - CommException( - "", 0xFFFF), # Response to second chunk of receipt, unexpected exception - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - with self.assertRaises(HSM2DongleError): - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - [0x02, 0x04, 0x00, 0x11, 0x22, 0x33], # First chunk of receipt - [ - 0x02, - 0x04, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - ], # Second chunk of receipt - ]) - - @parameterized.expand([ - ("input", 0x6A88, -2), - ("tx_hash_mismatch", 0x6A8D, -2), - ("tx_version", 0x6A8E, -2), - ("unknown", 0x6AFF, -10), - ("unexpected", [0, 0, 0xFF], -10), - ]) - def test_sign_authorized_btctx_invalid(self, _, device_error, expected_response): - if type(device_error) == int: - last_exchange = CommException("msg", device_error) - else: - last_exchange = bytes(device_error) - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - last_exchange, # Response to second chunk of BTC tx, specific error - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (False, expected_response), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - ]) - - def test_sign_authorized_btctx_unexpected_error_exc(self): - self.dongle.exchange.reset_mock() - self.dongle.exchange.side_effect = [ - bytes([0, 0, 0x02, - 0x09]), # Response to key id, request 9 bytes of BTC tx - bytes([ - 0, 0, 0x02, 0x03 - ]), # Response to first chunk of BTC tx, request additional 4 bytes - CommException("", 0xFF), # Response to second chunk of BTC tx, exception - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - with self.assertRaises(HSM2DongleError): - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - [ - 0x02, - 0x02, - 0x0C, - 0x00, - 0x00, - 0x00, - 0xAA, - 0xBB, - 0xCC, - 0xDD, - 0xEE, - ], # Length of payload plus first chunk of BTC tx - [0x02, 0x02, 0xFF, 0x77, 0x88], # Second chunk of BTC tx - ]) - - @parameterized.expand([ - ("data_size", 0x6A87, -1), - ("data_size_auth", 0x6A90, -1), - ("data_size_noauth", 0x6A91, -1), - ("unknown", 0x6AFF, -10), - ("unexpected", [0, 0, 0xFF], -10), - ]) - def test_sign_authorized_path_invalid(self, _, device_error, expected_response): - if type(device_error) == int: - last_exchange = CommException("msg", device_error) - else: - last_exchange = bytes(device_error) - self.dongle.exchange.side_effect = [last_exchange - ] # Response to key id, specific error - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - self.assertEqual( - (False, expected_response), - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ), - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - ]) - - def test_sign_authorized_path_unexpected_error_exc(self): - self.dongle.exchange.side_effect = [ - CommException("", 0xFFFF), # Response to key id, exception - ] - key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) - with self.assertRaises(HSM2DongleError): - self.hsm2dongle.sign_authorized( - key_id=key_id, - rsk_tx_receipt="00112233445566778899", - receipt_merkle_proof=["334455", "6677", "aabbccddee"], - btc_tx="aabbccddeeff7788", - input_index=1234, - ) - - self.assert_exchange([ - [ - 0x02, - 0x01, - 0x11, - 0x22, - 0x33, - 0x44, - 0xD2, - 0x04, - 0x00, - 0x00, - ], # Path and input index - ]) +class TestHSM2DongleSignUnauthorized(TestHSM2DongleBase): @patch("ledger.hsm2dongle.HSM2DongleSignature") def test_sign_unauthorized_ok(self, HSM2DongleSignatureMock): HSM2DongleSignatureMock.return_value = "the-signature" @@ -1145,6 +505,8 @@ def test_sign_unauthorized_invalid_hash(self): self.assertFalse(self.dongle.exchange.called) + +class TestHSM2DongleBlockchainState(TestHSM2DongleBase): def test_get_blockchain_state_ok(self): self.dongle.exchange.side_effect = [ bytes([0, 0, 0x01, 0x01]) + @@ -1329,7 +691,7 @@ def test_reset_advance_blockchain_exception(self): ]) -class TestHSM2DongleAdvanceBlockchain(_TestHSM2DongleBase): +class TestHSM2DongleAdvanceBlockchain(TestHSM2DongleBase): def setup_mocks(self, mmplsize_mock, get_cb_txn_mock, @@ -1427,7 +789,7 @@ def test_advance_blockchain_ok( [0x10, 0x09] + list(brothers_spec[2][0][0][60*2:60*3]), # Blk #3 bro #1 chunk ]) - @parameterized.expand(_TestHSM2DongleBase.CHUNK_ERROR_MAPPINGS) + @parameterized.expand(TestHSM2DongleBase.CHUNK_ERROR_MAPPINGS) @patch("ledger.hsm2dongle.get_block_hash") @patch("ledger.hsm2dongle.coinbase_tx_get_hash") @patch("ledger.hsm2dongle.get_coinbase_txn") @@ -1642,7 +1004,7 @@ def test_advance_blockchain_init_error(self, _, error, response): ]) -class TestHSM2DongleUpdateAncestor(_TestHSM2DongleBase): +class TestHSM2DongleUpdateAncestor(TestHSM2DongleBase): @patch("ledger.hsm2dongle.remove_mm_fields_if_present") @patch("ledger.hsm2dongle.rlp_mm_payload_size") def test_update_ancestor_ok(self, mmplsize_mock, rmvflds_mock): diff --git a/middleware/tests/ledger/test_hsm2dongle_sign_auth_legacy.py b/middleware/tests/ledger/test_hsm2dongle_sign_auth_legacy.py new file mode 100644 index 00000000..0ab595c7 --- /dev/null +++ b/middleware/tests/ledger/test_hsm2dongle_sign_auth_legacy.py @@ -0,0 +1,272 @@ +# The MIT License (MIT) +# +# Copyright (c) 2021 RSK Labs Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from unittest.mock import Mock, patch, call +from parameterized import parameterized +from tests.ledger.test_hsm2dongle import TestHSM2DongleBase +from ledger.hsm2dongle import ( + HSM2DongleError, + SighashComputationMode, +) +from ledgerblue.commException import CommException + +import logging + +logging.disable(logging.CRITICAL) + + +class TestHSM2DongleSignAuthorizedLegacy(TestHSM2DongleBase): + def setUp(self): + super().setUp() + + self.LEGACY_SPEC = { + "exchanges": [ + "q-path >02 01 11223344 D2040000", + "a-tx0 <02 02 0C", + "q-tx0 >02 02 0F000000 00 0000 AABBCCDDEE", + "a-tx1 <02 02 03", + "q-tx1 >02 02 FF7788", + "a-rc0 <02 04 04", + "q-rc0 >02 04 00112233", + "a-rc1 <02 04 06", + "q-rc1 >02 04 445566778899", + "a-mp0 <02 08 04", + "q-mp0 >02 08 03 03 3344", + "a-mp1 <02 08 03", + "q-mp1 >02 08 55 02 66", + "a-mp2 <02 08 07", + "q-mp2 >02 08 77 05 aabbccddee", + "a-sig <02 81 AABBCCDD", + ], + "mode": SighashComputationMode.LEGACY, + "tx": "aabbccddeeff7788", + "input": 1234, + "ws": None, + "ov": None, + "receipt": "00112233445566778899", + "mp": ["334455", "6677", "aabbccddee"], + } + + @patch("ledger.hsm2dongle.HSM2DongleSignature") + def test_ok(self, HSM2DongleSignatureMock): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec({**self.LEGACY_SPEC, "keyid": key_id}) + HSM2DongleSignatureMock.return_value = "the-signature" + + self.assertEqual( + (True, "the-signature"), + self.do_sign_auth(spec) + ) + self.assert_exchange(spec["requests"]) + self.assertEqual( + [call(bytes.fromhex("aabbccdd"))], + HSM2DongleSignatureMock.call_args_list, + ) + + @parameterized.expand([ + ("data_size", 0x6A87, -4), + ("state", 0x6A89, -4), + ("node_version", 0x6A92, -4), + ("shared_prefix_too_big", 0x6A93, -4), + ("receipt_hash_mismatch", 0x6A94, -4), + ("node_chaining_mismatch", 0x6A95, -4), + ("receipt_root_mismatch", 0x6A96, -4), + ("unknown", 0x6AFF, -10), + ("unexpected", [0, 0, 0xFF], -10), + ]) + def test_mp_invalid(self, _, device_error, expected_response): + if type(device_error) == int: + last_response = CommException("msg", device_error) + else: + last_response = bytes(device_error) + + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-mp2", + replace=last_response + ) + + self.assertEqual( + (False, expected_response), + self.do_sign_auth(spec) + ) + self.assert_exchange(spec["requests"]) + + @parameterized.expand([ + ("too_many_nodes", ["aa"]*256), + ("node_too_big", ["aa"]*100 + ["bb"*256]), + ]) + def test_mp_too_big(self, _, receipt_merkle_proof): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id, "mp": receipt_merkle_proof}, + stop="q-mp0" + ) + + self.assertEqual( + (False, -4), + self.do_sign_auth(spec) + ) + self.assert_exchange(spec["requests"]) + + def test_mp_unexpected_exc(self): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-mp2", + replace=CommException("msg", 0xFFFF), + ) + + with self.assertRaises(HSM2DongleError): + self.do_sign_auth(spec) + + self.assert_exchange(spec["requests"]) + + def test_mp_invalid_format(self): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id, "mp": ["334455", "6677", "not-a-hex"]}, + stop="q-mp0", + replace=None, + ) + + self.assertEqual( + (False, -4), + self.do_sign_auth(spec) + ) + + self.assert_exchange(spec["requests"]) + + @parameterized.expand([ + ("data_size", 0x6A87, -3), + ("state", 0x6A89, -3), + ("rlp", 0x6A8A, -3), + ("rlp_int", 0x6A8B, -3), + ("rlp_depth", 0x6A8C, -3), + ("unknown", 0x6AFF, -10), + ("unexpected", [0, 0, 0xFF], -10), + ]) + def test_receipt_invalid(self, _, device_error, expected_response): + if type(device_error) == int: + last_response = CommException("msg", device_error) + else: + last_response = bytes(device_error) + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-rc1", + replace=last_response + ) + + self.assertEqual( + (False, expected_response), + self.do_sign_auth(spec) + ) + self.assert_exchange(spec["requests"]) + + def test_receipt_unexpected_error_exc(self): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-rc1", + replace=CommException("", 0xFFFF) + ) + + with self.assertRaises(HSM2DongleError): + self.do_sign_auth(spec) + self.assert_exchange(spec["requests"]) + + @parameterized.expand([ + ("input", 0x6A88, -2), + ("tx_hash_mismatch", 0x6A8D, -2), + ("tx_version", 0x6A8E, -2), + ("invalid_sighash_computation_mode", 0x6A97, -2), + ("unknown", 0x6AFF, -10), + ("unexpected", [0, 0, 0xFF], -10), + ]) + def test_btctx_invalid(self, _, device_error, expected_response): + if type(device_error) == int: + last_response = CommException("msg", device_error) + else: + last_response = bytes(device_error) + + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-tx1", + replace=last_response + ) + + self.assertEqual( + (False, expected_response), + self.do_sign_auth(spec) + ) + self.assert_exchange(spec["requests"]) + + def test_btctx_unexpected_error_exc(self): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-rc0", + replace=CommException("", 0xFF) + ) + + with self.assertRaises(HSM2DongleError): + self.do_sign_auth(spec) + self.assert_exchange(spec["requests"]) + + @parameterized.expand([ + ("data_size", 0x6A87, -1), + ("data_size_auth", 0x6A90, -1), + ("data_size_noauth", 0x6A91, -1), + ("unknown", 0x6AFF, -10), + ("unexpected", [0, 0, 0xFF], -10), + ]) + def test_path_invalid(self, _, device_error, expected_response): + if type(device_error) == int: + last_response = CommException("msg", device_error) + else: + last_response = bytes(device_error) + + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-tx0", + replace=last_response + ) + + self.assertEqual( + (False, expected_response), + self.do_sign_auth(spec) + ) + + def test_path_unexpected_error_exc(self): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec( + {**self.LEGACY_SPEC, "keyid": key_id}, + stop="a-tx0", + replace=CommException("", 0xFFFF) + ) + + with self.assertRaises(HSM2DongleError): + self.do_sign_auth(spec) diff --git a/middleware/tests/ledger/test_hsm2dongle_sign_auth_segwit.py b/middleware/tests/ledger/test_hsm2dongle_sign_auth_segwit.py new file mode 100644 index 00000000..b6e2a716 --- /dev/null +++ b/middleware/tests/ledger/test_hsm2dongle_sign_auth_segwit.py @@ -0,0 +1,149 @@ +# The MIT License (MIT) +# +# Copyright (c) 2021 RSK Labs Ltd +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from unittest.mock import Mock, patch, call +from parameterized import parameterized +from tests.ledger.test_hsm2dongle import TestHSM2DongleBase +from ledger.hsm2dongle import ( + HSM2DongleError, + SighashComputationMode, +) +from ledgerblue.commException import CommException + +import logging + +logging.disable(logging.CRITICAL) + + +class TestHSM2DongleSignAuthorizedSegwit(TestHSM2DongleBase): + def setUp(self): + super().setUp() + + self.SEGWIT_SPEC = { + "exchanges": [ + "q-path >02 01 11223344 D2040000", + "a-tx0 <02 02 0C", + "q-tx0 >02 02 0F000000 01 0E00 AABBCCDDEE", + "a-tx1 <02 02 03", + "q-tx1 >02 02 FF7788", + "a-tx2 <02 02 05", + "q-tx2 >02 02 0522446688", + "a-tx3 <02 02 09", + "q-tx3 >02 02 AA 992C0A0000000000", + "a-rc0 <02 04 04", + "q-rc0 >02 04 00112233", + "a-rc1 <02 04 06", + "q-rc1 >02 04 445566778899", + "a-mp0 <02 08 04", + "q-mp0 >02 08 03 03 3344", + "a-mp1 <02 08 03", + "q-mp1 >02 08 55 02 66", + "a-mp2 <02 08 07", + "q-mp2 >02 08 77 05 aabbccddee", + "a-sig <02 81 AABBCCDD", + ], + "mode": SighashComputationMode.SEGWIT, + "tx": "aabbccddeeff7788", + "input": 1234, + "ws": "22446688aa", + "ov": 666777, + "receipt": "00112233445566778899", + "mp": ["334455", "6677", "aabbccddee"], + } + + @patch("ledger.hsm2dongle.HSM2DongleSignature") + def test_ok(self, HSM2DongleSignatureMock): + key_id = Mock(**{"to_binary.return_value": bytes.fromhex("11223344")}) + spec = self.process_sign_auth_spec({**self.SEGWIT_SPEC, "keyid": key_id}) + HSM2DongleSignatureMock.return_value = "the-signature" + + self.assertEqual( + (True, "the-signature"), + self.do_sign_auth(spec) + ) + self.assert_exchange(spec["requests"]) + self.assertEqual( + [call(bytes.fromhex("aabbccdd"))], + HSM2DongleSignatureMock.call_args_list, + ) + + def test_long_witness_script_length(self): + exchanges = [ + "q-path >02 01 11223344 D2040000", + "a-tx0 <02 02 0C", + "q-tx0 >02 02 0F000000 01 0D01 AABBCCDDEE", + "a-tx1 <02 02 03", + "q-tx1 >02 02 FF7788", + "a-tx2 <02 02 08", + "q-tx2 >02 02 FD0201 0001020304", + "a-tx3 Date: Fri, 5 Apr 2024 08:22:24 +1300 Subject: [PATCH 2/2] Updated integration tests tag for CI; added version info to TCPSigner --- .github/workflows/run-tests.yml | 2 +- ledger/src/tcpsigner/tcpsigner.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 51f0d5a6..49e45de1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -65,7 +65,7 @@ jobs: uses: actions/checkout@v3 with: repository: rootstock/hsm-integration-test - ref: 4.0.0.plus + ref: 4.0.0.segwit.plus path: hsm-integration-test ssh-key: ${{ secrets.HSM_INTEGRATION_TEST_SSH_KEY }} diff --git a/ledger/src/tcpsigner/tcpsigner.c b/ledger/src/tcpsigner/tcpsigner.c index adbdd490..dde1c35c 100644 --- a/ledger/src/tcpsigner/tcpsigner.c +++ b/ledger/src/tcpsigner/tcpsigner.c @@ -51,6 +51,7 @@ #include "bc_advance.h" #include "bc_state.h" #include "bc_diff.h" +#include "defs.h" #include "hex_reader.h" @@ -316,6 +317,12 @@ void main(int argc, char **argv) { // Output welcome message & parameters info("TCPSigner starting.\n"); + // Output signer version + info("Signer version: %u.%u.%u\n", + VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH); + info("Signer parameters:\n"); info_hex("Checkpoint:", arguments.checkpoint, sizeof(arguments.checkpoint)); info_bigd_hex("Difficulty: ",