From afd900f9b1badf949f1deda58ed2fc2a98e04877 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Wed, 12 Oct 2022 16:17:51 +0200 Subject: [PATCH] feat(common): Add CoinJoin request message. [no changelog] --- common/protob/messages-bitcoin.proto | 16 ++++++++- core/src/trezor/messages.py | 26 ++++++++++++++ .../firmware/protob/messages-bitcoin.options | 2 ++ python/src/trezorlib/messages.py | 35 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/common/protob/messages-bitcoin.proto b/common/protob/messages-bitcoin.proto index df6eb2793..6831e6d9e 100644 --- a/common/protob/messages-bitcoin.proto +++ b/common/protob/messages-bitcoin.proto @@ -198,6 +198,18 @@ message SignTx { optional AmountUnit amount_unit = 11 [default=BITCOIN]; // show amounts in optional bool decred_staking_ticket = 12 [default=false]; // only for Decred, this is signing a ticket purchase optional bool serialize = 13 [default=true]; // serialize the full transaction, as opposed to only outputting the signatures + optional CoinJoinRequest coinjoin_request = 14; // only for preauthorized CoinJoins + + /** + * Signing request for a CoinJoin transaction. + */ + message CoinJoinRequest { + required uint32 fee_rate = 1; // coordination fee rate in units of 10^-8 percent + required uint64 no_fee_threshold = 2; // PlebsDontPayThreshold in Wasabi, the input amount above which the fee rate applies + required uint64 min_registrable_amount = 3; // minimum registrable output amount + required bytes mask_public_key = 4; // ephemeral secp256k1 public key used for masking coinjoin_flags, 33 bytes in compressed form + required bytes signature = 5; // the trusted party's signature of the CoinJoin request digest + } } /** @@ -306,6 +318,7 @@ message TxAck { optional uint32 orig_index = 17; // index of the input in the original transaction (used when creating a replacement transaction) optional DecredStakingSpendType decred_staking_spend = 18; // if not None this holds the type of stake spend: revocation or stake generation optional bytes script_pubkey = 19; // scriptPubKey of the previous output spent by this input, only set of EXTERNAL inputs + optional uint32 coinjoin_flags = 20 [default=0]; // bit field of CoinJoin-specific flags } /** * Structure representing compiled transaction output @@ -359,6 +372,7 @@ message TxInput { optional uint32 orig_index = 17; // index of the input in the original transaction (used when creating a replacement transaction) optional DecredStakingSpendType decred_staking_spend = 18; // if not None this holds the type of stake spend: revocation or stake generation optional bytes script_pubkey = 19; // scriptPubKey of the previous output spent by this input, only set of EXTERNAL inputs + optional uint32 coinjoin_flags = 20 [default=0]; // bit field of CoinJoin-specific flags } /** Data type for transaction output to be signed. @@ -427,7 +441,7 @@ message TxAckPaymentRequest { option (unstable) = true; optional bytes nonce = 1; // the nonce used in the signature computation - required string recipient_name = 2; // merchant's name or coordinator's name in case of CoinJoin + required string recipient_name = 2; // merchant's name repeated PaymentRequestMemo memos = 3; // any memos that were signed as part of the request optional uint64 amount = 4; // the sum of the external output amounts requested, required for non-CoinJoin required bytes signature = 5; // the trusted party's signature of the paymentRequestDigest diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index ddc0700f6..9a5911112 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -595,6 +595,7 @@ if TYPE_CHECKING: amount_unit: "AmountUnit" decred_staking_ticket: "bool" serialize: "bool" + coinjoin_request: "CoinJoinRequest | None" def __init__( self, @@ -611,6 +612,7 @@ if TYPE_CHECKING: amount_unit: "AmountUnit | None" = None, decred_staking_ticket: "bool | None" = None, serialize: "bool | None" = None, + coinjoin_request: "CoinJoinRequest | None" = None, ) -> None: pass @@ -653,6 +655,7 @@ if TYPE_CHECKING: orig_index: "int | None" decred_staking_spend: "DecredStakingSpendType | None" script_pubkey: "bytes | None" + coinjoin_flags: "int" def __init__( self, @@ -673,6 +676,7 @@ if TYPE_CHECKING: orig_index: "int | None" = None, decred_staking_spend: "DecredStakingSpendType | None" = None, script_pubkey: "bytes | None" = None, + coinjoin_flags: "int | None" = None, ) -> None: pass @@ -972,6 +976,28 @@ if TYPE_CHECKING: def is_type_of(cls, msg: Any) -> TypeGuard["HDNodePathType"]: return isinstance(msg, cls) + class CoinJoinRequest(protobuf.MessageType): + fee_rate: "int" + no_fee_threshold: "int" + min_registrable_amount: "int" + mask_public_key: "bytes" + signature: "bytes" + + def __init__( + self, + *, + fee_rate: "int", + no_fee_threshold: "int", + min_registrable_amount: "int", + mask_public_key: "bytes", + signature: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["CoinJoinRequest"]: + return isinstance(msg, cls) + class TxRequestDetailsType(protobuf.MessageType): request_index: "int | None" tx_hash: "bytes | None" diff --git a/legacy/firmware/protob/messages-bitcoin.options b/legacy/firmware/protob/messages-bitcoin.options index aff252c67..020bb69c8 100644 --- a/legacy/firmware/protob/messages-bitcoin.options +++ b/legacy/firmware/protob/messages-bitcoin.options @@ -11,6 +11,7 @@ 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 @@ -86,3 +87,4 @@ GetOwnershipProof skip_message:true OwnershipProof skip_message:true TxAckPaymentRequest skip_message:true PaymentRequestMemo skip_message:true +CoinJoinRequest skip_message:true diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 2c08eb50d..4e3be96e2 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -1173,6 +1173,7 @@ class SignTx(protobuf.MessageType): 11: protobuf.Field("amount_unit", "AmountUnit", repeated=False, required=False), 12: protobuf.Field("decred_staking_ticket", "bool", repeated=False, required=False), 13: protobuf.Field("serialize", "bool", repeated=False, required=False), + 14: protobuf.Field("coinjoin_request", "CoinJoinRequest", repeated=False, required=False), } def __init__( @@ -1191,6 +1192,7 @@ class SignTx(protobuf.MessageType): amount_unit: Optional["AmountUnit"] = AmountUnit.BITCOIN, decred_staking_ticket: Optional["bool"] = False, serialize: Optional["bool"] = True, + coinjoin_request: Optional["CoinJoinRequest"] = None, ) -> None: self.outputs_count = outputs_count self.inputs_count = inputs_count @@ -1205,6 +1207,7 @@ class SignTx(protobuf.MessageType): self.amount_unit = amount_unit self.decred_staking_ticket = decred_staking_ticket self.serialize = serialize + self.coinjoin_request = coinjoin_request class TxRequest(protobuf.MessageType): @@ -1260,6 +1263,7 @@ class TxInput(protobuf.MessageType): 17: protobuf.Field("orig_index", "uint32", repeated=False, required=False), 18: protobuf.Field("decred_staking_spend", "DecredStakingSpendType", repeated=False, required=False), 19: protobuf.Field("script_pubkey", "bytes", repeated=False, required=False), + 20: protobuf.Field("coinjoin_flags", "uint32", repeated=False, required=False), } def __init__( @@ -1281,6 +1285,7 @@ class TxInput(protobuf.MessageType): orig_index: Optional["int"] = None, decred_staking_spend: Optional["DecredStakingSpendType"] = None, script_pubkey: Optional["bytes"] = None, + coinjoin_flags: Optional["int"] = 0, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.prev_hash = prev_hash @@ -1298,6 +1303,7 @@ class TxInput(protobuf.MessageType): self.orig_index = orig_index self.decred_staking_spend = decred_staking_spend self.script_pubkey = script_pubkey + self.coinjoin_flags = coinjoin_flags class TxOutput(protobuf.MessageType): @@ -1633,6 +1639,32 @@ class HDNodePathType(protobuf.MessageType): self.node = node +class CoinJoinRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = None + FIELDS = { + 1: protobuf.Field("fee_rate", "uint32", repeated=False, required=True), + 2: protobuf.Field("no_fee_threshold", "uint64", repeated=False, required=True), + 3: protobuf.Field("min_registrable_amount", "uint64", repeated=False, required=True), + 4: protobuf.Field("mask_public_key", "bytes", repeated=False, required=True), + 5: protobuf.Field("signature", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + fee_rate: "int", + no_fee_threshold: "int", + min_registrable_amount: "int", + mask_public_key: "bytes", + signature: "bytes", + ) -> None: + self.fee_rate = fee_rate + self.no_fee_threshold = no_fee_threshold + self.min_registrable_amount = min_registrable_amount + self.mask_public_key = mask_public_key + self.signature = signature + + class TxRequestDetailsType(protobuf.MessageType): MESSAGE_WIRE_TYPE = None FIELDS = { @@ -1748,6 +1780,7 @@ class TxInputType(protobuf.MessageType): 17: protobuf.Field("orig_index", "uint32", repeated=False, required=False), 18: protobuf.Field("decred_staking_spend", "DecredStakingSpendType", repeated=False, required=False), 19: protobuf.Field("script_pubkey", "bytes", repeated=False, required=False), + 20: protobuf.Field("coinjoin_flags", "uint32", repeated=False, required=False), } def __init__( @@ -1769,6 +1802,7 @@ class TxInputType(protobuf.MessageType): orig_index: Optional["int"] = None, decred_staking_spend: Optional["DecredStakingSpendType"] = None, script_pubkey: Optional["bytes"] = None, + coinjoin_flags: Optional["int"] = 0, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.prev_hash = prev_hash @@ -1786,6 +1820,7 @@ class TxInputType(protobuf.MessageType): self.orig_index = orig_index self.decred_staking_spend = decred_staking_spend self.script_pubkey = script_pubkey + self.coinjoin_flags = coinjoin_flags class TxOutputBinType(protobuf.MessageType):