1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-26 17:38:39 +00:00

Byte-precise size estimate for fees

Fixes issue #232.

It assumes largest possible signature size for all inputs.  For segwit
multisig it can be .25 bytes off due to difference between segwit
encoding (varint) vs. non-segwit encoding (op_push) of the multisig script.
This commit is contained in:
Jochen Hoenicke 2017-11-08 01:13:21 +01:00 committed by Pavol Rusnak
parent a4d46b7ae1
commit e1fa7af1da
4 changed files with 110 additions and 9 deletions

View File

@ -30,6 +30,8 @@
#include "coins.h"
#include "types.pb.h"
#define ser_length_size(len) ((len) < 253 ? 1 : (len) < 0x10000 ? 3 : 5)
uint32_t ser_length(uint32_t len, uint8_t *out);
uint32_t ser_length_hash(SHA256_CTX *ctx, uint32_t len);

View File

@ -67,6 +67,7 @@ static bool multisig_fp_set, multisig_fp_mismatch;
static uint8_t multisig_fp[32];
static uint32_t in_address_n[8];
static size_t in_address_n_count;
static uint32_t tx_weight;
/* A marker for in_address_n_count to indicate a mismatch in bip32 paths in
input */
@ -79,6 +80,13 @@ static size_t in_address_n_count;
use and still allow to quickly brute-force the correct bip32 path. */
#define BIP32_MAX_LAST_ELEMENT 1000000
/* transaction header size: 4 byte version */
#define TXSIZE_HEADER 4
/* transaction footer size: 4 byte lock time */
#define TXSIZE_FOOTER 4
/* transaction segwit overhead 2 marker */
#define TXSIZE_SEGWIT_OVERHEAD 2
enum {
SIGHASH_ALL = 1,
SIGHASH_FORKID = 0x40,
@ -433,6 +441,10 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinInf
version = _version;
lock_time = _lock_time;
tx_weight = 4 * (TXSIZE_HEADER + TXSIZE_FOOTER
+ ser_length_size(inputs_count)
+ ser_length_size(outputs_count));
signatures = 0;
idx1 = 0;
to_spend = 0;
@ -590,15 +602,13 @@ static bool signing_check_fee(void) {
return false;
}
uint64_t fee = to_spend - spending;
uint64_t tx_est_size_kb = (transactionEstimateSize(inputs_count, outputs_count) + 999) / 1000;
if (fee > tx_est_size_kb * coin->maxfee_kb) {
if (fee > ((uint64_t) tx_weight * coin->maxfee_kb)/4000) {
layoutFeeOverThreshold(coin, fee);
if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
signing_abort();
return false;
}
layoutProgress(_("Signing transaction"), progress);
}
// last confirmation
layoutConfirmTx(coin, to_spend - change_spend, fee);
@ -799,6 +809,7 @@ void signing_txack(TransactionType *tx)
switch (signing_stage) {
case STAGE_REQUEST_1_INPUT:
signing_check_input(&tx->inputs[0]);
tx_weight += tx_input_weight(&tx->inputs[0]);
if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG
|| tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) {
memcpy(&input, tx->inputs, sizeof(TxInputType));
@ -849,6 +860,9 @@ void signing_txack(TransactionType *tx)
signing_abort();
return;
}
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) {
@ -939,6 +953,7 @@ void signing_txack(TransactionType *tx)
if (!signing_check_output(&tx->outputs[0])) {
return;
}
tx_weight += tx_output_weight(coin, &tx->outputs[0]);
phase1_request_next_output();
return;
case STAGE_REQUEST_4_INPUT:

View File

@ -35,19 +35,41 @@
#define SEGWIT_VERSION_0 0
/* transaction input size (without script): 32 prevhash, 4 idx, 4 sequence */
#define TXSIZE_INPUT 40
/* transaction output size (without script): 8 amount */
#define TXSIZE_OUTPUT 8
/* size of a pubkey */
#define TXSIZE_PUBKEY 33
/* size of a DER signature (3 type bytes, 3 len bytes, 33 R, 32 S, 1 sighash */
#define TXSIZE_SIGNATURE 72
/* size of a multiscript without pubkey (1 M, 1 N, 1 checksig) */
#define TXSIZE_MULTISIGSCRIPT 3
/* size of a p2wpkh script (1 version, 1 push, 20 hash) */
#define TXSIZE_WITNESSPKHASH 22
/* size of a p2wsh script (1 version, 1 push, 32 hash) */
#define TXSIZE_WITNESSSCRIPT 34
/* size of a p2pkh script (dup, hash, push, 20 pubkeyhash, equal, checksig) */
#define TXSIZE_P2PKHASH 25
/* size of a p2sh script (hash, push, 20 scripthash, equal) */
#define TXSIZE_P2SCRIPT 23
static const uint8_t segwit_header[2] = {0,1};
#define op_push_size(len) ((len) < 0x4c ? 1 : (len) < 0x100 ? 2 : \
(len) < 0x10000 ? 3 : 5)
uint32_t op_push(uint32_t i, uint8_t *out) {
if (i < 0x4C) {
out[0] = i & 0xFF;
return 1;
}
if (i < 0xFF) {
if (i < 0x100) {
out[0] = 0x4C;
out[1] = i & 0xFF;
return 2;
}
if (i < 0xFFFF) {
if (i < 0x10000) {
out[0] = 0x4D;
out[1] = i & 0xFF;
out[2] = (i >> 8) & 0xFF;
@ -560,7 +582,68 @@ void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse)
}
}
uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs)
{
return 10 + inputs * 149 + outputs * 35;
uint32_t tx_input_weight(const TxInputType *txinput) {
uint32_t input_script_size;
if (txinput->has_multisig) {
uint32_t multisig_script_size = TXSIZE_MULTISIGSCRIPT
+ txinput->multisig.pubkeys_count * (1 + TXSIZE_PUBKEY);
input_script_size = 1 // the OP_FALSE bug in multisig
+ txinput->multisig.m * (1 + TXSIZE_SIGNATURE)
+ op_push_size(multisig_script_size) + multisig_script_size;
} else {
input_script_size = (1 + TXSIZE_SIGNATURE + 1 + TXSIZE_PUBKEY);
}
uint32_t weight = 4 * TXSIZE_INPUT;
if (txinput->script_type == InputScriptType_SPENDADDRESS
|| txinput->script_type == InputScriptType_SPENDMULTISIG) {
input_script_size += ser_length_size(input_script_size);
weight += 4 * input_script_size;
} else if (txinput->script_type == InputScriptType_SPENDWITNESS
|| txinput->script_type == InputScriptType_SPENDP2SHWITNESS) {
if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS) {
weight += 4 * (2 + (txinput->has_multisig
? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH));
} else {
weight += 4; // empty input script
}
weight += input_script_size; // discounted witness
}
return weight;
}
uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput) {
uint32_t output_script_size = 0;
if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) {
output_script_size = 1 + op_push_size(txoutput->op_return_data.size)
+ txoutput->op_return_data.size;
} else if (txoutput->address_n_count > 0) {
if (txoutput->script_type == OutputScriptType_PAYTOWITNESS) {
output_script_size = txoutput->has_multisig
? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH;
} else if (txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) {
output_script_size = TXSIZE_P2SCRIPT;
} else {
output_script_size = txoutput->has_multisig
? TXSIZE_P2SCRIPT : TXSIZE_P2PKHASH;
}
} else {
uint8_t addr_raw[MAX_ADDR_RAW_SIZE];
int witver;
size_t addr_raw_len;
if (coin->bech32_prefix
&& segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, txoutput->address)) {
output_script_size = 2 + addr_raw_len;
} else {
addr_raw_len = base58_decode_check(txoutput->address, addr_raw, MAX_ADDR_RAW_SIZE);
if (coin->has_address_type
&& address_check_prefix(addr_raw, coin->address_type)) {
output_script_size = TXSIZE_P2PKHASH;
} else if (coin->has_address_type_p2sh
&& address_check_prefix(addr_raw, coin->address_type_p2sh)) {
output_script_size = TXSIZE_P2SCRIPT;
}
}
}
output_script_size += ser_length_size(output_script_size);
return 4 * (TXSIZE_OUTPUT + output_script_size);
}

View File

@ -71,6 +71,7 @@ uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output);
uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_t datalen);
void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse);
uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs);
uint32_t tx_input_weight(const TxInputType *txinput);
uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput);
#endif