feat(legacy): Improve signing progress.

pull/2850/head
Andrew Kozlik 1 year ago committed by matejcik
parent 300c986851
commit 2760322eab

@ -34,6 +34,10 @@
#ifdef USE_SECP256K1_ZKP_ECDSA
#include "zkp_ecdsa.h"
#endif
#if DEBUG_LINK
#include <inttypes.h>
#include <stdio.h>
#endif
static uint32_t change_count;
static const CoinInfo *coin;
@ -102,13 +106,16 @@ static uint8_t decred_hash_prefix[32];
static uint64_t total_in, external_in, total_out, change_out;
static uint64_t orig_total_in, orig_external_in, orig_total_out,
orig_change_out;
static uint32_t progress, progress_step, progress_meta_step;
static uint32_t progress_step, progress_steps, progress_substep,
progress_substeps, progress_midpoint, progress_update;
static const char *progress_label;
static uint32_t tx_weight, tx_base_weight, our_weight, our_inputs_len;
PathSchema unlocked_schema;
typedef struct {
uint32_t inputs_count;
uint32_t outputs_count;
uint32_t segwit_count;
uint32_t next_legacy_input;
uint32_t min_sequence;
bool multisig_fp_set;
@ -370,6 +377,135 @@ static bool is_external_input(uint32_t i) {
return external_inputs[i / 32] & (1 << (i % 32));
}
static void report_progress(bool force) {
static uint32_t update_ctr = 0;
if (!force && update_ctr < progress_update) {
update_ctr++;
return;
}
uint32_t progress = 0;
if (progress_midpoint != 0) {
if (progress_step < progress_midpoint) {
// Checking previous transactions.
progress =
(500 * progress_step + 500 * progress_substep / progress_substeps) /
progress_midpoint;
} else {
// Signing transaction after checking. No substeps.
progress = 500 + 500 * (progress_step - progress_midpoint) /
(progress_steps - progress_midpoint);
}
} else {
// Loading transaction or signing transaction without checking. No substeps.
progress = 1000 * progress_step / progress_steps;
}
layoutProgress(progress_label, progress);
update_ctr = 0;
}
#if DEBUG_LINK
static bool assert_progress_finished(void) {
char line[54] = {0};
if (progress_step != progress_steps) {
snprintf(line, sizeof(line), "%s finished at %" PRIu32 "/%" PRIu32,
progress_label, progress_step, progress_steps);
} else if (progress_substep != 0) {
snprintf(line, sizeof(line), "%s finished at substep %" PRIu32,
progress_label, progress_substep);
} else {
return true;
}
fsm_sendFailure(FailureType_Failure_FirmwareError, line);
signing_abort();
return false;
}
#endif
static void init_loading_progress(void) {
progress_label = _("Loading transaction");
progress_step = 0;
progress_substep = 0;
progress_substeps = 1; // no substeps in tx loading
progress_midpoint = 0; // no midpoint in tx loading
progress_update = 10; // update screen every 10 steps
// Stage 1 and 2 - load inputs and outputs
progress_steps = info.inputs_count + info.outputs_count;
report_progress(true);
}
static void init_signing_progress(void) {
progress_label = _("Signing transaction");
progress_step = 0;
progress_steps = 0;
progress_update = 20; // update screen every 20 steps
// Verify previous transactions (STAGE_REQUEST_3_PREV_*).
// We don't know how long it will take to fetch the previous transactions. If
// they need to be checked, then we will reserve 50 % of the progress for
// this. Once we fetch a prev_tx's metadata, we subdivide the reserved space
// into substeps which represent the progress of fetching one prev_tx input or
// output.
if (!taproot_only && !(coin->overwintered && info.version == 5)) {
progress_steps += info.inputs_count;
progress_midpoint = progress_steps;
}
uint32_t external_count = 0;
for (size_t i = 0; i < info.inputs_count; ++i) {
external_count += is_external_input(i);
}
// Process inputs.
if (!(coin->force_bip143 || coin->overwintered || coin->decred)) {
// Sign and optionally serialize legacy inputs (STAGE_REQUEST_4_*).
progress_steps += (info.inputs_count - info.segwit_count - external_count) *
(info.inputs_count + info.outputs_count);
if (serialize) {
// Serialize non-legacy inputs (STAGE_REQUEST_NONLEGACY_INPUT).
progress_steps += info.segwit_count + external_count;
}
if (is_replacement) {
// Verify original input signatures (STAGE_REQUEST_3_ORIG_*).
progress_steps +=
(orig_info.inputs_count - orig_info.segwit_count - external_count) *
(orig_info.inputs_count + orig_info.outputs_count);
progress_steps += orig_info.segwit_count;
}
} else {
// Sign and optionally serialize each input (STAGE_REQUEST_NONLEGACY_INPUT,
// or in case of Decred STAGE_REQUEST_DECRED_WITNESS).
progress_steps += info.inputs_count;
if (is_replacement) {
// Verify each original input (STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT).
progress_steps += orig_info.inputs_count;
}
}
if (to.is_segwit) {
if (serialize) {
// Serialize witnesses for all inputs (STAGE_REQUEST_SEGWIT_WITNESS).
progress_steps += info.inputs_count;
} else {
// Process witnesses for all internal inputs
// (STAGE_REQUEST_SEGWIT_WITNESS).
progress_steps += info.inputs_count - external_count;
}
}
// Serialize outputs (STAGE_REQUEST_5_OUTPUT).
if (serialize && !coin->decred) {
progress_steps += info.outputs_count;
}
report_progress(true);
}
void send_req_1_input(void) {
signing_stage = STAGE_REQUEST_1_INPUT;
resp.has_request_type = true;
@ -616,6 +752,11 @@ void send_req_5_output(void) {
}
void send_req_finished(void) {
#if DEBUG_LINK
if (!assert_progress_finished()) {
return;
}
#endif
resp.has_request_type = true;
resp.request_type = RequestType_TXFINISHED;
msg_write(MessageType_MessageType_TxRequest, &resp);
@ -1056,6 +1197,7 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
tx_info->inputs_count = inputs_count;
tx_info->outputs_count = outputs_count;
tx_info->segwit_count = 0;
tx_info->next_legacy_input = 0xffffffff;
tx_info->min_sequence = SEQUENCE_FINAL;
tx_info->multisig_fp_set = false;
@ -1256,11 +1398,6 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
is_replacement = false;
unlocked_schema = unlock;
signing = true;
progress = 0;
// we step by 500/inputs_count per input in phase1 and phase2
// this means 50 % per phase.
progress_step = (500 << PROGRESS_PRECISION) / info.inputs_count;
is_coinjoin = (authorization != NULL) ? sectrue : secfalse;
if (is_coinjoin == sectrue) {
if (!init_coinjoin(msg, authorization)) {
@ -1292,7 +1429,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
hasher_Init(&hasher_check, HASHER_SHA2);
layoutProgressSwipe(_("Signing transaction"), 0);
init_loading_progress();
send_req_1_input();
}
@ -1511,6 +1648,10 @@ static bool tx_info_add_input(TxInfo *tx_info, const TxInputType *txinput) {
extract_input_bip32_path(tx_info, txinput);
}
if (is_segwit_input_script_type(txinput->script_type)) {
tx_info->segwit_count++;
}
// Remember the minimum nSequence value.
if (txinput->sequence < tx_info->min_sequence) {
tx_info->min_sequence = txinput->sequence;
@ -1689,6 +1830,9 @@ static bool coinjoin_add_input(TxInputType *txi) {
return false;
}
}
// Since this took a longer time, update progress.
report_progress(true);
return true;
}
@ -1748,6 +1892,10 @@ static bool signing_check_prevtx_hash(void) {
return false;
}
// prevtx is checked
progress_step++;
progress_substep = 0;
if (idx1 < info.inputs_count - 1) {
idx1++;
send_req_3_input();
@ -1756,10 +1904,6 @@ static bool signing_check_prevtx_hash(void) {
return false;
}
// Everything was checked, now phase 2 begins and the transaction is signed.
progress_meta_step =
progress_step / (info.inputs_count + info.outputs_count);
layoutProgress(_("Signing transaction"), progress);
idx1 = 0;
#if !BITCOIN_ONLY
if (coin->decred) {
@ -1851,7 +1995,7 @@ static bool signing_add_output(TxOutputType *txoutput) {
int co = compile_output(coin, amount_unit, &root, txoutput, &bin_output,
!skip_confirm);
if (!skip_confirm) {
layoutProgress(_("Signing transaction"), progress);
report_progress(true);
}
if (co < 0) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
@ -2773,6 +2917,14 @@ static void phase1_finish(void) {
return;
}
#if DEBUG_LINK
if (!assert_progress_finished()) {
return;
}
#endif
// Now phase 2 begins and the transaction is signed.
init_signing_progress();
if (taproot_only) {
// All internal inputs are Taproot. We do not need to verify that their
// parameters match previous transactions. We can trust the amounts and
@ -3092,16 +3244,13 @@ void signing_txack(TransactionType *tx) {
return;
}
static int update_ctr = 0;
if (update_ctr++ == 20) {
layoutProgress(_("Signing transaction"), progress);
update_ctr = 0;
}
report_progress(false);
memzero(&resp, sizeof(TxRequest));
switch (signing_stage) {
case STAGE_REQUEST_1_INPUT:
progress_step++;
if (!signing_validate_input(&tx->inputs[0]) ||
!signing_add_input(&tx->inputs[0])) {
return;
@ -3271,6 +3420,7 @@ void signing_txack(TransactionType *tx) {
phase1_request_next_input();
return;
case STAGE_REQUEST_2_OUTPUT:
progress_step++;
if (!signing_validate_output(&tx->outputs[0]) ||
!signing_add_output(&tx->outputs[0])) {
return;
@ -3431,7 +3581,7 @@ void signing_txack(TransactionType *tx) {
tp.is_decred = true;
}
#endif
progress_meta_step = progress_step / (tp.inputs_len + tp.outputs_len);
progress_substeps = tp.inputs_len + tp.outputs_len;
idx2 = 0;
if (tp.inputs_len > 0) {
send_req_3_prev_input();
@ -3444,8 +3594,7 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress = (idx1 * progress_step + idx2 * progress_meta_step) >>
PROGRESS_PRECISION;
progress_substep++;
if (!tx_serialize_input_hash(&tp, tx->inputs)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize input"));
@ -3464,9 +3613,7 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_bin_output(&tx->bin_outputs[0])) {
return;
}
progress = (idx1 * progress_step +
(tp.inputs_len + idx2) * progress_meta_step) >>
PROGRESS_PRECISION;
progress_substep++;
if (!tx_serialize_output_hash(&tp, tx->bin_outputs)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize output"));
@ -3500,7 +3647,7 @@ void signing_txack(TransactionType *tx) {
#endif
}
if (idx2 < tp.outputs_len - 1) {
/* Check prevtx of next input */
/* Check next output of prevtx */
idx2++;
send_req_3_prev_output();
#if !BITCOIN_ONLY
@ -3541,6 +3688,7 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_input(tx->inputs)) {
return;
}
progress_step++;
// Add input to the outer transaction check.
if (!tx_input_check_hash(&hasher_check, tx->inputs)) {
@ -3563,6 +3711,7 @@ void signing_txack(TransactionType *tx) {
!signing_hash_orig_input(tx->inputs)) {
return;
}
progress_step++;
idx2++;
if (idx2 < orig_info.inputs_count) {
@ -3587,6 +3736,7 @@ void signing_txack(TransactionType *tx) {
!signing_hash_orig_output(tx->outputs)) {
return;
}
progress_step++;
idx2++;
if (idx2 < orig_info.outputs_count) {
@ -3613,9 +3763,8 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress =
500 + ((signatures * progress_step + idx2 * progress_meta_step) >>
PROGRESS_PRECISION);
progress_step++;
if (idx2 == 0) {
tx_init(&ti, info.inputs_count, info.outputs_count, info.version,
info.lock_time, info.expiry, tx->branch_id, 0,
@ -3670,9 +3819,8 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_output(&tx->outputs[0])) {
return;
}
progress = 500 + ((signatures * progress_step +
(info.inputs_count + idx2) * progress_meta_step) >>
PROGRESS_PRECISION);
progress_step++;
if (compile_output(coin, amount_unit, &root, tx->outputs, &bin_output,
false) <= 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
@ -3696,11 +3844,9 @@ void signing_txack(TransactionType *tx) {
!signing_sign_legacy_input()) {
return;
}
// since this took a longer time, update progress
signatures++;
progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION);
layoutProgress(_("Signing transaction"), progress);
update_ctr = 0;
// since this took a longer time, update progress
report_progress(true);
phase2_request_next_input(false);
}
return;
@ -3709,6 +3855,7 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress_step++;
if (is_external_input(idx1) !=
(tx->inputs[0].script_type == InputScriptType_EXTERNAL)) {
@ -3766,11 +3913,9 @@ void signing_txack(TransactionType *tx) {
if (!signing_sign_ecdsa(&tx->inputs[0], node.private_key,
node.public_key, hash))
return;
// since this took a longer time, update progress
signatures++;
progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION);
layoutProgress(_("Signing transaction"), progress);
update_ctr = 0;
// since this took a longer time, update progress
report_progress(true);
} else if (tx->inputs[0].script_type ==
InputScriptType_SPENDP2SHWITNESS &&
!tx->inputs[0].has_multisig) {
@ -3824,6 +3969,8 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_output(&tx->outputs[0])) {
return;
}
progress_step++;
if (compile_output(coin, amount_unit, &root, tx->outputs, &bin_output,
false) <= 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
@ -3846,9 +3993,8 @@ void signing_txack(TransactionType *tx) {
return;
}
signatures++;
progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION);
layoutProgress(_("Signing transaction"), progress);
update_ctr = 0;
progress_step++;
report_progress(true);
phase2_request_next_witness(false);
return;
@ -3858,9 +4004,6 @@ void signing_txack(TransactionType *tx) {
if (!signing_validate_input(&tx->inputs[0])) {
return;
}
progress =
500 + ((signatures * progress_step + idx2 * progress_meta_step) >>
PROGRESS_PRECISION);
if (idx1 == 0) {
// witness
tx_init(&to, info.inputs_count, info.outputs_count, info.version,
@ -3902,11 +4045,10 @@ void signing_txack(TransactionType *tx) {
if (!signing_sign_decred_input(&tx->inputs[0])) {
return;
}
// since this took a longer time, update progress
signatures++;
progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION);
layoutProgress(_("Signing transaction"), progress);
update_ctr = 0;
progress_step++;
// since this took a longer time, update progress
report_progress(true);
if (idx1 < info.inputs_count - 1) {
idx1++;
send_req_decred_witness();

Loading…
Cancel
Save