mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 07:28:10 +00:00
feat(legacy): Implement CoinJoin signing.
This commit is contained in:
parent
d74c9ba0a8
commit
68bbcbdc4d
1
legacy/firmware/.changelog.d/2718.added.4
Normal file
1
legacy/firmware/.changelog.d/2718.added.4
Normal file
@ -0,0 +1 @@
|
||||
Implement coinjoin signing.
|
@ -103,10 +103,11 @@ static uint32_t unlock_path = 0;
|
||||
return; \
|
||||
}
|
||||
|
||||
#define CHECK_UNLOCKED \
|
||||
if (!session_isUnlocked()) { \
|
||||
layoutHome(); \
|
||||
return; \
|
||||
#define CHECK_UNLOCKED \
|
||||
if (!session_isUnlocked()) { \
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Locked")); \
|
||||
layoutHome(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define CHECK_PARAM(cond, errormsg) \
|
||||
|
@ -168,7 +168,15 @@ void fsm_msgSignTx(const SignTx *msg) {
|
||||
CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count,
|
||||
_("Value overflow"));
|
||||
|
||||
CHECK_PIN
|
||||
const AuthorizeCoinJoin *authorization = NULL;
|
||||
if (authorization_type == MessageType_MessageType_AuthorizeCoinJoin) {
|
||||
authorization = config_getCoinJoinAuthorization();
|
||||
if (authorization == NULL) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
CHECK_PIN
|
||||
}
|
||||
|
||||
PathSchema unlock = fsm_getUnlockedSchema(MessageType_MessageType_SignTx);
|
||||
|
||||
@ -184,11 +192,13 @@ void fsm_msgSignTx(const SignTx *msg) {
|
||||
const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL);
|
||||
if (!node) return;
|
||||
|
||||
signing_init(msg, coin, node, unlock);
|
||||
signing_init(msg, coin, node, authorization, unlock);
|
||||
}
|
||||
|
||||
void fsm_msgTxAck(TxAck *msg) {
|
||||
CHECK_UNLOCKED
|
||||
if (!signing_is_preauthorized()) {
|
||||
CHECK_UNLOCKED
|
||||
}
|
||||
|
||||
CHECK_PARAM(msg->has_tx, _("No transaction provided"));
|
||||
|
||||
|
@ -11,7 +11,6 @@ Address.address max_size:130
|
||||
Address.mac type:FT_IGNORE
|
||||
|
||||
SignTx.coin_name max_size:21
|
||||
SignTx.coinjoin_request type:FT_IGNORE
|
||||
|
||||
SignMessage.address_n max_count:8
|
||||
SignMessage.message max_size:1024
|
||||
@ -96,8 +95,9 @@ AuthorizeCoinJoin.coordinator max_size:37
|
||||
AuthorizeCoinJoin.address_n max_count:8
|
||||
AuthorizeCoinJoin.coin_name max_size:21
|
||||
|
||||
CoinJoinRequest.mask_public_key max_size:33
|
||||
CoinJoinRequest.signature max_size:64
|
||||
|
||||
# Unused messages.
|
||||
TxAckPaymentRequest skip_message:true
|
||||
PaymentRequestMemo skip_message:true
|
||||
CoinJoinRequest skip_message:true
|
||||
|
@ -103,7 +103,7 @@ 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 tx_weight;
|
||||
static uint32_t tx_weight, tx_base_weight, our_weight, our_inputs_len;
|
||||
PathSchema unlocked_schema;
|
||||
|
||||
typedef struct {
|
||||
@ -149,6 +149,13 @@ static bool is_replacement; // Is this a replacement transaction?
|
||||
static TxInfo orig_info;
|
||||
static uint8_t orig_hash[32]; // TXID of the original transaction.
|
||||
|
||||
/* Variables specific to CoinJoin transactions. */
|
||||
static secbool is_coinjoin; // Is this a CoinJoin transaction?
|
||||
static uint64_t coinjoin_coordination_fee_base;
|
||||
static AuthorizeCoinJoin coinjoin_authorization;
|
||||
static CoinJoinRequest coinjoin_request;
|
||||
static Hasher coinjoin_request_hasher;
|
||||
|
||||
/* A marker for in_address_n_count to indicate a mismatch in bip32 paths in
|
||||
input */
|
||||
#define BIP32_NOCHANGEALLOWED 1
|
||||
@ -649,6 +656,12 @@ void phase1_request_next_input(void) {
|
||||
idx2 = 0;
|
||||
}
|
||||
|
||||
if (to.is_segwit) {
|
||||
tx_base_weight += TXSIZE_SEGWIT_OVERHEAD;
|
||||
tx_weight += TXSIZE_SEGWIT_OVERHEAD + to.inputs_len;
|
||||
our_weight += TXSIZE_SEGWIT_OVERHEAD + our_inputs_len;
|
||||
}
|
||||
|
||||
send_req_2_output();
|
||||
}
|
||||
}
|
||||
@ -924,32 +937,46 @@ static bool fill_input_script_pubkey(TxInputType *in) {
|
||||
static bool validate_path(InputScriptType script_type,
|
||||
pb_size_t address_n_count, const uint32_t *address_n,
|
||||
bool has_multisig) {
|
||||
// Sanity check not critical for security. The main reason for this is that we
|
||||
// are not comfortable with using the same private key in multiple signature
|
||||
// schemes (ECDSA and Schnorr) and we want to be sure that the user went
|
||||
// through a warning screen before we sign the input.
|
||||
if (!coin_path_check(coin, script_type, address_n_count, address_n,
|
||||
has_multisig, unlocked_schema, true)) {
|
||||
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
|
||||
!coin_path_check(coin, script_type, address_n_count, address_n,
|
||||
has_multisig, unlocked_schema, false)) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
|
||||
if (is_coinjoin == sectrue) {
|
||||
// Check whether the authorization matches the parameters of the input.
|
||||
if (address_n_count !=
|
||||
coinjoin_authorization.address_n_count + BIP32_WALLET_DEPTH ||
|
||||
memcmp(address_n, coinjoin_authorization.address_n,
|
||||
sizeof(uint32_t) * coinjoin_authorization.address_n_count) !=
|
||||
0 ||
|
||||
script_type != coinjoin_authorization.script_type) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Unauthorized path"));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Sanity check not critical for security. The main reason for this is that
|
||||
// we are not comfortable with using the same private key in multiple
|
||||
// signature schemes (ECDSA and Schnorr) and we want to be sure that the
|
||||
// user went through a warning screen before we sign the input.
|
||||
if (!coin_path_check(coin, script_type, address_n_count, address_n,
|
||||
has_multisig, unlocked_schema, true)) {
|
||||
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
|
||||
!coin_path_check(coin, script_type, address_n_count, address_n,
|
||||
has_multisig, unlocked_schema, false)) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!foreign_address_confirmed) {
|
||||
if (signing_stage < STAGE_REQUEST_3_INPUT) {
|
||||
if (!fsm_layoutPathWarning()) {
|
||||
if (!foreign_address_confirmed) {
|
||||
if (signing_stage < STAGE_REQUEST_3_INPUT) {
|
||||
if (!fsm_layoutPathWarning()) {
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
foreign_address_confirmed = true;
|
||||
} else {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Transaction has changed during signing"));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
foreign_address_confirmed = true;
|
||||
} else {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Transaction has changed during signing"));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1131,8 +1158,47 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_coinjoin(const SignTx *msg,
|
||||
const AuthorizeCoinJoin *authorization) {
|
||||
if (!msg->has_coinjoin_request) {
|
||||
fsm_sendFailure(FailureType_Failure_DataError,
|
||||
_("Missing coinjoin request."));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(coin->coin_name, authorization->coin_name) != 0) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Unauthorized operation."));
|
||||
}
|
||||
|
||||
memcpy(&coinjoin_authorization, authorization,
|
||||
sizeof(coinjoin_authorization));
|
||||
memcpy(&coinjoin_request, &msg->coinjoin_request, sizeof(coinjoin_request));
|
||||
|
||||
// Begin hashing the CoinJoin request.
|
||||
hasher_Init(&coinjoin_request_hasher, HASHER_SHA2);
|
||||
hasher_Update(&coinjoin_request_hasher, (const uint8_t *)"CJR1", 4);
|
||||
size_t coordinator_len =
|
||||
strnlen(authorization->coordinator, sizeof(authorization->coordinator));
|
||||
tx_script_hash(&coinjoin_request_hasher, coordinator_len,
|
||||
(const uint8_t *)authorization->coordinator);
|
||||
uint32_t slip44 = coin->coin_type & PATH_UNHARDEN_MASK;
|
||||
hasher_Update(&coinjoin_request_hasher, (uint8_t *)&slip44, sizeof(slip44));
|
||||
hasher_Update(&coinjoin_request_hasher,
|
||||
(const uint8_t *)&coinjoin_request.fee_rate, 4);
|
||||
hasher_Update(&coinjoin_request_hasher,
|
||||
(const uint8_t *)&coinjoin_request.no_fee_threshold, 8);
|
||||
hasher_Update(&coinjoin_request_hasher,
|
||||
(const uint8_t *)&coinjoin_request.min_registrable_amount, 8);
|
||||
hasher_Update(&coinjoin_request_hasher,
|
||||
coinjoin_request.mask_public_key.bytes, 33);
|
||||
ser_length_hash(&coinjoin_request_hasher, msg->inputs_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
|
||||
PathSchema unlock) {
|
||||
const AuthorizeCoinJoin *authorization, PathSchema unlock) {
|
||||
coin = _coin;
|
||||
amount_unit = msg->has_amount_unit ? msg->amount_unit : AmountUnit_BITCOIN;
|
||||
serialize = msg->has_serialize ? msg->serialize : true;
|
||||
@ -1162,7 +1228,10 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
|
||||
}
|
||||
#endif
|
||||
|
||||
tx_weight = 4 * size;
|
||||
tx_base_weight = 4 * size;
|
||||
tx_weight = tx_base_weight;
|
||||
our_weight = tx_base_weight;
|
||||
our_inputs_len = 0;
|
||||
|
||||
foreign_address_confirmed = false;
|
||||
taproot_only = true;
|
||||
@ -1178,10 +1247,14 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
|
||||
orig_external_in = 0;
|
||||
orig_total_out = 0;
|
||||
orig_change_out = 0;
|
||||
coinjoin_coordination_fee_base = 0;
|
||||
memzero(external_inputs, sizeof(external_inputs));
|
||||
memzero(&input, sizeof(TxInputType));
|
||||
memzero(&output, sizeof(TxOutputType));
|
||||
memzero(&resp, sizeof(TxRequest));
|
||||
memzero(&coinjoin_authorization, sizeof(coinjoin_authorization));
|
||||
memzero(&coinjoin_request, sizeof(coinjoin_request));
|
||||
memzero(&coinjoin_request_hasher, sizeof(coinjoin_request_hasher));
|
||||
is_replacement = false;
|
||||
unlocked_schema = unlock;
|
||||
signing = true;
|
||||
@ -1190,6 +1263,13 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
|
||||
// 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)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t branch_id = 0;
|
||||
#if !BITCOIN_ONLY
|
||||
branch_id = info.branch_id;
|
||||
@ -1561,6 +1641,59 @@ static bool tx_info_check_outputs_hash(TxInfo *tx_info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool coinjoin_add_input(TxInputType *txi) {
|
||||
// Masks for the signable and no_fee bits in coinjoin_flags.
|
||||
const uint8_t COINJOIN_FLAGS_SIGNABLE = 0x01;
|
||||
const uint8_t COINJOIN_FLAGS_NO_FEE = 0x02;
|
||||
|
||||
hasher_Update(&coinjoin_request_hasher, (uint8_t *)&txi->coinjoin_flags, 1);
|
||||
|
||||
if (txi->script_type == InputScriptType_EXTERNAL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compute the masking bit for the signable bit in coinjoin flags.
|
||||
static CONFIDENTIAL uint8_t output_private_key[32] = {0};
|
||||
uint8_t shared_secret[65] = {0};
|
||||
bool res = (zkp_bip340_tweak_private_key(node.private_key, NULL,
|
||||
output_private_key) == 0);
|
||||
res = res && (ecdh_multiply(&secp256k1, output_private_key,
|
||||
coinjoin_request.mask_public_key.bytes,
|
||||
shared_secret) == 0);
|
||||
memzero(&output_private_key, sizeof(output_private_key));
|
||||
if (!res) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Failed to derive shared secret."));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
Hasher mask_hasher = {0};
|
||||
uint8_t mask[SHA256_DIGEST_LENGTH] = {0};
|
||||
hasher_Init(&mask_hasher, HASHER_SHA2);
|
||||
hasher_Update(&mask_hasher, shared_secret + 1, 32);
|
||||
tx_prevout_hash(&mask_hasher, txi);
|
||||
hasher_Final(&mask_hasher, mask);
|
||||
|
||||
// Ensure that the input can be signed.
|
||||
bool signable = (txi->coinjoin_flags ^ mask[0]) & COINJOIN_FLAGS_SIGNABLE;
|
||||
if (!signable) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError, _("Unauthorized input"));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add to coordination_fee_base, except for remixes and small inputs which are
|
||||
// not charged a coordination fee.
|
||||
bool no_fee = txi->coinjoin_flags & COINJOIN_FLAGS_NO_FEE;
|
||||
if (txi->amount > coinjoin_request.no_fee_threshold && !no_fee) {
|
||||
if (!add_amount(&coinjoin_coordination_fee_base, txi->amount)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool signing_add_input(TxInputType *txinput) {
|
||||
// hash all input data to check it later (relevant for fee computation)
|
||||
if (!tx_input_check_hash(&info.hasher_check, txinput)) {
|
||||
@ -1583,6 +1716,12 @@ static bool signing_add_input(TxInputType *txinput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_coinjoin == sectrue) {
|
||||
if (!coinjoin_add_input(txinput)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if !BITCOIN_ONLY
|
||||
if (coin->decred) {
|
||||
if (serialize) {
|
||||
@ -1671,6 +1810,12 @@ static bool signing_add_output(TxOutputType *txoutput) {
|
||||
|
||||
bool is_change = is_change_output(&info, txoutput);
|
||||
|
||||
uint32_t output_weight = tx_output_weight(coin, txoutput);
|
||||
tx_weight += output_weight;
|
||||
if (is_change) {
|
||||
our_weight += output_weight;
|
||||
}
|
||||
|
||||
// Don't allow adding new external outputs in replacement transactions. There
|
||||
// is actually nothing wrong with adding new external outputs, but the only
|
||||
// way to pay for them would be by supplying a new (verified) external input,
|
||||
@ -1704,7 +1849,7 @@ static bool signing_add_output(TxOutputType *txoutput) {
|
||||
|
||||
// Skip confirmation of change-outputs and skip output confirmation altogether
|
||||
// in replacement transactions.
|
||||
bool skip_confirm = is_change || is_replacement;
|
||||
bool skip_confirm = is_change || is_replacement || (is_coinjoin == sectrue);
|
||||
int co = compile_output(coin, amount_unit, &root, txoutput, &bin_output,
|
||||
!skip_confirm);
|
||||
if (!skip_confirm) {
|
||||
@ -1957,7 +2102,7 @@ static bool signing_add_orig_output(TxOutputType *orig_output) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool signing_confirm_tx(void) {
|
||||
static bool payment_confirm_tx(void) {
|
||||
if (has_unverified_external_input) {
|
||||
layoutConfirmUnverifiedExternalInputs();
|
||||
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
||||
@ -2089,6 +2234,130 @@ static bool signing_confirm_tx(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool coinjoin_confirm_tx(void) {
|
||||
// Minimum registrable output amount accepted by the CoinJoin coordinator. The
|
||||
// CoinJoin request may specify an even lower amount.
|
||||
const uint64_t MIN_REGISTRABLE_OUTPUT_AMOUNT = 5000;
|
||||
|
||||
// Largest possible weight of an output supported by Trezor (P2TR or P2WSH).
|
||||
const uint64_t MAX_OUTPUT_WEIGHT = 4 * (8 + 1 + 1 + 1 + 32);
|
||||
|
||||
// Public keys for CoinJoin request signatures.
|
||||
const uint8_t COINJOIN_REQ_PUBKEY[] = {
|
||||
0x02, 0x57, 0x03, 0xbb, 0xe1, 0x5b, 0xb0, 0x8e, 0x98, 0x21, 0xfe,
|
||||
0x64, 0xaf, 0xf6, 0xb2, 0xef, 0x1a, 0x31, 0x60, 0xe3, 0x79, 0x9d,
|
||||
0xd8, 0xf0, 0xce, 0xbf, 0x2c, 0x79, 0xe8, 0x67, 0xdd, 0x12, 0x5d};
|
||||
#if DEBUG_LINK
|
||||
// secp256k1 public key of m/0h for "all all ... all" seed.
|
||||
const uint8_t COINJOIN_REQ_PUBKEY_DEBUG[] = {
|
||||
0x03, 0x0f, 0xdf, 0x5e, 0x28, 0x9b, 0x5a, 0xef, 0x53, 0x62, 0x90,
|
||||
0x95, 0x3a, 0xe8, 0x1c, 0xe6, 0x0e, 0x84, 0x1f, 0xf9, 0x56, 0xf3,
|
||||
0x66, 0xac, 0x12, 0x3f, 0xa6, 0x9d, 0xb3, 0xc7, 0x9f, 0x21, 0xb0};
|
||||
#endif
|
||||
|
||||
// Finish hashing the CoinJoin request.
|
||||
hasher_Update(&coinjoin_request_hasher, info.hash_prevouts,
|
||||
sizeof(info.hash_prevouts));
|
||||
hasher_Update(&coinjoin_request_hasher, info.hash_outputs,
|
||||
sizeof(info.hash_outputs));
|
||||
|
||||
// Verify the CoinJoin request signature.
|
||||
uint8_t coinjoin_request_digest[SHA256_DIGEST_LENGTH] = {0};
|
||||
hasher_Final(&coinjoin_request_hasher, coinjoin_request_digest);
|
||||
#if DEBUG_LINK
|
||||
if (ecdsa_verify_digest(&secp256k1, COINJOIN_REQ_PUBKEY_DEBUG,
|
||||
coinjoin_request.signature.bytes,
|
||||
coinjoin_request_digest) == 0) {
|
||||
// success
|
||||
} else
|
||||
#endif
|
||||
if (ecdsa_verify_digest(&secp256k1, COINJOIN_REQ_PUBKEY,
|
||||
coinjoin_request.signature.bytes,
|
||||
coinjoin_request_digest) == 0) {
|
||||
// success
|
||||
} else {
|
||||
fsm_sendFailure(FailureType_Failure_DataError,
|
||||
_("Invalid signature in coinjoin request."));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (has_unverified_external_input) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Unverifiable external input."));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t mining_fee = 0;
|
||||
if (total_out <= total_in) {
|
||||
mining_fee = total_in - total_out;
|
||||
}
|
||||
|
||||
// The maximum mining fee that the user should be paying.
|
||||
uint64_t our_max_mining_fee =
|
||||
coinjoin_authorization.max_fee_per_kvbyte * ((our_weight + 3) / 4) / 1000;
|
||||
|
||||
// The coordination fee for the user's inputs.
|
||||
uint64_t our_coordination_fee =
|
||||
MIN(coinjoin_request.fee_rate,
|
||||
coinjoin_authorization.max_coordinator_fee_rate) *
|
||||
coinjoin_coordination_fee_base / FEE_RATE_DECIMALS / 100;
|
||||
|
||||
// Total fees that the user is paying.
|
||||
uint64_t our_fees = 0;
|
||||
if (change_out <= total_in - external_in) {
|
||||
our_fees = total_in - external_in - change_out;
|
||||
}
|
||||
|
||||
// For the next step we need to estimate an upper bound on the mining fee used
|
||||
// by the coordinator. The coordinator does not include the base weight of the
|
||||
// transaction when computing the mining fee, so we take this into account.
|
||||
uint64_t max_fee_per_output =
|
||||
MAX_OUTPUT_WEIGHT * mining_fee / (tx_weight - tx_base_weight);
|
||||
|
||||
// Calculate the minimum registrable output amount in a CoinJoin plus the
|
||||
// mining fee that it would cost to register. Amounts below this value are
|
||||
// left to the coordinator or miners and effectively constitute an extra fee
|
||||
// for the user.
|
||||
uint64_t min_allowed_output_amount_plus_fee =
|
||||
MIN(coinjoin_request.min_registrable_amount,
|
||||
MIN_REGISTRABLE_OUTPUT_AMOUNT) +
|
||||
max_fee_per_output;
|
||||
|
||||
if (our_fees > our_coordination_fee + our_max_mining_fee +
|
||||
min_allowed_output_amount_plus_fee) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Total fee over threshold."));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (coinjoin_authorization.max_rounds < 1) {
|
||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||
_("Exceeded number of coinjoin rounds."));
|
||||
signing_abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
coinjoin_authorization.max_rounds -= 1;
|
||||
if (coinjoin_authorization.max_rounds >= 1) {
|
||||
config_setCoinJoinAuthorization(&coinjoin_authorization);
|
||||
} else {
|
||||
config_setCoinJoinAuthorization(NULL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool signing_confirm_tx(void) {
|
||||
if (is_coinjoin == sectrue) {
|
||||
return coinjoin_confirm_tx();
|
||||
} else {
|
||||
return payment_confirm_tx();
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t signing_hash_type(const TxInputType *txinput) {
|
||||
uint32_t hash_type = SIGHASH_ALL;
|
||||
if (txinput->script_type == InputScriptType_SPENDTAPROOT) {
|
||||
@ -2851,12 +3120,17 @@ void signing_txack(TransactionType *tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
tx_weight += tx_input_weight(coin, &tx->inputs[0]);
|
||||
uint32_t input_weight = tx_input_weight(coin, &tx->inputs[0]);
|
||||
#if !BITCOIN_ONLY
|
||||
if (coin->decred) {
|
||||
tx_weight += tx_decred_witness_weight(&tx->inputs[0]);
|
||||
input_weight += tx_decred_witness_weight(&tx->inputs[0]);
|
||||
}
|
||||
#endif
|
||||
tx_weight += input_weight;
|
||||
if (is_internal_input_script_type(tx->inputs[0].script_type)) {
|
||||
our_weight += input_weight;
|
||||
our_inputs_len += 1;
|
||||
}
|
||||
|
||||
if (tx->inputs[0].script_type != InputScriptType_SPENDTAPROOT &&
|
||||
tx->inputs[0].script_type != InputScriptType_EXTERNAL) {
|
||||
@ -2884,9 +3158,6 @@ void signing_txack(TransactionType *tx) {
|
||||
}
|
||||
}
|
||||
} else if (is_segwit_input_script_type(tx->inputs[0].script_type)) {
|
||||
if (!to.is_segwit) {
|
||||
tx_weight += TXSIZE_SEGWIT_OVERHEAD + to.inputs_len;
|
||||
}
|
||||
#if !ENABLE_SEGWIT_NONSEGWIT_MIXING
|
||||
// don't mix segwit and non-segwit inputs
|
||||
if (idx1 == 0) {
|
||||
@ -3006,7 +3277,6 @@ void signing_txack(TransactionType *tx) {
|
||||
!signing_add_output(&tx->outputs[0])) {
|
||||
return;
|
||||
}
|
||||
tx_weight += tx_output_weight(coin, &tx->outputs[0]);
|
||||
|
||||
if (tx->outputs[0].has_orig_hash) {
|
||||
memcpy(&output, &tx->outputs[0], sizeof(output));
|
||||
@ -3663,3 +3933,7 @@ void signing_abort(void) {
|
||||
memzero(&root, sizeof(root));
|
||||
memzero(&node, sizeof(node));
|
||||
}
|
||||
|
||||
bool signing_is_preauthorized(void) {
|
||||
return signing && (is_coinjoin == sectrue);
|
||||
}
|
||||
|
@ -29,8 +29,9 @@
|
||||
#include "messages-bitcoin.pb.h"
|
||||
|
||||
void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root,
|
||||
PathSchema unlock);
|
||||
const AuthorizeCoinJoin *authorization, PathSchema unlock);
|
||||
void signing_abort(void);
|
||||
void signing_txack(TransactionType *tx);
|
||||
bool signing_is_preauthorized(void);
|
||||
|
||||
#endif
|
||||
|
@ -44,7 +44,6 @@ ROUND_ID_LEN = 32
|
||||
SLIP25_PATH = parse_path("m/10025h")
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
@pytest.mark.setup_client(pin=PIN)
|
||||
def test_sign_tx(client: Client):
|
||||
# NOTE: FAKE input tx
|
||||
@ -251,7 +250,6 @@ def test_sign_tx(client: Client):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_sign_tx_large(client: Client):
|
||||
# NOTE: FAKE input tx
|
||||
|
||||
@ -474,7 +472,6 @@ def test_sign_tx_spend(client: Client):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_wrong_coordinator(client: Client):
|
||||
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
|
||||
|
||||
@ -501,7 +498,6 @@ def test_wrong_coordinator(client: Client):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_wrong_account_type(client: Client):
|
||||
params = {
|
||||
"client": client,
|
||||
@ -528,7 +524,6 @@ def test_wrong_account_type(client: Client):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_cancel_authorization(client: Client):
|
||||
# Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator.
|
||||
|
||||
@ -693,7 +688,6 @@ def test_get_address(client: Client):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_t1
|
||||
def test_multisession_authorization(client: Client):
|
||||
# Authorize CoinJoin with www.example1.com in session 1.
|
||||
btc.authorize_coinjoin(
|
||||
|
@ -1,9 +1,15 @@
|
||||
{
|
||||
"T1": {
|
||||
"device_tests": {
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_cancel_authorization": "60e18060db1756fd0199dd381b41c4b20da89ed1acfe3e1378e2f511d97732bf",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_get_address": "402c3f89f6ad5fd3bc78f804b376c36c918fc685cc2c77b38c6ae030af738d22",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_get_public_key": "9b3c916759b79048a4ab3e3fe8ce0ea0cf8d4ae6cfb66a5d712f21edfdb01782",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "d2ac218c6bc361f732a65daee2ae7183ca935a7b246545ebe6346dbd33d0a702",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_sign_tx": "f630f414daeeb8e0aa3d01ed38a512cb9bfb83abd0c022e01dd02d6824ade61d",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "4365228d89327c72b2d0fa4d487fb28c19ed832e94d8c0c71db83b060c06074a",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "edeb75022cc6bff15d1274ba9bac4cf41dd8ea5771436010ae07fd441dc73b69",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_wrong_account_type": "60e18060db1756fd0199dd381b41c4b20da89ed1acfe3e1378e2f511d97732bf",
|
||||
"T1_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "60e18060db1756fd0199dd381b41c4b20da89ed1acfe3e1378e2f511d97732bf",
|
||||
"T1_bitcoin-test_bcash.py::test_attack_change_input": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b",
|
||||
"T1_bitcoin-test_bcash.py::test_send_bch_change": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b",
|
||||
"T1_bitcoin-test_bcash.py::test_send_bch_multisig_change": "0962a2e630e06b6d20282cc241be40f41bc1648d0a26247c7c008f32a197d0cb",
|
||||
|
Loading…
Reference in New Issue
Block a user