mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-05 12:21:16 +00:00
feat(legacy): implement amount_unit for SignTx
This commit is contained in:
parent
7f0e939359
commit
1d3166017d
@ -26,7 +26,7 @@ const CoinInfo coins[COINS_COUNT] = {
|
|||||||
% for c in supported_on("trezor1", bitcoin):
|
% for c in supported_on("trezor1", bitcoin):
|
||||||
{
|
{
|
||||||
.coin_name = ${c_str(c.coin_name)},
|
.coin_name = ${c_str(c.coin_name)},
|
||||||
.coin_shortcut = ${c_str(" " + c.coin_shortcut)},
|
.coin_shortcut = ${c_str(c.coin_shortcut)},
|
||||||
.maxfee_kb = ${c_int(c.maxfee_kb)},
|
.maxfee_kb = ${c_int(c.maxfee_kb)},
|
||||||
.signed_message_header = ${signed_message_header(c.signed_message_header)},
|
.signed_message_header = ${signed_message_header(c.signed_message_header)},
|
||||||
.has_segwit = ${c_bool(c.segwit)},
|
.has_segwit = ${c_bool(c.segwit)},
|
||||||
|
@ -92,18 +92,18 @@ static const char *address_n_str(const uint32_t *address_n,
|
|||||||
const char *abbr = 0;
|
const char *abbr = 0;
|
||||||
if (native_segwit) {
|
if (native_segwit) {
|
||||||
if (coin && coin->has_segwit && coin->bech32_prefix) {
|
if (coin && coin->has_segwit && coin->bech32_prefix) {
|
||||||
abbr = coin->coin_shortcut + 1;
|
abbr = coin->coin_shortcut;
|
||||||
}
|
}
|
||||||
} else if (p2sh_segwit) {
|
} else if (p2sh_segwit) {
|
||||||
if (coin && coin->has_segwit) {
|
if (coin && coin->has_segwit) {
|
||||||
abbr = coin->coin_shortcut + 1;
|
abbr = coin->coin_shortcut;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (coin) {
|
if (coin) {
|
||||||
if (coin->has_segwit) {
|
if (coin->has_segwit) {
|
||||||
legacy = true;
|
legacy = true;
|
||||||
}
|
}
|
||||||
abbr = coin->coin_shortcut + 1;
|
abbr = coin->coin_shortcut;
|
||||||
#if !BITCOIN_ONLY
|
#if !BITCOIN_ONLY
|
||||||
} else {
|
} else {
|
||||||
abbr = slip44_extras(address_n[1]);
|
abbr = slip44_extras(address_n[1]);
|
||||||
@ -346,10 +346,51 @@ static void render_address_dialog(const CoinInfo *coin, const char *address,
|
|||||||
oledRefresh();
|
oledRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out) {
|
static size_t format_coin_amount(uint64_t amount, const char *prefix,
|
||||||
|
const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
|
char *output, size_t output_len) {
|
||||||
|
// " " + (optional "m"/u") + shortcut + ending zero -> 16 should suffice
|
||||||
|
char suffix[16];
|
||||||
|
memzero(suffix, sizeof(suffix));
|
||||||
|
suffix[0] = ' ';
|
||||||
|
uint32_t decimals = coin->decimals;
|
||||||
|
switch (amount_unit) {
|
||||||
|
case AmountUnit_SATOSHI:
|
||||||
|
decimals = 0;
|
||||||
|
strlcpy(suffix + 1, "sat ", sizeof(suffix) - 1);
|
||||||
|
strlcpy(suffix + 5, coin->coin_shortcut, sizeof(suffix) - 5);
|
||||||
|
break;
|
||||||
|
case AmountUnit_MILLIBITCOIN:
|
||||||
|
if (decimals >= 6) {
|
||||||
|
decimals -= 6;
|
||||||
|
suffix[1] = 'u';
|
||||||
|
strlcpy(suffix + 2, coin->coin_shortcut, sizeof(suffix) - 2);
|
||||||
|
} else {
|
||||||
|
strlcpy(suffix + 1, coin->coin_shortcut, sizeof(suffix) - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AmountUnit_MICROBITCOIN:
|
||||||
|
if (decimals >= 3) {
|
||||||
|
decimals -= 3;
|
||||||
|
suffix[1] = 'm';
|
||||||
|
strlcpy(suffix + 2, coin->coin_shortcut, sizeof(suffix) - 2);
|
||||||
|
} else {
|
||||||
|
strlcpy(suffix + 1, coin->coin_shortcut, sizeof(suffix) - 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // AmountUnit_BITCOIN
|
||||||
|
strlcpy(suffix + 1, coin->coin_shortcut, sizeof(suffix) - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bn_format_uint64(amount, prefix, suffix, decimals, 0, false, output,
|
||||||
|
output_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutConfirmOutput(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
|
const TxOutputType *out) {
|
||||||
char str_out[32 + 3] = {0};
|
char str_out[32 + 3] = {0};
|
||||||
bn_format_uint64(out->amount, NULL, coin->coin_shortcut, coin->decimals, 0,
|
format_coin_amount(out->amount, NULL, coin, amount_unit, str_out,
|
||||||
false, str_out, sizeof(str_out) - 3);
|
sizeof(str_out) - 3);
|
||||||
strlcat(str_out, " to", sizeof(str_out));
|
strlcat(str_out, " to", sizeof(str_out));
|
||||||
const char *address = out->address;
|
const char *address = out->address;
|
||||||
const char *extra_line =
|
const char *extra_line =
|
||||||
@ -423,9 +464,9 @@ void layoutConfirmOpReturn(const uint8_t *data, uint32_t size) {
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool formatAmountDifference(const CoinInfo *coin, uint64_t amount1,
|
static bool formatAmountDifference(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
uint64_t amount2, char *output,
|
uint64_t amount1, uint64_t amount2,
|
||||||
size_t output_length) {
|
char *output, size_t output_length) {
|
||||||
uint64_t abs_diff = 0;
|
uint64_t abs_diff = 0;
|
||||||
const char *sign = NULL;
|
const char *sign = NULL;
|
||||||
if (amount1 >= amount2) {
|
if (amount1 >= amount2) {
|
||||||
@ -435,17 +476,20 @@ static bool formatAmountDifference(const CoinInfo *coin, uint64_t amount1,
|
|||||||
sign = "-";
|
sign = "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
return bn_format_uint64(abs_diff, sign, coin->coin_shortcut, coin->decimals,
|
return format_coin_amount(abs_diff, sign, coin, amount_unit, output,
|
||||||
0, false, output, output_length) != 0;
|
output_length) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void layoutConfirmTx(const CoinInfo *coin, uint64_t total_in,
|
void layoutConfirmTx(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
uint64_t total_out, uint64_t change_out) {
|
uint64_t total_in, uint64_t total_out,
|
||||||
|
uint64_t change_out) {
|
||||||
char str_out[32] = {0};
|
char str_out[32] = {0};
|
||||||
formatAmountDifference(coin, total_in, change_out, str_out, sizeof(str_out));
|
formatAmountDifference(coin, amount_unit, total_in, change_out, str_out,
|
||||||
|
sizeof(str_out));
|
||||||
|
|
||||||
char str_fee[32] = {0};
|
char str_fee[32] = {0};
|
||||||
formatAmountDifference(coin, total_in, total_out, str_fee, sizeof(str_fee));
|
formatAmountDifference(coin, amount_unit, total_in, total_out, str_fee,
|
||||||
|
sizeof(str_fee));
|
||||||
|
|
||||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
||||||
_("Really send"), str_out, _("from your wallet?"),
|
_("Really send"), str_out, _("from your wallet?"),
|
||||||
@ -458,8 +502,8 @@ void layoutConfirmReplacement(const char *description, uint8_t txid[32]) {
|
|||||||
description, str[0], str[1], str[2], str[3], NULL);
|
description, str[0], str[1], str[2], str[3], NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void layoutConfirmModifyFee(const CoinInfo *coin, uint64_t fee_old,
|
void layoutConfirmModifyFee(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
uint64_t fee_new) {
|
uint64_t fee_old, uint64_t fee_new) {
|
||||||
char str_fee_change[32] = {0};
|
char str_fee_change[32] = {0};
|
||||||
char str_fee_new[32] = {0};
|
char str_fee_new[32] = {0};
|
||||||
char *question = NULL;
|
char *question = NULL;
|
||||||
@ -472,21 +516,21 @@ void layoutConfirmModifyFee(const CoinInfo *coin, uint64_t fee_old,
|
|||||||
question = _("Decrease your fee by:");
|
question = _("Decrease your fee by:");
|
||||||
fee_change = fee_old - fee_new;
|
fee_change = fee_old - fee_new;
|
||||||
}
|
}
|
||||||
bn_format_uint64(fee_change, NULL, coin->coin_shortcut, coin->decimals, 0,
|
format_coin_amount(fee_change, NULL, coin, amount_unit, str_fee_change,
|
||||||
false, str_fee_change, sizeof(str_fee_change));
|
sizeof(str_fee_change));
|
||||||
|
|
||||||
bn_format_uint64(fee_new, NULL, coin->coin_shortcut, coin->decimals, 0, false,
|
format_coin_amount(fee_new, NULL, coin, amount_unit, str_fee_new,
|
||||||
str_fee_new, sizeof(str_fee_new));
|
sizeof(str_fee_new));
|
||||||
|
|
||||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
||||||
question, str_fee_change, NULL, _("Transaction fee:"),
|
question, str_fee_change, NULL, _("Transaction fee:"),
|
||||||
str_fee_new, NULL);
|
str_fee_new, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee) {
|
void layoutFeeOverThreshold(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
|
uint64_t fee) {
|
||||||
char str_fee[32] = {0};
|
char str_fee[32] = {0};
|
||||||
bn_format_uint64(fee, NULL, coin->coin_shortcut, coin->decimals, 0, false,
|
format_coin_amount(fee, NULL, coin, amount_unit, str_fee, sizeof(str_fee));
|
||||||
str_fee, sizeof(str_fee));
|
|
||||||
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
|
||||||
_("Fee"), str_fee, _("is unexpectedly high."), NULL,
|
_("Fee"), str_fee, _("is unexpectedly high."), NULL,
|
||||||
_("Send anyway?"), NULL);
|
_("Send anyway?"), NULL);
|
||||||
|
@ -46,15 +46,18 @@ void layoutProgressSwipe(const char *desc, int permil);
|
|||||||
|
|
||||||
void layoutScreensaver(void);
|
void layoutScreensaver(void);
|
||||||
void layoutHome(void);
|
void layoutHome(void);
|
||||||
void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out);
|
void layoutConfirmOutput(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
|
const TxOutputType *out);
|
||||||
void layoutConfirmOmni(const uint8_t *data, uint32_t size);
|
void layoutConfirmOmni(const uint8_t *data, uint32_t size);
|
||||||
void layoutConfirmOpReturn(const uint8_t *data, uint32_t size);
|
void layoutConfirmOpReturn(const uint8_t *data, uint32_t size);
|
||||||
void layoutConfirmTx(const CoinInfo *coin, uint64_t total_in,
|
void layoutConfirmTx(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
uint64_t total_out, uint64_t change_out);
|
uint64_t total_in, uint64_t total_out,
|
||||||
|
uint64_t change_out);
|
||||||
void layoutConfirmReplacement(const char *description, uint8_t txid[32]);
|
void layoutConfirmReplacement(const char *description, uint8_t txid[32]);
|
||||||
void layoutConfirmModifyFee(const CoinInfo *coin, uint64_t fee_old,
|
void layoutConfirmModifyFee(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
uint64_t fee_new);
|
uint64_t fee_old, uint64_t fee_new);
|
||||||
void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee);
|
void layoutFeeOverThreshold(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
|
uint64_t fee);
|
||||||
void layoutChangeCountOverThreshold(uint32_t change_count);
|
void layoutChangeCountOverThreshold(uint32_t change_count);
|
||||||
void layoutConfirmNondefaultLockTime(uint32_t lock_time,
|
void layoutConfirmNondefaultLockTime(uint32_t lock_time,
|
||||||
bool lock_time_disabled);
|
bool lock_time_disabled);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
static uint32_t change_count;
|
static uint32_t change_count;
|
||||||
static const CoinInfo *coin;
|
static const CoinInfo *coin;
|
||||||
|
static AmountUnit amount_unit;
|
||||||
static CONFIDENTIAL HDNode root;
|
static CONFIDENTIAL HDNode root;
|
||||||
static CONFIDENTIAL HDNode node;
|
static CONFIDENTIAL HDNode node;
|
||||||
static bool signing = false;
|
static bool signing = false;
|
||||||
@ -799,6 +800,7 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
|
|||||||
void signing_init(const SignTx *msg, const CoinInfo *_coin,
|
void signing_init(const SignTx *msg, const CoinInfo *_coin,
|
||||||
const HDNode *_root) {
|
const HDNode *_root) {
|
||||||
coin = _coin;
|
coin = _coin;
|
||||||
|
amount_unit = msg->has_amount_unit ? msg->amount_unit : AmountUnit_BITCOIN;
|
||||||
memcpy(&root, _root, sizeof(HDNode));
|
memcpy(&root, _root, sizeof(HDNode));
|
||||||
|
|
||||||
if (!tx_info_init(&info, msg->inputs_count, msg->outputs_count, msg->version,
|
if (!tx_info_init(&info, msg->inputs_count, msg->outputs_count, msg->version,
|
||||||
@ -1229,7 +1231,8 @@ static bool signing_check_output(TxOutputType *txoutput) {
|
|||||||
// Skip confirmation of change-outputs and skip output confirmation altogether
|
// Skip confirmation of change-outputs and skip output confirmation altogether
|
||||||
// in replacement transactions.
|
// in replacement transactions.
|
||||||
bool skip_confirm = is_change || is_replacement;
|
bool skip_confirm = is_change || is_replacement;
|
||||||
int co = compile_output(coin, &root, txoutput, &bin_output, !skip_confirm);
|
int co = compile_output(coin, amount_unit, &root, txoutput, &bin_output,
|
||||||
|
!skip_confirm);
|
||||||
if (!skip_confirm) {
|
if (!skip_confirm) {
|
||||||
layoutProgress(_("Signing transaction"), progress);
|
layoutProgress(_("Signing transaction"), progress);
|
||||||
}
|
}
|
||||||
@ -1375,7 +1378,8 @@ static bool signing_check_orig_input(TxInputType *orig_input) {
|
|||||||
static bool signing_check_orig_output(TxOutputType *orig_output) {
|
static bool signing_check_orig_output(TxOutputType *orig_output) {
|
||||||
// Compute scriptPubKey.
|
// Compute scriptPubKey.
|
||||||
TxOutputBinType orig_bin_output;
|
TxOutputBinType orig_bin_output;
|
||||||
if (compile_output(coin, &root, orig_output, &orig_bin_output, false) <= 0) {
|
if (compile_output(coin, amount_unit, &root, orig_output, &orig_bin_output,
|
||||||
|
false) <= 0) {
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
_("Failed to compile output"));
|
_("Failed to compile output"));
|
||||||
signing_abort();
|
signing_abort();
|
||||||
@ -1480,7 +1484,7 @@ static bool signing_confirm_tx(void) {
|
|||||||
if (total_out <= total_in) {
|
if (total_out <= total_in) {
|
||||||
fee = total_in - total_out;
|
fee = total_in - total_out;
|
||||||
if (fee > ((uint64_t)tx_weight * coin->maxfee_kb) / 4000) {
|
if (fee > ((uint64_t)tx_weight * coin->maxfee_kb) / 4000) {
|
||||||
layoutFeeOverThreshold(coin, fee);
|
layoutFeeOverThreshold(coin, amount_unit, fee);
|
||||||
if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold,
|
if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold,
|
||||||
false)) {
|
false)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||||
@ -1565,7 +1569,7 @@ static bool signing_confirm_tx(void) {
|
|||||||
|
|
||||||
// Fee modification.
|
// Fee modification.
|
||||||
if (fee != orig_fee) {
|
if (fee != orig_fee) {
|
||||||
layoutConfirmModifyFee(coin, orig_fee, fee);
|
layoutConfirmModifyFee(coin, amount_unit, orig_fee, fee);
|
||||||
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||||
signing_abort();
|
signing_abort();
|
||||||
@ -1586,7 +1590,7 @@ static bool signing_confirm_tx(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// last confirmation
|
// last confirmation
|
||||||
layoutConfirmTx(coin, total_in, total_out, change_out);
|
layoutConfirmTx(coin, amount_unit, total_in, total_out, change_out);
|
||||||
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||||
signing_abort();
|
signing_abort();
|
||||||
@ -2431,7 +2435,8 @@ void signing_txack(TransactionType *tx) {
|
|||||||
progress = 500 + ((signatures * progress_step +
|
progress = 500 + ((signatures * progress_step +
|
||||||
(info.inputs_count + idx2) * progress_meta_step) >>
|
(info.inputs_count + idx2) * progress_meta_step) >>
|
||||||
PROGRESS_PRECISION);
|
PROGRESS_PRECISION);
|
||||||
if (compile_output(coin, &root, tx->outputs, &bin_output, false) <= 0) {
|
if (compile_output(coin, amount_unit, &root, tx->outputs, &bin_output,
|
||||||
|
false) <= 0) {
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
_("Failed to compile output"));
|
_("Failed to compile output"));
|
||||||
signing_abort();
|
signing_abort();
|
||||||
@ -2573,7 +2578,8 @@ void signing_txack(TransactionType *tx) {
|
|||||||
if (!signing_validate_output(&tx->outputs[0])) {
|
if (!signing_validate_output(&tx->outputs[0])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (compile_output(coin, &root, tx->outputs, &bin_output, false) <= 0) {
|
if (compile_output(coin, amount_unit, &root, tx->outputs, &bin_output,
|
||||||
|
false) <= 0) {
|
||||||
fsm_sendFailure(FailureType_Failure_ProcessError,
|
fsm_sendFailure(FailureType_Failure_ProcessError,
|
||||||
_("Failed to compile output"));
|
_("Failed to compile output"));
|
||||||
signing_abort();
|
signing_abort();
|
||||||
|
@ -191,8 +191,9 @@ bool compute_address(const CoinInfo *coin, InputScriptType script_type,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in,
|
int compile_output(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
TxOutputBinType *out, bool needs_confirm) {
|
const HDNode *root, TxOutputType *in, TxOutputBinType *out,
|
||||||
|
bool needs_confirm) {
|
||||||
memzero(out, sizeof(TxOutputBinType));
|
memzero(out, sizeof(TxOutputBinType));
|
||||||
out->amount = in->amount;
|
out->amount = in->amount;
|
||||||
out->decred_script_version = DECRED_SCRIPT_VERSION;
|
out->decred_script_version = DECRED_SCRIPT_VERSION;
|
||||||
@ -328,7 +329,7 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (needs_confirm) {
|
if (needs_confirm) {
|
||||||
layoutConfirmOutput(coin, in);
|
layoutConfirmOutput(coin, amount_unit, in);
|
||||||
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
||||||
return -1; // user aborted
|
return -1; // user aborted
|
||||||
}
|
}
|
||||||
|
@ -72,8 +72,9 @@ uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len,
|
|||||||
uint32_t serialize_script_multisig(const CoinInfo *coin,
|
uint32_t serialize_script_multisig(const CoinInfo *coin,
|
||||||
const MultisigRedeemScriptType *multisig,
|
const MultisigRedeemScriptType *multisig,
|
||||||
uint8_t sighash, uint8_t *out);
|
uint8_t sighash, uint8_t *out);
|
||||||
int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in,
|
int compile_output(const CoinInfo *coin, AmountUnit amount_unit,
|
||||||
TxOutputBinType *out, bool needs_confirm);
|
const HDNode *root, TxOutputType *in, TxOutputBinType *out,
|
||||||
|
bool needs_confirm);
|
||||||
|
|
||||||
void tx_input_check_hash(Hasher *hasher, const TxInputType *input);
|
void tx_input_check_hash(Hasher *hasher, const TxInputType *input);
|
||||||
uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input);
|
uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input);
|
||||||
|
Loading…
Reference in New Issue
Block a user