core/bitcoin: Support multiple change-outputs.

pull/1102/head
Andrew Kozlik 4 years ago committed by Andrew Kozlik
parent 91da49266c
commit eb28998f98

@ -14,6 +14,7 @@ _Most likely to be released on July 1st._
- Dedicated `initialized` field in storage.
- Support EXTERNAL transaction inputs with a SLIP-0019 proof of ownership. [#1052]
- Support pre-signed EXTERNAL transaction inputs.
- Support multiple change-outputs. [#1098]
### Changed
- `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
[#1067]: https://github.com/trezor/trezor-firmware/issues/1067
[#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)
utils.unimport_end(mods)
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):
mods = utils.unimport_begin()
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
_MAX_SERIALIZED_CHUNK_SIZE = const(2048)
# the maximum number of change-outputs allowed without user confirmation
_MAX_SILENT_CHANGE_COUNT = const(2)
class Bitcoin:
async def signer(self) -> None:
@ -92,6 +95,7 @@ class Bitcoin:
self.external_in = 0 # sum of external input amounts
self.total_out = 0 # sum of output amounts
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)
# transaction and signature serialization
@ -154,6 +158,8 @@ class Bitcoin:
# fee > (coin.maxfee per byte * tx size)
if fee > (self.coin.maxfee_kb / 1000) * (self.weight.get_total() / 4):
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:
await helpers.confirm_nondefault_locktime(self.tx.lock_time)
if not self.external:
@ -268,9 +274,10 @@ class Bitcoin:
self.external_in += txi.amount
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
self.change_out = txo.amount
self.change_out += txo.amount
self.change_count += 1
else:
await helpers.confirm_output(txo, self.coin)

@ -63,6 +63,13 @@ class UiConfirmFeeOverThreshold:
__eq__ = utils.obj_eq
class UiConfirmChangeCountOverThreshold:
def __init__(self, change_count: int):
self.change_count = change_count
__eq__ = utils.obj_eq
class UiConfirmForeignAddress:
def __init__(self, address_n: list):
self.address_n = address_n
@ -93,6 +100,10 @@ def confirm_feeoverthreshold(fee: int, coin: CoinInfo) -> Awaitable[Any]: # typ
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
return (yield UiConfirmForeignAddress(address_n))

@ -99,6 +99,20 @@ async def confirm_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:
from trezor.ui.text import Text
from apps.common.confirm import require_confirm

Loading…
Cancel
Save