mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-03 03:50:58 +00:00
core/bitcoin: Support multiple change-outputs.
This commit is contained in:
parent
91da49266c
commit
eb28998f98
@ -14,6 +14,7 @@ _Most likely to be released on July 1st._
|
|||||||
- Dedicated `initialized` field in storage.
|
- Dedicated `initialized` field in storage.
|
||||||
- Support EXTERNAL transaction inputs with a SLIP-0019 proof of ownership. [#1052]
|
- Support EXTERNAL transaction inputs with a SLIP-0019 proof of ownership. [#1052]
|
||||||
- Support pre-signed EXTERNAL transaction inputs.
|
- Support pre-signed EXTERNAL transaction inputs.
|
||||||
|
- Support multiple change-outputs. [#1098]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `Features.pin_cached` renamed to `unlocked`.
|
- `Features.pin_cached` renamed to `unlocked`.
|
||||||
@ -168,3 +169,4 @@ Version 2.0.5 [Mar 2018]
|
|||||||
[#1056]: https://github.com/trezor/trezor-firmware/issues/1056
|
[#1056]: https://github.com/trezor/trezor-firmware/issues/1056
|
||||||
[#1067]: https://github.com/trezor/trezor-firmware/issues/1067
|
[#1067]: https://github.com/trezor/trezor-firmware/issues/1067
|
||||||
[#1074]: https://github.com/trezor/trezor-firmware/issues/1074
|
[#1074]: https://github.com/trezor/trezor-firmware/issues/1074
|
||||||
|
[#1098]: https://github.com/trezor/trezor-firmware/issues/1098
|
||||||
|
@ -67,6 +67,13 @@ async def sign_tx(
|
|||||||
res = await layout.confirm_feeoverthreshold(ctx, req.fee, req.coin)
|
res = await layout.confirm_feeoverthreshold(ctx, req.fee, req.coin)
|
||||||
utils.unimport_end(mods)
|
utils.unimport_end(mods)
|
||||||
progress.report_init()
|
progress.report_init()
|
||||||
|
elif isinstance(req, helpers.UiConfirmChangeCountOverThreshold):
|
||||||
|
mods = utils.unimport_begin()
|
||||||
|
res = await layout.confirm_change_count_over_threshold(
|
||||||
|
ctx, req.change_count
|
||||||
|
)
|
||||||
|
utils.unimport_end(mods)
|
||||||
|
progress.report_init()
|
||||||
elif isinstance(req, helpers.UiConfirmNonDefaultLocktime):
|
elif isinstance(req, helpers.UiConfirmNonDefaultLocktime):
|
||||||
mods = utils.unimport_begin()
|
mods = utils.unimport_begin()
|
||||||
res = await layout.confirm_nondefault_locktime(ctx, req.lock_time)
|
res = await layout.confirm_nondefault_locktime(ctx, req.lock_time)
|
||||||
|
@ -38,6 +38,9 @@ _BIP32_MAX_LAST_ELEMENT = const(1000000)
|
|||||||
# the number of bytes to preallocate for serialized transaction chunks
|
# the number of bytes to preallocate for serialized transaction chunks
|
||||||
_MAX_SERIALIZED_CHUNK_SIZE = const(2048)
|
_MAX_SERIALIZED_CHUNK_SIZE = const(2048)
|
||||||
|
|
||||||
|
# the maximum number of change-outputs allowed without user confirmation
|
||||||
|
_MAX_SILENT_CHANGE_COUNT = const(2)
|
||||||
|
|
||||||
|
|
||||||
class Bitcoin:
|
class Bitcoin:
|
||||||
async def signer(self) -> None:
|
async def signer(self) -> None:
|
||||||
@ -92,6 +95,7 @@ class Bitcoin:
|
|||||||
self.external_in = 0 # sum of external input amounts
|
self.external_in = 0 # sum of external input amounts
|
||||||
self.total_out = 0 # sum of output amounts
|
self.total_out = 0 # sum of output amounts
|
||||||
self.change_out = 0 # change output amount
|
self.change_out = 0 # change output amount
|
||||||
|
self.change_count = 0 # the number of change-outputs
|
||||||
self.weight = tx_weight.TxWeightCalculator(tx.inputs_count, tx.outputs_count)
|
self.weight = tx_weight.TxWeightCalculator(tx.inputs_count, tx.outputs_count)
|
||||||
|
|
||||||
# transaction and signature serialization
|
# transaction and signature serialization
|
||||||
@ -154,6 +158,8 @@ class Bitcoin:
|
|||||||
# fee > (coin.maxfee per byte * tx size)
|
# fee > (coin.maxfee per byte * tx size)
|
||||||
if fee > (self.coin.maxfee_kb / 1000) * (self.weight.get_total() / 4):
|
if fee > (self.coin.maxfee_kb / 1000) * (self.weight.get_total() / 4):
|
||||||
await helpers.confirm_feeoverthreshold(fee, self.coin)
|
await helpers.confirm_feeoverthreshold(fee, self.coin)
|
||||||
|
if self.change_count > _MAX_SILENT_CHANGE_COUNT:
|
||||||
|
await helpers.confirm_change_count_over_threshold(self.change_count)
|
||||||
if self.tx.lock_time > 0:
|
if self.tx.lock_time > 0:
|
||||||
await helpers.confirm_nondefault_locktime(self.tx.lock_time)
|
await helpers.confirm_nondefault_locktime(self.tx.lock_time)
|
||||||
if not self.external:
|
if not self.external:
|
||||||
@ -268,9 +274,10 @@ class Bitcoin:
|
|||||||
self.external_in += txi.amount
|
self.external_in += txi.amount
|
||||||
|
|
||||||
async def confirm_output(self, txo: TxOutputType, script_pubkey: bytes) -> None:
|
async def confirm_output(self, txo: TxOutputType, script_pubkey: bytes) -> None:
|
||||||
if self.change_out == 0 and self.output_is_change(txo):
|
if self.output_is_change(txo):
|
||||||
# output is change and does not need confirmation
|
# output is change and does not need confirmation
|
||||||
self.change_out = txo.amount
|
self.change_out += txo.amount
|
||||||
|
self.change_count += 1
|
||||||
else:
|
else:
|
||||||
await helpers.confirm_output(txo, self.coin)
|
await helpers.confirm_output(txo, self.coin)
|
||||||
|
|
||||||
|
@ -63,6 +63,13 @@ class UiConfirmFeeOverThreshold:
|
|||||||
__eq__ = utils.obj_eq
|
__eq__ = utils.obj_eq
|
||||||
|
|
||||||
|
|
||||||
|
class UiConfirmChangeCountOverThreshold:
|
||||||
|
def __init__(self, change_count: int):
|
||||||
|
self.change_count = change_count
|
||||||
|
|
||||||
|
__eq__ = utils.obj_eq
|
||||||
|
|
||||||
|
|
||||||
class UiConfirmForeignAddress:
|
class UiConfirmForeignAddress:
|
||||||
def __init__(self, address_n: list):
|
def __init__(self, address_n: list):
|
||||||
self.address_n = address_n
|
self.address_n = address_n
|
||||||
@ -93,6 +100,10 @@ def confirm_feeoverthreshold(fee: int, coin: CoinInfo) -> Awaitable[Any]: # typ
|
|||||||
return (yield UiConfirmFeeOverThreshold(fee, coin))
|
return (yield UiConfirmFeeOverThreshold(fee, coin))
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_change_count_over_threshold(change_count: int) -> Awaitable[Any]: # type: ignore
|
||||||
|
return (yield UiConfirmChangeCountOverThreshold(change_count))
|
||||||
|
|
||||||
|
|
||||||
def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore
|
def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore
|
||||||
return (yield UiConfirmForeignAddress(address_n))
|
return (yield UiConfirmForeignAddress(address_n))
|
||||||
|
|
||||||
|
@ -99,6 +99,20 @@ async def confirm_feeoverthreshold(
|
|||||||
await require_confirm(ctx, text, ButtonRequestType.FeeOverThreshold)
|
await require_confirm(ctx, text, ButtonRequestType.FeeOverThreshold)
|
||||||
|
|
||||||
|
|
||||||
|
async def confirm_change_count_over_threshold(
|
||||||
|
ctx: wire.Context, change_count: int
|
||||||
|
) -> None:
|
||||||
|
from trezor.ui.text import Text
|
||||||
|
from apps.common.confirm import require_confirm
|
||||||
|
|
||||||
|
text = Text("Warning", ui.ICON_SEND, ui.GREEN)
|
||||||
|
text.normal("There are {}".format(change_count))
|
||||||
|
text.normal("change-outputs.")
|
||||||
|
text.br_half()
|
||||||
|
text.normal("Continue?")
|
||||||
|
await require_confirm(ctx, text, ButtonRequestType.SignTx)
|
||||||
|
|
||||||
|
|
||||||
async def confirm_nondefault_locktime(ctx: wire.Context, lock_time: int) -> None:
|
async def confirm_nondefault_locktime(ctx: wire.Context, lock_time: int) -> None:
|
||||||
from trezor.ui.text import Text
|
from trezor.ui.text import Text
|
||||||
from apps.common.confirm import require_confirm
|
from apps.common.confirm import require_confirm
|
||||||
|
Loading…
Reference in New Issue
Block a user