From b03d93c1822879ebcb1082ab646353fcfa15b00f Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 19:47:54 -0600 Subject: [PATCH 01/15] pb2py - add support for INT64 --- tools/pb2py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pb2py b/tools/pb2py index 5188ec078c..587c8065a5 100755 --- a/tools/pb2py +++ b/tools/pb2py @@ -68,6 +68,7 @@ def process_message(descriptor, protobuf_module, msg_id, indexfile, is_upy): types = { field.TYPE_UINT64: 'p.UVarintType', + field.TYPE_INT64: 'p.UVarintType', field.TYPE_UINT32: 'p.UVarintType', field.TYPE_ENUM: 'p.UVarintType', field.TYPE_SINT32: 'p.Sint32Type', From 0902aefd624b37a5278a862595aa66d18ed4a566 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 19:49:56 -0600 Subject: [PATCH 02/15] update generated protobuf files with Stellar changes --- trezorlib/messages/ApplySettings.py | 1 - trezorlib/messages/DebugLinkDecision.py | 4 +--- trezorlib/messages/DebugLinkState.py | 1 - trezorlib/messages/Features.py | 1 - trezorlib/messages/MessageType.py | 19 +++++++++++++++++ trezorlib/messages/StellarAccountMergeOp.py | 10 +++++++++ trezorlib/messages/StellarAllowTrustOp.py | 13 ++++++++++++ trezorlib/messages/StellarAssetType.py | 10 +++++++++ trezorlib/messages/StellarBumpSequenceOp.py | 10 +++++++++ trezorlib/messages/StellarChangeTrustOp.py | 12 +++++++++++ trezorlib/messages/StellarCreateAccountOp.py | 11 ++++++++++ .../messages/StellarCreatePassiveOfferOp.py | 15 +++++++++++++ trezorlib/messages/StellarGetPublicKey.py | 9 ++++++++ trezorlib/messages/StellarManageDataOp.py | 11 ++++++++++ trezorlib/messages/StellarManageOfferOp.py | 16 ++++++++++++++ trezorlib/messages/StellarMessageSignature.py | 10 +++++++++ trezorlib/messages/StellarPathPaymentOp.py | 16 ++++++++++++++ trezorlib/messages/StellarPaymentOp.py | 13 ++++++++++++ trezorlib/messages/StellarPublicKey.py | 9 ++++++++ trezorlib/messages/StellarSetOptionsOp.py | 20 ++++++++++++++++++ trezorlib/messages/StellarSignMessage.py | 10 +++++++++ trezorlib/messages/StellarSignTx.py | 21 +++++++++++++++++++ trezorlib/messages/StellarSignedTx.py | 10 +++++++++ trezorlib/messages/StellarTxOpRequest.py | 6 ++++++ trezorlib/messages/StellarVerifyMessage.py | 11 ++++++++++ trezorlib/messages/__init__.py | 21 ++++++++++++++++++- 26 files changed, 283 insertions(+), 7 deletions(-) create mode 100644 trezorlib/messages/StellarAccountMergeOp.py create mode 100644 trezorlib/messages/StellarAllowTrustOp.py create mode 100644 trezorlib/messages/StellarAssetType.py create mode 100644 trezorlib/messages/StellarBumpSequenceOp.py create mode 100644 trezorlib/messages/StellarChangeTrustOp.py create mode 100644 trezorlib/messages/StellarCreateAccountOp.py create mode 100644 trezorlib/messages/StellarCreatePassiveOfferOp.py create mode 100644 trezorlib/messages/StellarGetPublicKey.py create mode 100644 trezorlib/messages/StellarManageDataOp.py create mode 100644 trezorlib/messages/StellarManageOfferOp.py create mode 100644 trezorlib/messages/StellarMessageSignature.py create mode 100644 trezorlib/messages/StellarPathPaymentOp.py create mode 100644 trezorlib/messages/StellarPaymentOp.py create mode 100644 trezorlib/messages/StellarPublicKey.py create mode 100644 trezorlib/messages/StellarSetOptionsOp.py create mode 100644 trezorlib/messages/StellarSignMessage.py create mode 100644 trezorlib/messages/StellarSignTx.py create mode 100644 trezorlib/messages/StellarSignedTx.py create mode 100644 trezorlib/messages/StellarTxOpRequest.py create mode 100644 trezorlib/messages/StellarVerifyMessage.py diff --git a/trezorlib/messages/ApplySettings.py b/trezorlib/messages/ApplySettings.py index 3d836642a7..32f8295414 100644 --- a/trezorlib/messages/ApplySettings.py +++ b/trezorlib/messages/ApplySettings.py @@ -8,6 +8,5 @@ class ApplySettings(p.MessageType): 2: ('label', p.UnicodeType, 0), 3: ('use_passphrase', p.BoolType, 0), 4: ('homescreen', p.BytesType, 0), - 5: ('passphrase_source', p.UVarintType, 0), } MESSAGE_WIRE_TYPE = 25 diff --git a/trezorlib/messages/DebugLinkDecision.py b/trezorlib/messages/DebugLinkDecision.py index e5016f304c..482d886c6a 100644 --- a/trezorlib/messages/DebugLinkDecision.py +++ b/trezorlib/messages/DebugLinkDecision.py @@ -4,8 +4,6 @@ from .. import protobuf as p class DebugLinkDecision(p.MessageType): FIELDS = { - 1: ('yes_no', p.BoolType, 0), - 2: ('up_down', p.BoolType, 0), - 3: ('input', p.UnicodeType, 0), + 1: ('yes_no', p.BoolType, 0), # required } MESSAGE_WIRE_TYPE = 100 diff --git a/trezorlib/messages/DebugLinkState.py b/trezorlib/messages/DebugLinkState.py index ef671619ed..9438fe8c3a 100644 --- a/trezorlib/messages/DebugLinkState.py +++ b/trezorlib/messages/DebugLinkState.py @@ -15,6 +15,5 @@ class DebugLinkState(p.MessageType): 8: ('reset_entropy', p.BytesType, 0), 9: ('recovery_fake_word', p.UnicodeType, 0), 10: ('recovery_word_pos', p.UVarintType, 0), - 11: ('reset_word_pos', p.UVarintType, 0), } MESSAGE_WIRE_TYPE = 102 diff --git a/trezorlib/messages/Features.py b/trezorlib/messages/Features.py index d1eaaa39a9..878266dec7 100644 --- a/trezorlib/messages/Features.py +++ b/trezorlib/messages/Features.py @@ -31,6 +31,5 @@ class Features(p.MessageType): 24: ('fw_patch', p.UVarintType, 0), 25: ('fw_vendor', p.UnicodeType, 0), 26: ('fw_vendor_keys', p.BytesType, 0), - 27: ('unfinished_backup', p.BoolType, 0), } MESSAGE_WIRE_TYPE = 17 diff --git a/trezorlib/messages/MessageType.py b/trezorlib/messages/MessageType.py index 8409048cdc..c4a2c1ad3d 100644 --- a/trezorlib/messages/MessageType.py +++ b/trezorlib/messages/MessageType.py @@ -84,3 +84,22 @@ DebugLinkMemoryRead = 110 DebugLinkMemory = 111 DebugLinkMemoryWrite = 112 DebugLinkFlashErase = 113 +StellarGetPublicKey = 200 +StellarPublicKey = 201 +StellarSignTx = 202 +StellarTxOpRequest = 203 +StellarSignMessage = 204 +StellarMessageSignature = 205 +StellarVerifyMessage = 206 +StellarCreateAccountOp = 210 +StellarPaymentOp = 211 +StellarPathPaymentOp = 212 +StellarManageOfferOp = 213 +StellarCreatePassiveOfferOp = 214 +StellarSetOptionsOp = 215 +StellarChangeTrustOp = 216 +StellarAllowTrustOp = 217 +StellarAccountMergeOp = 218 +StellarManageDataOp = 220 +StellarBumpSequenceOp = 221 +StellarSignedTx = 230 diff --git a/trezorlib/messages/StellarAccountMergeOp.py b/trezorlib/messages/StellarAccountMergeOp.py new file mode 100644 index 0000000000..83732ae9b9 --- /dev/null +++ b/trezorlib/messages/StellarAccountMergeOp.py @@ -0,0 +1,10 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarAccountMergeOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('destination_account', p.BytesType, 0), + } + MESSAGE_WIRE_TYPE = 218 diff --git a/trezorlib/messages/StellarAllowTrustOp.py b/trezorlib/messages/StellarAllowTrustOp.py new file mode 100644 index 0000000000..2232c3ec1c --- /dev/null +++ b/trezorlib/messages/StellarAllowTrustOp.py @@ -0,0 +1,13 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarAllowTrustOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('trusted_account', p.BytesType, 0), + 3: ('asset_type', p.UVarintType, 0), + 4: ('asset_code', p.UnicodeType, 0), + 5: ('is_authorized', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 217 diff --git a/trezorlib/messages/StellarAssetType.py b/trezorlib/messages/StellarAssetType.py new file mode 100644 index 0000000000..c997a7d674 --- /dev/null +++ b/trezorlib/messages/StellarAssetType.py @@ -0,0 +1,10 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarAssetType(p.MessageType): + FIELDS = { + 1: ('type', p.UVarintType, 0), + 2: ('code', p.UnicodeType, 0), + 3: ('issuer', p.BytesType, 0), + } diff --git a/trezorlib/messages/StellarBumpSequenceOp.py b/trezorlib/messages/StellarBumpSequenceOp.py new file mode 100644 index 0000000000..ba82bd6c14 --- /dev/null +++ b/trezorlib/messages/StellarBumpSequenceOp.py @@ -0,0 +1,10 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarBumpSequenceOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('bump_to', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 221 diff --git a/trezorlib/messages/StellarChangeTrustOp.py b/trezorlib/messages/StellarChangeTrustOp.py new file mode 100644 index 0000000000..86884830ec --- /dev/null +++ b/trezorlib/messages/StellarChangeTrustOp.py @@ -0,0 +1,12 @@ +# Automatically generated by pb2py +from .. import protobuf as p +from .StellarAssetType import StellarAssetType + + +class StellarChangeTrustOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('asset', StellarAssetType, 0), + 3: ('limit', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 216 diff --git a/trezorlib/messages/StellarCreateAccountOp.py b/trezorlib/messages/StellarCreateAccountOp.py new file mode 100644 index 0000000000..c650c9c5be --- /dev/null +++ b/trezorlib/messages/StellarCreateAccountOp.py @@ -0,0 +1,11 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarCreateAccountOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('new_account', p.BytesType, 0), + 3: ('starting_balance', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 210 diff --git a/trezorlib/messages/StellarCreatePassiveOfferOp.py b/trezorlib/messages/StellarCreatePassiveOfferOp.py new file mode 100644 index 0000000000..c3ee4865cf --- /dev/null +++ b/trezorlib/messages/StellarCreatePassiveOfferOp.py @@ -0,0 +1,15 @@ +# Automatically generated by pb2py +from .. import protobuf as p +from .StellarAssetType import StellarAssetType + + +class StellarCreatePassiveOfferOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('selling_asset', StellarAssetType, 0), + 3: ('buying_asset', StellarAssetType, 0), + 4: ('amount', p.UVarintType, 0), + 5: ('price_n', p.UVarintType, 0), + 6: ('price_d', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 214 diff --git a/trezorlib/messages/StellarGetPublicKey.py b/trezorlib/messages/StellarGetPublicKey.py new file mode 100644 index 0000000000..2d2272aae2 --- /dev/null +++ b/trezorlib/messages/StellarGetPublicKey.py @@ -0,0 +1,9 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarGetPublicKey(p.MessageType): + FIELDS = { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + } + MESSAGE_WIRE_TYPE = 200 diff --git a/trezorlib/messages/StellarManageDataOp.py b/trezorlib/messages/StellarManageDataOp.py new file mode 100644 index 0000000000..140019354b --- /dev/null +++ b/trezorlib/messages/StellarManageDataOp.py @@ -0,0 +1,11 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarManageDataOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('key', p.UnicodeType, 0), + 3: ('value', p.BytesType, 0), + } + MESSAGE_WIRE_TYPE = 220 diff --git a/trezorlib/messages/StellarManageOfferOp.py b/trezorlib/messages/StellarManageOfferOp.py new file mode 100644 index 0000000000..858d92f639 --- /dev/null +++ b/trezorlib/messages/StellarManageOfferOp.py @@ -0,0 +1,16 @@ +# Automatically generated by pb2py +from .. import protobuf as p +from .StellarAssetType import StellarAssetType + + +class StellarManageOfferOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('selling_asset', StellarAssetType, 0), + 3: ('buying_asset', StellarAssetType, 0), + 4: ('amount', p.UVarintType, 0), + 5: ('price_n', p.UVarintType, 0), + 6: ('price_d', p.UVarintType, 0), + 7: ('offer_id', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 213 diff --git a/trezorlib/messages/StellarMessageSignature.py b/trezorlib/messages/StellarMessageSignature.py new file mode 100644 index 0000000000..430ea9449c --- /dev/null +++ b/trezorlib/messages/StellarMessageSignature.py @@ -0,0 +1,10 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarMessageSignature(p.MessageType): + FIELDS = { + 1: ('public_key', p.BytesType, 0), + 2: ('signature', p.BytesType, 0), + } + MESSAGE_WIRE_TYPE = 205 diff --git a/trezorlib/messages/StellarPathPaymentOp.py b/trezorlib/messages/StellarPathPaymentOp.py new file mode 100644 index 0000000000..4381ecce17 --- /dev/null +++ b/trezorlib/messages/StellarPathPaymentOp.py @@ -0,0 +1,16 @@ +# Automatically generated by pb2py +from .. import protobuf as p +from .StellarAssetType import StellarAssetType + + +class StellarPathPaymentOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('send_asset', StellarAssetType, 0), + 3: ('send_max', p.UVarintType, 0), + 4: ('destination_account', p.BytesType, 0), + 5: ('destination_asset', StellarAssetType, 0), + 6: ('destination_amount', p.UVarintType, 0), + 7: ('paths', StellarAssetType, p.FLAG_REPEATED), + } + MESSAGE_WIRE_TYPE = 212 diff --git a/trezorlib/messages/StellarPaymentOp.py b/trezorlib/messages/StellarPaymentOp.py new file mode 100644 index 0000000000..d4706dcd1f --- /dev/null +++ b/trezorlib/messages/StellarPaymentOp.py @@ -0,0 +1,13 @@ +# Automatically generated by pb2py +from .. import protobuf as p +from .StellarAssetType import StellarAssetType + + +class StellarPaymentOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('destination_account', p.BytesType, 0), + 3: ('asset', StellarAssetType, 0), + 4: ('amount', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 211 diff --git a/trezorlib/messages/StellarPublicKey.py b/trezorlib/messages/StellarPublicKey.py new file mode 100644 index 0000000000..1229d25f4f --- /dev/null +++ b/trezorlib/messages/StellarPublicKey.py @@ -0,0 +1,9 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarPublicKey(p.MessageType): + FIELDS = { + 1: ('public_key', p.BytesType, 0), + } + MESSAGE_WIRE_TYPE = 201 diff --git a/trezorlib/messages/StellarSetOptionsOp.py b/trezorlib/messages/StellarSetOptionsOp.py new file mode 100644 index 0000000000..53d3c2ff88 --- /dev/null +++ b/trezorlib/messages/StellarSetOptionsOp.py @@ -0,0 +1,20 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarSetOptionsOp(p.MessageType): + FIELDS = { + 1: ('source_account', p.BytesType, 0), + 2: ('inflation_destination_account', p.BytesType, 0), + 3: ('clear_flags', p.UVarintType, 0), + 4: ('set_flags', p.UVarintType, 0), + 5: ('master_weight', p.UVarintType, 0), + 6: ('low_threshold', p.UVarintType, 0), + 7: ('medium_threshold', p.UVarintType, 0), + 8: ('high_threshold', p.UVarintType, 0), + 9: ('home_domain', p.UnicodeType, 0), + 10: ('signer_type', p.UVarintType, 0), + 11: ('signer_key', p.BytesType, 0), + 12: ('signer_weight', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 215 diff --git a/trezorlib/messages/StellarSignMessage.py b/trezorlib/messages/StellarSignMessage.py new file mode 100644 index 0000000000..5d51be39de --- /dev/null +++ b/trezorlib/messages/StellarSignMessage.py @@ -0,0 +1,10 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarSignMessage(p.MessageType): + FIELDS = { + 1: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 2: ('message', p.UnicodeType, 0), + } + MESSAGE_WIRE_TYPE = 204 diff --git a/trezorlib/messages/StellarSignTx.py b/trezorlib/messages/StellarSignTx.py new file mode 100644 index 0000000000..a78649d45b --- /dev/null +++ b/trezorlib/messages/StellarSignTx.py @@ -0,0 +1,21 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarSignTx(p.MessageType): + FIELDS = { + 1: ('protocol_version', p.UVarintType, 0), + 2: ('address_n', p.UVarintType, p.FLAG_REPEATED), + 3: ('network_passphrase', p.UnicodeType, 0), + 4: ('source_account', p.BytesType, 0), + 5: ('fee', p.UVarintType, 0), + 6: ('sequence_number', p.UVarintType, 0), + 8: ('timebounds_start', p.UVarintType, 0), + 9: ('timebounds_end', p.UVarintType, 0), + 10: ('memo_type', p.UVarintType, 0), + 11: ('memo_text', p.UnicodeType, 0), + 12: ('memo_id', p.UVarintType, 0), + 13: ('memo_hash', p.BytesType, 0), + 14: ('num_operations', p.UVarintType, 0), + } + MESSAGE_WIRE_TYPE = 202 diff --git a/trezorlib/messages/StellarSignedTx.py b/trezorlib/messages/StellarSignedTx.py new file mode 100644 index 0000000000..9ae7a314c0 --- /dev/null +++ b/trezorlib/messages/StellarSignedTx.py @@ -0,0 +1,10 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarSignedTx(p.MessageType): + FIELDS = { + 1: ('public_key', p.BytesType, 0), + 2: ('signature', p.BytesType, 0), + } + MESSAGE_WIRE_TYPE = 230 diff --git a/trezorlib/messages/StellarTxOpRequest.py b/trezorlib/messages/StellarTxOpRequest.py new file mode 100644 index 0000000000..8b93fba824 --- /dev/null +++ b/trezorlib/messages/StellarTxOpRequest.py @@ -0,0 +1,6 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarTxOpRequest(p.MessageType): + MESSAGE_WIRE_TYPE = 203 diff --git a/trezorlib/messages/StellarVerifyMessage.py b/trezorlib/messages/StellarVerifyMessage.py new file mode 100644 index 0000000000..967bdea48b --- /dev/null +++ b/trezorlib/messages/StellarVerifyMessage.py @@ -0,0 +1,11 @@ +# Automatically generated by pb2py +from .. import protobuf as p + + +class StellarVerifyMessage(p.MessageType): + FIELDS = { + 1: ('public_key', p.BytesType, 0), + 2: ('message', p.BytesType, 0), + 3: ('signature', p.BytesType, 0), + } + MESSAGE_WIRE_TYPE = 206 diff --git a/trezorlib/messages/__init__.py b/trezorlib/messages/__init__.py index 54b9b5174f..07dc3fc858 100644 --- a/trezorlib/messages/__init__.py +++ b/trezorlib/messages/__init__.py @@ -15,6 +15,7 @@ from .NEMMosaicSupplyChange import NEMMosaicSupplyChange from .NEMProvisionNamespace import NEMProvisionNamespace from .NEMTransactionCommon import NEMTransactionCommon from .NEMTransfer import NEMTransfer +from .StellarAssetType import StellarAssetType from .TransactionType import TransactionType from .TxInputType import TxInputType from .TxOutputBinType import TxOutputBinType @@ -29,7 +30,6 @@ from . import ButtonRequestType from . import PinMatrixRequestType from . import RecoveryDeviceType from . import WordRequestType -from . import PassphraseSourceType from . import NEMMosaicLevy from . import NEMSupplyChangeType from . import NEMModificationType @@ -111,6 +111,25 @@ from .SignMessage import SignMessage from .SignTx import SignTx from .SignedIdentity import SignedIdentity from .SimpleSignTx import SimpleSignTx +from .StellarAccountMergeOp import StellarAccountMergeOp +from .StellarAllowTrustOp import StellarAllowTrustOp +from .StellarBumpSequenceOp import StellarBumpSequenceOp +from .StellarChangeTrustOp import StellarChangeTrustOp +from .StellarCreateAccountOp import StellarCreateAccountOp +from .StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp +from .StellarGetPublicKey import StellarGetPublicKey +from .StellarManageDataOp import StellarManageDataOp +from .StellarManageOfferOp import StellarManageOfferOp +from .StellarMessageSignature import StellarMessageSignature +from .StellarPathPaymentOp import StellarPathPaymentOp +from .StellarPaymentOp import StellarPaymentOp +from .StellarPublicKey import StellarPublicKey +from .StellarSetOptionsOp import StellarSetOptionsOp +from .StellarSignMessage import StellarSignMessage +from .StellarSignTx import StellarSignTx +from .StellarSignedTx import StellarSignedTx +from .StellarTxOpRequest import StellarTxOpRequest +from .StellarVerifyMessage import StellarVerifyMessage from .Success import Success from .TxAck import TxAck from .TxRequest import TxRequest From 1d722f838a9cd758801098e45c6f983d02f31d87 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 19:50:22 -0600 Subject: [PATCH 03/15] add Stellar support --- trezorctl | 60 +++++++++ trezorlib/client.py | 167 +++++++++++++++++++++++++ trezorlib/stellar.py | 290 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 517 insertions(+) create mode 100644 trezorlib/stellar.py diff --git a/trezorctl b/trezorctl index 115993eea1..bb24a7fe22 100755 --- a/trezorctl +++ b/trezorctl @@ -27,6 +27,7 @@ import functools import json import os import sys +import trezorlib.stellar as stellar from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException, format_protobuf from trezorlib.transport import get_transport, enumerate_devices, TransportException @@ -865,6 +866,65 @@ def cosi_sign(connect, address, data, global_commitment, global_pubkey): return client.cosi_sign(address_n, binascii.unhexlify(data), binascii.unhexlify(global_commitment), binascii.unhexlify(global_pubkey)) +# +# Stellar functions +# +@cli.command(help='Get Stellar public address') +@click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") +@click.pass_obj +def stellar_get_address(connect, address): + client = connect() + address_n = stellar.expand_path_or_default(client, address) + # StellarPublicKey response + response = client.stellar_get_public_key(address_n) + return stellar.address_from_public_key(response.public_key) + +@cli.command(help='Sign a string with a Stellar key') +@click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") +@click.argument('message') +@click.pass_obj +def stellar_sign_message(connect, address, message): + client = connect() + address_n = stellar.expand_path_or_default(client, address) + response = client.stellar_sign_message(address_n, message) + return base64.b64encode(response.signature) + +@cli.command(help='Verify that a signature is valid') +@click.option('--message-is-b64/--no-message-is-b64', default=False, required=False, help="If set, the message argument will be interpreted as a base64-encoded string") +@click.argument('address') +@click.argument('signatureb64') +@click.argument('message') +@click.pass_obj +def stellar_verify_message(connect, address, message_is_b64, signatureb64, message): + if message_is_b64: + message = base64.b64decode(message) + else: + message = message.encode('utf-8') + + pubkey_bytes = stellar.address_to_public_key(address) + + client = connect() + is_verified = client.stellar_verify_message(pubkey_bytes, base64.b64decode(signatureb64), message) + + if is_verified: + return "Success: message verified" + else: + print("ERROR: invalid signature, verification failed") + sys.exit(1) + +@cli.command(help='Sign a base64-encoded transaction envelope') +@click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") +@click.option('-n', '--network-passphrase', required=False, help="Network passphrase (blank for public network). Testnet is: 'Test SDF Network ; September 2015'") +@click.argument('b64envelope') +@click.pass_obj +def stellar_sign_transaction(connect, b64envelope, address, network_passphrase): + client = connect() + address_n = stellar.expand_path_or_default(client, address) + # raw signature bytes + resp = client.stellar_sign_transaction(base64.b64decode(b64envelope), address_n, network_passphrase) + + return base64.b64encode(resp.signature) + # # Main # diff --git a/trezorlib/client.py b/trezorlib/client.py index 68e259ac89..66263924ed 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -37,6 +37,7 @@ from . import nem from .coins import coins_slip44 from .debuglink import DebugLink from .protobuf import MessageType +from . import stellar as stellar if sys.version_info.major < 3: @@ -1100,6 +1101,172 @@ class ProtocolMixin(object): return self.call(proto.SelfTest(payload=b'\x00\xFF\x55\xAA\x66\x99\x33\xCCABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\x00\xFF\x55\xAA\x66\x99\x33\xCC')) + @expect(proto.StellarPublicKey) + def stellar_get_public_key(self, address_n): + return self.call(proto.StellarGetPublicKey(address_n=address_n)) + + def stellar_sign_transaction(self, txEnvelope, address_n, networkPassphrase=None): + # default networkPassphrase to the public network + if networkPassphrase is None: + networkPassphrase = "Public Global Stellar Network ; September 2015" + + parsed = stellar.parse_transaction_bytes(txEnvelope) + + # Will return a StellarTxOpRequest + resp = self.call(proto.StellarSignTx( + protocol_version=parsed["protocol_version"], + address_n=address_n, + network_passphrase=networkPassphrase, + source_account=parsed["source_account"], + fee=parsed["fee"], + sequence_number=parsed["sequence_number"], + timebounds_start=parsed["timebounds_start"], + timebounds_end=parsed["timebounds_end"], + memo_type=parsed["memo_type"], + memo_text=parsed["memo_text"], + memo_id=parsed["memo_id"], + memo_hash=parsed["memo_hash"], + num_operations=parsed["num_operations"] + )) + if resp.__class__.__name__ != "StellarTxOpRequest": + raise CallException("Unexpected response to transaction") + + for opIdx in range(0, parsed["num_operations"]): + op = parsed["operations"][opIdx] + resp = None + + # Create account + if op["type"] == 0: + resp = self.call(proto.StellarCreateAccountOp( + source_account=op["source_account"], + new_account=op["new_account"], + starting_balance=op["starting_balance"] + )) + # Payment + if op["type"] == 1: + asset = types.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], + issuer=op["asset"]["issuer"]) + resp = self.call(proto.StellarPaymentOp( + source_account=op["source_account"], + destination_account=op["destination_account"], + amount=op["amount"], + asset=asset + )) + # Path Payment + if op["type"] == 2: + destination_asset = types.StellarAssetType(type=op["destination_asset"]["type"], + code=op["destination_asset"]["code"], + issuer=op["destination_asset"]["issuer"]) + resp = self.call(proto.StellarPathPaymentOp( + source_account=op["source_account"], + send_max=op["send_max"], + destination_account=op["destination_account"], + destination_asset=destination_asset, + destination_amount=op["destination_amount"], + paths=op["paths"] + )) + # Manage Offer + if op["type"] == 3: + selling_asset = types.StellarAssetType(type=op["selling_asset"]["type"], + code=op["selling_asset"]["code"], + issuer=op["selling_asset"]["issuer"]) + buying_asset = types.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], + issuer=op["buying_asset"]["issuer"]) + resp = self.call(proto.StellarManageOfferOp( + source_account=op["source_account"], + selling_asset=selling_asset, + buying_asset=buying_asset, + amount=op["amount"], + price_n=op["price_n"], + price_d=op["price_d"], + offer_id=op["offer_id"] + )) + # Passive Offer + if op["type"] == 4: + selling_asset = types.StellarAssetType(type=op["selling_asset"]["type"], + code=op["selling_asset"]["code"], + issuer=op["selling_asset"]["issuer"]) + buying_asset = types.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], + issuer=op["buying_asset"]["issuer"]) + resp = self.call(proto.StellarCreatePassiveOfferOp( + source_account=op["source_account"], + selling_asset=selling_asset, + buying_asset=buying_asset, + amount=op["amount"], + price_n=op["price_n"], + price_d=op["price_d"] + )) + # Set Options + if op["type"] == 5: + resp = self.call(proto.StellarSetOptionsOp( + source_account=op["source_account"], + inflation_destination_account=op["inflation_destination"], + clear_flags=op["clear_flags"], + set_flags=op["set_flags"], + master_weight=op["master_weight"], + low_threshold=op["low_threshold"], + medium_threshold=op["medium_threshold"], + high_threshold=op["high_threshold"], + home_domain=op["home_domain"], + signer_type=op["signer_type"], + signer_key=op["signer_key"], + signer_weight=op["signer_weight"], + )) + # Change Trust + if op["type"] == 6: + asset = types.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], + issuer=op["asset"]["issuer"]) + resp = self.call(proto.StellarChangeTrustOp( + source_account=op["source_account"], + limit=op["limit"], + asset=asset + )) + # Allow Trust + if op["type"] == 7: + resp = self.call(proto.StellarAllowTrustOp( + source_account=op["source_account"], + trusted_account=op["trusted_account"], + asset_type=op["asset_type"], + asset_code=op["asset_code"], + is_authorized=op["is_authorized"] + )) + # Merge Account + if op["type"] == 8: + resp = self.call(proto.StellarAccountMergeOp( + source_account=op["source_account"], + destination_account=op["destination_account"] + )) + # Manage data + if op["type"] == 10: + resp = self.call(proto.StellarManageDataOp( + source_account=op["source_account"], + key=op["key"], + value=op["value"] + )) + # Merge Account + if op["type"] == 11: + resp = self.call(proto.StellarBumpSequenceOp( + source_account=op["source_account"], + bump_to=op["bump_to"] + )) + + # Exit if the response was a StellarSignedTx + if resp.__class__.__name__ == "StellarSignedTx": + return resp + + raise CallException("Reached end of operations without a signature") + + @expect(proto.StellarMessageSignature) + def stellar_sign_message(self, address_n, message): + return self.call(proto.StellarSignMessage(address_n=address_n, message=message)) + + def stellar_verify_message(self, pubkey_bytes, signature, message): + resp = self.call(proto.StellarVerifyMessage(public_key=pubkey_bytes, message=message, signature=signature)) + + if isinstance(resp, proto.Success): + return True + return False + class TrezorClient(ProtocolMixin, TextUIMixin, BaseClient): pass diff --git a/trezorlib/stellar.py b/trezorlib/stellar.py new file mode 100644 index 0000000000..b369ff5c4b --- /dev/null +++ b/trezorlib/stellar.py @@ -0,0 +1,290 @@ +import base64 +import struct +import binascii +import xdrlib + +def expand_path_or_default(client, address): + """Uses client to parse address and returns an array of integers + If no address is specified, the default of m/44'/148'/0' is used + """ + if address: + return client.expand_path(address) + else: + return client.expand_path("m/44'/148'/0'") + + +def address_from_public_key(pk_bytes): + """Returns the base32-encoded version of pk_bytes (G...) + """ + final_bytes = bytearray() + + # version + final_bytes.append(6 << 3) + # public key + final_bytes.extend(pk_bytes) + # checksum + final_bytes.extend(struct.pack(" max_timebound or parsed["timebounds_start"] < 0: + raise ValueError("Starting timebound out of range (must be between 0 and " + max_timebound) + if parsed["timebounds_end"] > max_timebound or parsed["timebounds_end"] < 0: + raise ValueError("Ending timebound out of range (must be between 0 and " + max_timebound) + + # memo type determines what optional fields are set + parsed["memo_type"] = unpacker.unpack_uint() + parsed["memo_text"] = None + parsed["memo_id"] = None + parsed["memo_hash"] = None + + # text + if parsed["memo_type"] == 1: + parsed["memo_text"] = unpacker.unpack_string() + # id (64-bit uint) + if parsed["memo_type"] == 2: + parsed["memo_id"] = unpacker.unpack_uhyper() + # hash / return are the same structure (32 bytes representing a hash) + if parsed["memo_type"] == 3 or parsed["memo_type"] == 4: + parsed["memo+hash"] = unpacker.unpack_fopaque(32) + + parsed["num_operations"] = unpacker.unpack_uint() + + for opIdx in range(0, parsed["num_operations"]): + parsed["operations"].append(_parse_operation_bytes(unpacker)) + + return parsed + +def _parse_operation_bytes(unpacker): + """Returns a dictionary describing the next operation as read from + the byte stream in unpacker + """ + op = { + "source_account": None, + "type": None + } + + has_source_account = unpacker.unpack_bool() + if has_source_account: + op["source_account"] = unpacker.unpack_fopaque(32) + + op["type"] = unpacker.unpack_uint() + + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L16 + if op["type"] == 0: + op["new_account"] = _xdr_read_address(unpacker) + op["starting_balance"] = unpacker.unpack_hyper() + + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L54 + if op["type"] == 1: + op["destination_account"] = _xdr_read_address(unpacker) + op["asset"] = _xdr_read_asset(unpacker) + op["amount"] = unpacker.unpack_hyper() + + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L72 + if op["type"] == 2: + op["send_asset"] = _xdr_read_asset(unpacker) + op["send_max"] = unpacker.unpack_hyper() + op["destination_account"] = _xdr_read_address(unpacker) + op["destination_asset"] = _xdr_read_asset(unpacker) + op["destination_amount"] = unpacker.unpack_hyper() + op["paths"] = [] + + num_paths = unpacker.unpack_uint() + for i in range(0, num_paths): + op["paths"].append(_xdr_read_asset(unpacker)) + + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L93 + if op["type"] == 3: + op["selling_asset"] = _xdr_read_asset(unpacker) + op["buying_asset"] = _xdr_read_asset(unpacker) + op["amount"] = unpacker.unpack_hyper() + op["price_n"] = unpacker.unpack_uint() + op["price_d"] = unpacker.unpack_uint() + op["offer_id"] = unpacker.unpack_uhyper() + + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L111 + if op["type"] == 4: + op["selling_asset"] = _xdr_read_asset(unpacker) + op["buying_asset"] = _xdr_read_asset(unpacker) + op["amount"] = unpacker.unpack_hyper() + op["price_n"] = unpacker.unpack_uint() + op["price_d"] = unpacker.unpack_uint() + + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L129 + if op["type"] == 5: + op["inflation_destination"] = None + op["clear_flags"] = None + op["set_flags"] = None + op["master_weight"] = None + op["low_threshold"] = None + op["medium_threshold"] = None + op["high_threshold"] = None + op["home_domain"] = None + op["signer_type"] = None + op["signer_key"] = None + op["signer_weight"] = None + + op["has_inflation_destination"] = unpacker.unpack_bool() + if op["has_inflation_destination"]: + op["inflation_destination"] = _xdr_read_address(unpacker) + + op["has_clear_flags"] = unpacker.unpack_bool() + if op["has_clear_flags"]: + op["clear_flags"] = unpacker.unpack_uint() + + op["has_set_flags"] = unpacker.unpack_bool() + if op["has_set_flags"]: + op["set_flags"] = unpacker.unpack_uint() + + op["has_master_weight"] = unpacker.unpack_bool() + if op["has_master_weight"]: + op["master_weight"] = unpacker.unpack_uint() + + op["has_low_threshold"] = unpacker.unpack_bool() + if op["has_low_threshold"]: + op["low_threshold"] = unpacker.unpack_uint() + + op["has_medium_threshold"] = unpacker.unpack_bool() + if op["has_medium_threshold"]: + op["medium_threshold"] = unpacker.unpack_uint() + + op["has_high_threshold"] = unpacker.unpack_bool() + if op["has_high_threshold"]: + op["high_threshold"] = unpacker.unpack_uint() + + op["has_home_domain"] = unpacker.unpack_bool() + if op["has_home_domain"]: + op["home_domain"] = unpacker.unpack_string() + + op["has_signer"] = unpacker.unpack_bool() + if op["has_signer"]: + op["signer_type"] = unpacker.unpack_uint() + op["signer_key"] = unpacker.unpack_fopaque(32) + op["signer_weight"] = unpacker.unpack_uint() + + # Change Trust + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L156 + if op["type"] == 6: + op["asset"] = _xdr_read_asset(unpacker) + op["limit"] = unpacker.unpack_uhyper() + + # Allow Trust + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L173 + if op["type"] == 7: + op["trusted_account"] = _xdr_read_address(unpacker) + op["asset_type"] = unpacker.unpack_uint() + + if op["asset_type"] == 1: + op["asset_code"] = unpacker.unpack_fstring(4) + if op["asset_type"] == 2: + op["asset_code"] = unpacker.unpack_fstring(12) + + op["is_authorized"] = unpacker.unpack_bool() + + # Merge Account + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L251 + if op["type"] == 8: + op["destination_account"] = _xdr_read_address(unpacker) + + # Inflation is not implemented since any account can send this operation + + # Manage Data + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L218 + if op["type"] == 10: + op["key"] = unpacker.unpack_string() + + op["value"] = None + op["has_value"] = unpacker.unpack_bool() + if op["has_value"]: + op["value"] = unpacker.unpack_opaque() + + # Bump Sequence + # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L269 + if op["type"] == 11: + op["bump_to"] = unpacker.unpack_uhyper() + + return op + +def _xdr_read_asset(unpacker): + """Reads a stellar Asset from unpacker""" + asset = { + "type": unpacker.unpack_uint(), + "code": None, + "issuer": None + } + + # alphanum 4 + if asset["type"] == 1: + asset["code"] = unpacker.unpack_fstring(4) + asset["issuer"] = _xdr_read_address(unpacker) + + if asset["type"] == 2: + asset["code"] = unpacker.unpack_fstring(12) + asset["issuer"] = _xdr_read_address(unpacker) + + return asset + + +def _xdr_read_address(unpacker): + """Reads a stellar address and returns the 32-byte + data representing the address + """ + # First 4 bytes are the address type + address_type = unpacker.unpack_uint() + if address_type != 0: + raise ValueError("Unsupported address type") + + return unpacker.unpack_fopaque(32) + +def _crc16_checksum(bytes): + """Returns the CRC-16 checksum of bytearray bytes + + Ported from Java implementation at: http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java.html + + Initial value changed to 0x0000 to match Stellar configuration. + """ + crc = 0x0000 + polynomial = 0x1021 + + for byte in bytes: + for i in range(0, 8): + bit = ((byte >> (7 - i) & 1) == 1) + c15 = ((crc >> 15 & 1) == 1) + crc <<= 1 + if c15 ^ bit: + crc ^= polynomial + + return crc & 0xffff \ No newline at end of file From 7eef1df6b0fd648a6ed1b53bcd0d64dcb71a0755 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Tue, 3 Apr 2018 19:50:38 -0600 Subject: [PATCH 04/15] stellar - added some unit tests --- .../test_msg_stellar_get_public_key.py | 26 ++++++++++ .../test_msg_stellar_sign_transaction.py | 48 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py create mode 100644 trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py diff --git a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py new file mode 100644 index 0000000000..b3f3a012cb --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py @@ -0,0 +1,26 @@ +# This file is part of the TREZOR project. +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +from .common import * +import trezorlib.stellar as stellar + +class TestMsgStellarGetPublicKey(TrezorTest): + + def test_stellar_get_address(self): + self.setup_mnemonic_nopin_nopassphrase() + + # GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW + response = self.client.stellar_get_public_key(self.client.expand_path("m/44'/148'/0'")) + assert stellar.address_from_public_key(response.public_key) == b'GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW' diff --git a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py new file mode 100644 index 0000000000..f3b0df1045 --- /dev/null +++ b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py @@ -0,0 +1,48 @@ +# This file is part of the TREZOR project. +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . +# +# XDR decoding tool available at: +# https://www.stellar.org/laboratory/#xdr-viewer +# + +import base64 +from .common import * + +class TestMsgStellarSignTransaction(TrezorTest): + + def get_network_passphrase(self): + """Use the same passphrase as the network that generated the test XDR/signatures""" + return "Integration Test Network ; zulucrypto" + + def get_address_n(self): + """BIP32 path of the default account""" + return self.client.expand_path("m/44'/148'/0'") + + def test_sign_tx_bump_sequence_op(self): + self.setup_mnemonic_nopin_nopassphrase() + + xdr = base64.b64decode("AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAt//////////wAAAAAAAAAA") + + response = self.client.stellar_sign_transaction(xdr, self.get_address_n(), self.get_network_passphrase()) + assert base64.b64encode(response.signature) == b'UAOL4ZPYIOzEgM66kBrhyNjLR66dNXtuNrmvd3m0/pc8qCSoLmYY4TybS0lHiMtb+LFZESTaxrpErMHz1sZ6DQ==' + + + def test_sign_tx_account_merge_op(self): + self.setup_mnemonic_nopin_nopassphrase() + + xdr = base64.b64decode("AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAgAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAAAAAAAQHNtGkAAABAgjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==") + + response = self.client.stellar_sign_transaction(xdr, self.get_address_n(), self.get_network_passphrase()) + assert base64.b64encode(response.signature) == b'gjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==' From 76241dca5c4610c200d5b98967702f0895a88711 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Thu, 19 Apr 2018 13:28:31 -0600 Subject: [PATCH 05/15] trezorctl - clean up imports --- trezorctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trezorctl b/trezorctl index bb24a7fe22..d089db7860 100755 --- a/trezorctl +++ b/trezorctl @@ -27,7 +27,6 @@ import functools import json import os import sys -import trezorlib.stellar as stellar from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException, format_protobuf from trezorlib.transport import get_transport, enumerate_devices, TransportException @@ -35,6 +34,7 @@ from trezorlib import messages as proto from trezorlib import protobuf from trezorlib.coins import coins_txapi from trezorlib.ckd_public import PRIME_DERIVATION_FLAG +from trezorlib import stellar class ChoiceType(click.Choice): From 38647de7f94cd14ed0d87117052ec4be484536b1 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Thu, 19 Apr 2018 13:29:36 -0600 Subject: [PATCH 06/15] client.py - minor style fixes and fix for use of "types.Message" instead of "proto.Message" --- trezorlib/client.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index 66263924ed..f948879d7d 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -34,10 +34,10 @@ from . import messages as proto from . import tools from . import mapping from . import nem +from . import stellar from .coins import coins_slip44 from .debuglink import DebugLink from .protobuf import MessageType -from . import stellar as stellar if sys.version_info.major < 3: @@ -1105,18 +1105,18 @@ class ProtocolMixin(object): def stellar_get_public_key(self, address_n): return self.call(proto.StellarGetPublicKey(address_n=address_n)) - def stellar_sign_transaction(self, txEnvelope, address_n, networkPassphrase=None): + def stellar_sign_transaction(self, tx_envelope, address_n, network_passphrase=None): # default networkPassphrase to the public network - if networkPassphrase is None: - networkPassphrase = "Public Global Stellar Network ; September 2015" + if network_passphrase is None: + network_passphrase = "Public Global Stellar Network ; September 2015" - parsed = stellar.parse_transaction_bytes(txEnvelope) + parsed = stellar.parse_transaction_bytes(tx_envelope) # Will return a StellarTxOpRequest resp = self.call(proto.StellarSignTx( protocol_version=parsed["protocol_version"], address_n=address_n, - network_passphrase=networkPassphrase, + network_passphrase=network_passphrase, source_account=parsed["source_account"], fee=parsed["fee"], sequence_number=parsed["sequence_number"], @@ -1128,11 +1128,11 @@ class ProtocolMixin(object): memo_hash=parsed["memo_hash"], num_operations=parsed["num_operations"] )) - if resp.__class__.__name__ != "StellarTxOpRequest": + if not isinstance(resp, proto.StellarTxOpRequest): raise CallException("Unexpected response to transaction") - for opIdx in range(0, parsed["num_operations"]): - op = parsed["operations"][opIdx] + for op_idx in range(0, parsed["num_operations"]): + op = parsed["operations"][op_idx] resp = None # Create account @@ -1144,7 +1144,7 @@ class ProtocolMixin(object): )) # Payment if op["type"] == 1: - asset = types.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], + asset = proto.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], issuer=op["asset"]["issuer"]) resp = self.call(proto.StellarPaymentOp( source_account=op["source_account"], @@ -1154,7 +1154,7 @@ class ProtocolMixin(object): )) # Path Payment if op["type"] == 2: - destination_asset = types.StellarAssetType(type=op["destination_asset"]["type"], + destination_asset = proto.StellarAssetType(type=op["destination_asset"]["type"], code=op["destination_asset"]["code"], issuer=op["destination_asset"]["issuer"]) resp = self.call(proto.StellarPathPaymentOp( @@ -1167,10 +1167,10 @@ class ProtocolMixin(object): )) # Manage Offer if op["type"] == 3: - selling_asset = types.StellarAssetType(type=op["selling_asset"]["type"], + selling_asset = proto.StellarAssetType(type=op["selling_asset"]["type"], code=op["selling_asset"]["code"], issuer=op["selling_asset"]["issuer"]) - buying_asset = types.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], + buying_asset = proto.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], issuer=op["buying_asset"]["issuer"]) resp = self.call(proto.StellarManageOfferOp( source_account=op["source_account"], @@ -1183,10 +1183,10 @@ class ProtocolMixin(object): )) # Passive Offer if op["type"] == 4: - selling_asset = types.StellarAssetType(type=op["selling_asset"]["type"], + selling_asset = proto.StellarAssetType(type=op["selling_asset"]["type"], code=op["selling_asset"]["code"], issuer=op["selling_asset"]["issuer"]) - buying_asset = types.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], + buying_asset = proto.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], issuer=op["buying_asset"]["issuer"]) resp = self.call(proto.StellarCreatePassiveOfferOp( source_account=op["source_account"], @@ -1214,7 +1214,7 @@ class ProtocolMixin(object): )) # Change Trust if op["type"] == 6: - asset = types.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], + asset = proto.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], issuer=op["asset"]["issuer"]) resp = self.call(proto.StellarChangeTrustOp( source_account=op["source_account"], @@ -1251,7 +1251,7 @@ class ProtocolMixin(object): )) # Exit if the response was a StellarSignedTx - if resp.__class__.__name__ == "StellarSignedTx": + if isinstance(resp, proto.StellarSignedTx): return resp raise CallException("Reached end of operations without a signature") @@ -1263,9 +1263,7 @@ class ProtocolMixin(object): def stellar_verify_message(self, pubkey_bytes, signature, message): resp = self.call(proto.StellarVerifyMessage(public_key=pubkey_bytes, message=message, signature=signature)) - if isinstance(resp, proto.Success): - return True - return False + return isinstance(resp, proto.Success) class TrezorClient(ProtocolMixin, TextUIMixin, BaseClient): From 73b07a489bb6955969495b3aaea5db3aa3bb23cf Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Thu, 19 Apr 2018 15:10:27 -0600 Subject: [PATCH 07/15] stellar - refactor parser to return protobuf messages instead of map --- trezorlib/stellar.py | 352 ++++++++++++++++++++++++------------------- 1 file changed, 194 insertions(+), 158 deletions(-) diff --git a/trezorlib/stellar.py b/trezorlib/stellar.py index b369ff5c4b..62278cbe29 100644 --- a/trezorlib/stellar.py +++ b/trezorlib/stellar.py @@ -1,8 +1,34 @@ import base64 import struct -import binascii import xdrlib +from . import messages as proto + +# Memo types +MEMO_TYPE_TEXT = 0 +MEMO_TYPE_ID = 1 +MEMO_TYPE_HASH = 2 +MEMO_TYPE_RETURN = 4 + +# Asset types +ASSET_TYPE_NATIVE = 0 +ASSET_TYPE_ALPHA4 = 1 +ASSET_TYPE_ALPHA12 = 2 + +# Operations +OP_CREATE_ACCOUNT = 0 +OP_PAYMENT = 1 +OP_PATH_PAYMENT = 2 +OP_MANAGE_OFFER = 3 +OP_CREATE_PASSIVE_OFFER = 4 +OP_SET_OPTIONS = 5 +OP_CHANGE_TRUST = 6 +OP_ALLOW_TRUST = 7 +OP_ACCOUNT_MERGE = 8 +OP_INFLATION = 9 # Included for documentation purposes, not supported by Trezor +OP_MANAGE_DATA = 10 +OP_BUMP_SEQUENCE = 11 + def expand_path_or_default(client, address): """Uses client to parse address and returns an array of integers If no address is specified, the default of m/44'/148'/0' is used @@ -31,7 +57,6 @@ def address_to_public_key(address_str): """Returns the raw 32 bytes representing a public key by extracting it from the G... string """ - final_bytes = bytearray() decoded = base64.b32decode(address_str) # skip 0th byte (version) and last two bytes (checksum) @@ -39,221 +64,232 @@ def address_to_public_key(address_str): def parse_transaction_bytes(bytes): - """Parses base64data into a StellarSignTx message + """Parses base64data into a map with the following keys: + tx - a StellarSignTx describing the transaction header + operations - an array of protobuf message objects for each operation """ - parsed = {} - parsed["protocol_version"] = 1 - parsed["operations"] = [] + tx = proto.StellarSignTx( + protocol_version=1 + ) + operations = [] unpacker = xdrlib.Unpacker(bytes) - parsed["source_account"] = _xdr_read_address(unpacker) - parsed["fee"] = unpacker.unpack_uint() - parsed["sequence_number"] = unpacker.unpack_uhyper() + tx.source_account = _xdr_read_address(unpacker) + tx.fee = unpacker.unpack_uint() + tx.sequence_number = unpacker.unpack_uhyper() # Timebounds is an optional field - parsed["timebounds_start"] = 0 - parsed["timebounds_end"] = 0 has_timebounds = unpacker.unpack_bool() if has_timebounds: max_timebound = 2**32-1 # max unsigned 32-bit int (trezor does not support the full 64-bit time value) - parsed["timebounds_start"] = unpacker.unpack_uhyper() - parsed["timebounds_end"] = unpacker.unpack_uhyper() + tx.timebounds_start = unpacker.unpack_uhyper() + tx.timebounds_end = unpacker.unpack_uhyper() - if parsed["timebounds_start"] > max_timebound or parsed["timebounds_start"] < 0: + if tx.timebounds_start > max_timebound or tx.timebounds_start < 0: raise ValueError("Starting timebound out of range (must be between 0 and " + max_timebound) - if parsed["timebounds_end"] > max_timebound or parsed["timebounds_end"] < 0: + if tx.timebounds_end > max_timebound or tx.timebounds_end < 0: raise ValueError("Ending timebound out of range (must be between 0 and " + max_timebound) # memo type determines what optional fields are set - parsed["memo_type"] = unpacker.unpack_uint() - parsed["memo_text"] = None - parsed["memo_id"] = None - parsed["memo_hash"] = None + tx.memo_type = unpacker.unpack_uint() # text - if parsed["memo_type"] == 1: - parsed["memo_text"] = unpacker.unpack_string() + if tx.memo_type == MEMO_TYPE_HASH: + tx.memo_text = unpacker.unpack_string() # id (64-bit uint) - if parsed["memo_type"] == 2: - parsed["memo_id"] = unpacker.unpack_uhyper() + if tx.memo_type == MEMO_TYPE_ID: + tx.memo_id = unpacker.unpack_uhyper() # hash / return are the same structure (32 bytes representing a hash) - if parsed["memo_type"] == 3 or parsed["memo_type"] == 4: - parsed["memo+hash"] = unpacker.unpack_fopaque(32) + if tx.memo_type == MEMO_TYPE_HASH or tx.memo_type == MEMO_TYPE_RETURN: + tx.memo_hash = unpacker.unpack_fopaque(32) - parsed["num_operations"] = unpacker.unpack_uint() + tx.num_operations = unpacker.unpack_uint() - for opIdx in range(0, parsed["num_operations"]): - parsed["operations"].append(_parse_operation_bytes(unpacker)) + for i in range(tx.num_operations): + operations.append(_parse_operation_bytes(unpacker)) - return parsed - -def _parse_operation_bytes(unpacker): - """Returns a dictionary describing the next operation as read from - the byte stream in unpacker - """ - op = { - "source_account": None, - "type": None + return { + "tx": tx, + "operations": operations } +def _parse_operation_bytes(unpacker): + """Returns a protobuf message representing the next operation as read from + the byte stream in unpacker + """ + + # Check for and parse optional source account field + source_account = None has_source_account = unpacker.unpack_bool() if has_source_account: - op["source_account"] = unpacker.unpack_fopaque(32) + source_account = unpacker.unpack_fopaque(32) - op["type"] = unpacker.unpack_uint() + # Operation type (See OP_ constants) + type = unpacker.unpack_uint() - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L16 - if op["type"] == 0: - op["new_account"] = _xdr_read_address(unpacker) - op["starting_balance"] = unpacker.unpack_hyper() + if type == OP_CREATE_ACCOUNT: + return proto.StellarCreateAccountOp( + source_account=source_account, + new_account=_xdr_read_address(unpacker), + starting_balance=unpacker.unpack_hyper() + ) - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L54 - if op["type"] == 1: - op["destination_account"] = _xdr_read_address(unpacker) - op["asset"] = _xdr_read_asset(unpacker) - op["amount"] = unpacker.unpack_hyper() + if type == OP_PAYMENT: + return proto.StellarPaymentOp( + source_account=source_account, + destination_account=_xdr_read_address(unpacker), + asset=_xdr_read_asset(unpacker), + amount=unpacker.unpack_hyper() + ) - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L72 - if op["type"] == 2: - op["send_asset"] = _xdr_read_asset(unpacker) - op["send_max"] = unpacker.unpack_hyper() - op["destination_account"] = _xdr_read_address(unpacker) - op["destination_asset"] = _xdr_read_asset(unpacker) - op["destination_amount"] = unpacker.unpack_hyper() - op["paths"] = [] + if type == OP_PATH_PAYMENT: + op = proto.StellarPathPaymentOp( + source_account=source_account, + send_asset=_xdr_read_asset(unpacker), + send_max=unpacker.unpack_hyper(), + destination_account=_xdr_read_address(unpacker), + destination_asset=_xdr_read_asset(unpacker), + paths=[] + ) num_paths = unpacker.unpack_uint() - for i in range(0, num_paths): - op["paths"].append(_xdr_read_asset(unpacker)) + for i in range(num_paths): + op.paths.append(_xdr_read_asset(unpacker)) - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L93 - if op["type"] == 3: - op["selling_asset"] = _xdr_read_asset(unpacker) - op["buying_asset"] = _xdr_read_asset(unpacker) - op["amount"] = unpacker.unpack_hyper() - op["price_n"] = unpacker.unpack_uint() - op["price_d"] = unpacker.unpack_uint() - op["offer_id"] = unpacker.unpack_uhyper() + return op - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L111 - if op["type"] == 4: - op["selling_asset"] = _xdr_read_asset(unpacker) - op["buying_asset"] = _xdr_read_asset(unpacker) - op["amount"] = unpacker.unpack_hyper() - op["price_n"] = unpacker.unpack_uint() - op["price_d"] = unpacker.unpack_uint() + if type == OP_MANAGE_OFFER: + return proto.StellarManageOfferOp( + source_account=source_account, + selling_asset=_xdr_read_asset(unpacker), + buying_asset=_xdr_read_asset(unpacker), + amount=unpacker.unpack_hyper(), + price_n=unpacker.unpack_uint(), + price_d=unpacker.unpack_uint(), + offer_id=unpacker.unpack_uhyper() + ) - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L129 - if op["type"] == 5: - op["inflation_destination"] = None - op["clear_flags"] = None - op["set_flags"] = None - op["master_weight"] = None - op["low_threshold"] = None - op["medium_threshold"] = None - op["high_threshold"] = None - op["home_domain"] = None - op["signer_type"] = None - op["signer_key"] = None - op["signer_weight"] = None + if type == OP_CREATE_PASSIVE_OFFER: + return proto.StellarCreatePassiveOfferOp( + source_account=source_account, + selling_asset=_xdr_read_asset(unpacker), + buying_asset=_xdr_read_asset(unpacker), + amount=unpacker.unpack_hyper(), + price_n=unpacker.unpack_uint(), + price_d=unpacker.unpack_uint() + ) - op["has_inflation_destination"] = unpacker.unpack_bool() - if op["has_inflation_destination"]: - op["inflation_destination"] = _xdr_read_address(unpacker) + if type == OP_SET_OPTIONS: + op = proto.StellarSetOptionsOp( + source_account=source_account + ) - op["has_clear_flags"] = unpacker.unpack_bool() - if op["has_clear_flags"]: - op["clear_flags"] = unpacker.unpack_uint() + # Inflation destination + if unpacker.unpack_bool(): + op.inflation_destination_account = _xdr_read_address(unpacker) - op["has_set_flags"] = unpacker.unpack_bool() - if op["has_set_flags"]: - op["set_flags"] = unpacker.unpack_uint() + # clear flags + if unpacker.unpack_bool(): + op.clear_flags = unpacker.unpack_uint() - op["has_master_weight"] = unpacker.unpack_bool() - if op["has_master_weight"]: - op["master_weight"] = unpacker.unpack_uint() + # set flags + if unpacker.unpack_bool(): + op.set_flags = unpacker.unpack_uint() - op["has_low_threshold"] = unpacker.unpack_bool() - if op["has_low_threshold"]: - op["low_threshold"] = unpacker.unpack_uint() + # master weight + if unpacker.unpack_bool(): + op.master_weight = unpacker.unpack_uint() - op["has_medium_threshold"] = unpacker.unpack_bool() - if op["has_medium_threshold"]: - op["medium_threshold"] = unpacker.unpack_uint() + # low threshold + if unpacker.unpack_bool(): + op.low_threshold = unpacker.unpack_uint() - op["has_high_threshold"] = unpacker.unpack_bool() - if op["has_high_threshold"]: - op["high_threshold"] = unpacker.unpack_uint() + # medium threshold + if unpacker.unpack_bool(): + op.medium_threshold = unpacker.unpack_uint() - op["has_home_domain"] = unpacker.unpack_bool() - if op["has_home_domain"]: - op["home_domain"] = unpacker.unpack_string() + # high threshold + if unpacker.unpack_bool(): + op.high_threshold = unpacker.unpack_uint() - op["has_signer"] = unpacker.unpack_bool() - if op["has_signer"]: - op["signer_type"] = unpacker.unpack_uint() - op["signer_key"] = unpacker.unpack_fopaque(32) - op["signer_weight"] = unpacker.unpack_uint() + # home domain + if unpacker.unpack_bool(): + op.home_domain = unpacker.unpack_string() - # Change Trust - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L156 - if op["type"] == 6: - op["asset"] = _xdr_read_asset(unpacker) - op["limit"] = unpacker.unpack_uhyper() + # signer + if unpacker.unpack_bool(): + op.signer_type = unpacker.unpack_uint() + op.signer_key = unpacker.unpack_fopaque(32) + op.signer_weight = unpacker.unpack_uint() - # Allow Trust - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L173 - if op["type"] == 7: - op["trusted_account"] = _xdr_read_address(unpacker) - op["asset_type"] = unpacker.unpack_uint() + return op - if op["asset_type"] == 1: - op["asset_code"] = unpacker.unpack_fstring(4) - if op["asset_type"] == 2: - op["asset_code"] = unpacker.unpack_fstring(12) + if type == OP_CHANGE_TRUST: + return proto.StellarChangeTrustOp( + source_account=source_account, + asset=_xdr_read_asset(unpacker), + limit=unpacker.unpack_uhyper() + ) - op["is_authorized"] = unpacker.unpack_bool() + if type == OP_ALLOW_TRUST: + op = proto.StellarAllowTrustOp( + source_account=source_account, + trusted_account=_xdr_read_address(unpacker), + asset_type=unpacker.unpack_uint() + ) - # Merge Account - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L251 - if op["type"] == 8: - op["destination_account"] = _xdr_read_address(unpacker) + if op.asset_type == ASSET_TYPE_ALPHA4: + op.asset_code = unpacker.unpack_fstring(4) + if op.asset_type == ASSET_TYPE_ALPHA12: + op.asset_code = unpacker.unpack_fstring(12) - # Inflation is not implemented since any account can send this operation + op.is_authorized = unpacker.unpack_bool() - # Manage Data - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L218 - if op["type"] == 10: - op["key"] = unpacker.unpack_string() + return op - op["value"] = None - op["has_value"] = unpacker.unpack_bool() - if op["has_value"]: - op["value"] = unpacker.unpack_opaque() + if type == OP_ACCOUNT_MERGE: + return proto.StellarAccountMergeOp( + source_account=source_account, + destination_account=_xdr_read_address(unpacker) + ) + + # Inflation is not implemented since anyone can submit this operation to the network + + if type == OP_MANAGE_DATA: + op = proto.StellarManageDataOp( + source_account=source_account, + key=unpacker.unpack_string(), + ) + + # Only set value if the field is present + if unpacker.unpack_bool(): + op.value = unpacker.unpack_opaque() + + return op # Bump Sequence # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L269 - if op["type"] == 11: - op["bump_to"] = unpacker.unpack_uhyper() + if type == OP_BUMP_SEQUENCE: + return proto.StellarBumpSequenceOp( + source_account=source_account, + bump_to=unpacker.unpack_uhyper() + ) - return op + raise ValueError("Unknown operation type: " + type) def _xdr_read_asset(unpacker): """Reads a stellar Asset from unpacker""" - asset = { - "type": unpacker.unpack_uint(), - "code": None, - "issuer": None - } + asset = proto.StellarAssetType( + type=unpacker.unpack_uint() + ) - # alphanum 4 - if asset["type"] == 1: - asset["code"] = unpacker.unpack_fstring(4) - asset["issuer"] = _xdr_read_address(unpacker) + if asset.type == ASSET_TYPE_ALPHA4: + asset.code = unpacker.unpack_fstring(4) + asset.issuer = _xdr_read_address(unpacker) - if asset["type"] == 2: - asset["code"] = unpacker.unpack_fstring(12) - asset["issuer"] = _xdr_read_address(unpacker) + if asset.type == ASSET_TYPE_ALPHA12: + asset.code = unpacker.unpack_fstring(12) + asset.issuer = _xdr_read_address(unpacker) return asset From 1fb521f4cda40199cc2927b4b52bd97760e0a2be Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Thu, 19 Apr 2018 15:10:57 -0600 Subject: [PATCH 08/15] client.py updated to use Stellar messages instead of map --- trezorlib/client.py | 150 ++++---------------------------------------- 1 file changed, 13 insertions(+), 137 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index f948879d7d..a09937d055 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -1112,149 +1112,25 @@ class ProtocolMixin(object): parsed = stellar.parse_transaction_bytes(tx_envelope) + tx = parsed["tx"] + tx.network_passphrase = network_passphrase + tx.address_n = address_n + # Will return a StellarTxOpRequest - resp = self.call(proto.StellarSignTx( - protocol_version=parsed["protocol_version"], - address_n=address_n, - network_passphrase=network_passphrase, - source_account=parsed["source_account"], - fee=parsed["fee"], - sequence_number=parsed["sequence_number"], - timebounds_start=parsed["timebounds_start"], - timebounds_end=parsed["timebounds_end"], - memo_type=parsed["memo_type"], - memo_text=parsed["memo_text"], - memo_id=parsed["memo_id"], - memo_hash=parsed["memo_hash"], - num_operations=parsed["num_operations"] - )) + resp = self.call(parsed["tx"]) if not isinstance(resp, proto.StellarTxOpRequest): raise CallException("Unexpected response to transaction") - for op_idx in range(0, parsed["num_operations"]): - op = parsed["operations"][op_idx] - resp = None + # Send each operation to the device. + # The response to the last message should be a StellarSignedTx + for op in parsed["operations"]: + resp = self.call(op) - # Create account - if op["type"] == 0: - resp = self.call(proto.StellarCreateAccountOp( - source_account=op["source_account"], - new_account=op["new_account"], - starting_balance=op["starting_balance"] - )) - # Payment - if op["type"] == 1: - asset = proto.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], - issuer=op["asset"]["issuer"]) - resp = self.call(proto.StellarPaymentOp( - source_account=op["source_account"], - destination_account=op["destination_account"], - amount=op["amount"], - asset=asset - )) - # Path Payment - if op["type"] == 2: - destination_asset = proto.StellarAssetType(type=op["destination_asset"]["type"], - code=op["destination_asset"]["code"], - issuer=op["destination_asset"]["issuer"]) - resp = self.call(proto.StellarPathPaymentOp( - source_account=op["source_account"], - send_max=op["send_max"], - destination_account=op["destination_account"], - destination_asset=destination_asset, - destination_amount=op["destination_amount"], - paths=op["paths"] - )) - # Manage Offer - if op["type"] == 3: - selling_asset = proto.StellarAssetType(type=op["selling_asset"]["type"], - code=op["selling_asset"]["code"], - issuer=op["selling_asset"]["issuer"]) - buying_asset = proto.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], - issuer=op["buying_asset"]["issuer"]) - resp = self.call(proto.StellarManageOfferOp( - source_account=op["source_account"], - selling_asset=selling_asset, - buying_asset=buying_asset, - amount=op["amount"], - price_n=op["price_n"], - price_d=op["price_d"], - offer_id=op["offer_id"] - )) - # Passive Offer - if op["type"] == 4: - selling_asset = proto.StellarAssetType(type=op["selling_asset"]["type"], - code=op["selling_asset"]["code"], - issuer=op["selling_asset"]["issuer"]) - buying_asset = proto.StellarAssetType(type=op["buying_asset"]["type"], code=op["buying_asset"]["code"], - issuer=op["buying_asset"]["issuer"]) - resp = self.call(proto.StellarCreatePassiveOfferOp( - source_account=op["source_account"], - selling_asset=selling_asset, - buying_asset=buying_asset, - amount=op["amount"], - price_n=op["price_n"], - price_d=op["price_d"] - )) - # Set Options - if op["type"] == 5: - resp = self.call(proto.StellarSetOptionsOp( - source_account=op["source_account"], - inflation_destination_account=op["inflation_destination"], - clear_flags=op["clear_flags"], - set_flags=op["set_flags"], - master_weight=op["master_weight"], - low_threshold=op["low_threshold"], - medium_threshold=op["medium_threshold"], - high_threshold=op["high_threshold"], - home_domain=op["home_domain"], - signer_type=op["signer_type"], - signer_key=op["signer_key"], - signer_weight=op["signer_weight"], - )) - # Change Trust - if op["type"] == 6: - asset = proto.StellarAssetType(type=op["asset"]["type"], code=op["asset"]["code"], - issuer=op["asset"]["issuer"]) - resp = self.call(proto.StellarChangeTrustOp( - source_account=op["source_account"], - limit=op["limit"], - asset=asset - )) - # Allow Trust - if op["type"] == 7: - resp = self.call(proto.StellarAllowTrustOp( - source_account=op["source_account"], - trusted_account=op["trusted_account"], - asset_type=op["asset_type"], - asset_code=op["asset_code"], - is_authorized=op["is_authorized"] - )) - # Merge Account - if op["type"] == 8: - resp = self.call(proto.StellarAccountMergeOp( - source_account=op["source_account"], - destination_account=op["destination_account"] - )) - # Manage data - if op["type"] == 10: - resp = self.call(proto.StellarManageDataOp( - source_account=op["source_account"], - key=op["key"], - value=op["value"] - )) - # Merge Account - if op["type"] == 11: - resp = self.call(proto.StellarBumpSequenceOp( - source_account=op["source_account"], - bump_to=op["bump_to"] - )) + # Verify expected response message StellarSignedTx and return it + if not isinstance(resp, proto.StellarSignedTx): + raise CallException("Stellar.UnexpectedEndOfOperations", "Reached end of operations without a signature") - # Exit if the response was a StellarSignedTx - if isinstance(resp, proto.StellarSignedTx): - return resp - - raise CallException("Reached end of operations without a signature") + return resp @expect(proto.StellarMessageSignature) def stellar_sign_message(self, address_n, message): From 94f3f437467574c14989138954ae93dd86e3b0aa Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Thu, 19 Apr 2018 15:18:55 -0600 Subject: [PATCH 09/15] sync with current state of upstream master --- tools/pb2py | 1 - trezorlib/messages/ApplySettings.py | 2 ++ trezorlib/messages/DebugLinkDecision.py | 4 +++- trezorlib/messages/DebugLinkState.py | 1 + trezorlib/messages/Features.py | 1 + trezorlib/messages/StellarCreateAccountOp.py | 2 +- trezorlib/messages/StellarCreatePassiveOfferOp.py | 2 +- trezorlib/messages/StellarManageOfferOp.py | 2 +- trezorlib/messages/StellarPathPaymentOp.py | 4 ++-- trezorlib/messages/StellarPaymentOp.py | 2 +- trezorlib/messages/__init__.py | 1 + 11 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tools/pb2py b/tools/pb2py index 587c8065a5..5188ec078c 100755 --- a/tools/pb2py +++ b/tools/pb2py @@ -68,7 +68,6 @@ def process_message(descriptor, protobuf_module, msg_id, indexfile, is_upy): types = { field.TYPE_UINT64: 'p.UVarintType', - field.TYPE_INT64: 'p.UVarintType', field.TYPE_UINT32: 'p.UVarintType', field.TYPE_ENUM: 'p.UVarintType', field.TYPE_SINT32: 'p.Sint32Type', diff --git a/trezorlib/messages/ApplySettings.py b/trezorlib/messages/ApplySettings.py index 32f8295414..fbb776e6ae 100644 --- a/trezorlib/messages/ApplySettings.py +++ b/trezorlib/messages/ApplySettings.py @@ -8,5 +8,7 @@ class ApplySettings(p.MessageType): 2: ('label', p.UnicodeType, 0), 3: ('use_passphrase', p.BoolType, 0), 4: ('homescreen', p.BytesType, 0), + 5: ('passphrase_source', p.UVarintType, 0), + 6: ('auto_lock_delay_ms', p.UVarintType, 0), } MESSAGE_WIRE_TYPE = 25 diff --git a/trezorlib/messages/DebugLinkDecision.py b/trezorlib/messages/DebugLinkDecision.py index 482d886c6a..e5016f304c 100644 --- a/trezorlib/messages/DebugLinkDecision.py +++ b/trezorlib/messages/DebugLinkDecision.py @@ -4,6 +4,8 @@ from .. import protobuf as p class DebugLinkDecision(p.MessageType): FIELDS = { - 1: ('yes_no', p.BoolType, 0), # required + 1: ('yes_no', p.BoolType, 0), + 2: ('up_down', p.BoolType, 0), + 3: ('input', p.UnicodeType, 0), } MESSAGE_WIRE_TYPE = 100 diff --git a/trezorlib/messages/DebugLinkState.py b/trezorlib/messages/DebugLinkState.py index 9438fe8c3a..ef671619ed 100644 --- a/trezorlib/messages/DebugLinkState.py +++ b/trezorlib/messages/DebugLinkState.py @@ -15,5 +15,6 @@ class DebugLinkState(p.MessageType): 8: ('reset_entropy', p.BytesType, 0), 9: ('recovery_fake_word', p.UnicodeType, 0), 10: ('recovery_word_pos', p.UVarintType, 0), + 11: ('reset_word_pos', p.UVarintType, 0), } MESSAGE_WIRE_TYPE = 102 diff --git a/trezorlib/messages/Features.py b/trezorlib/messages/Features.py index 878266dec7..d1eaaa39a9 100644 --- a/trezorlib/messages/Features.py +++ b/trezorlib/messages/Features.py @@ -31,5 +31,6 @@ class Features(p.MessageType): 24: ('fw_patch', p.UVarintType, 0), 25: ('fw_vendor', p.UnicodeType, 0), 26: ('fw_vendor_keys', p.BytesType, 0), + 27: ('unfinished_backup', p.BoolType, 0), } MESSAGE_WIRE_TYPE = 17 diff --git a/trezorlib/messages/StellarCreateAccountOp.py b/trezorlib/messages/StellarCreateAccountOp.py index c650c9c5be..e1fee62db6 100644 --- a/trezorlib/messages/StellarCreateAccountOp.py +++ b/trezorlib/messages/StellarCreateAccountOp.py @@ -6,6 +6,6 @@ class StellarCreateAccountOp(p.MessageType): FIELDS = { 1: ('source_account', p.BytesType, 0), 2: ('new_account', p.BytesType, 0), - 3: ('starting_balance', p.UVarintType, 0), + 3: ('starting_balance', p.Sint64Type, 0), } MESSAGE_WIRE_TYPE = 210 diff --git a/trezorlib/messages/StellarCreatePassiveOfferOp.py b/trezorlib/messages/StellarCreatePassiveOfferOp.py index c3ee4865cf..b84ab0662f 100644 --- a/trezorlib/messages/StellarCreatePassiveOfferOp.py +++ b/trezorlib/messages/StellarCreatePassiveOfferOp.py @@ -8,7 +8,7 @@ class StellarCreatePassiveOfferOp(p.MessageType): 1: ('source_account', p.BytesType, 0), 2: ('selling_asset', StellarAssetType, 0), 3: ('buying_asset', StellarAssetType, 0), - 4: ('amount', p.UVarintType, 0), + 4: ('amount', p.Sint64Type, 0), 5: ('price_n', p.UVarintType, 0), 6: ('price_d', p.UVarintType, 0), } diff --git a/trezorlib/messages/StellarManageOfferOp.py b/trezorlib/messages/StellarManageOfferOp.py index 858d92f639..8a77ad4417 100644 --- a/trezorlib/messages/StellarManageOfferOp.py +++ b/trezorlib/messages/StellarManageOfferOp.py @@ -8,7 +8,7 @@ class StellarManageOfferOp(p.MessageType): 1: ('source_account', p.BytesType, 0), 2: ('selling_asset', StellarAssetType, 0), 3: ('buying_asset', StellarAssetType, 0), - 4: ('amount', p.UVarintType, 0), + 4: ('amount', p.Sint64Type, 0), 5: ('price_n', p.UVarintType, 0), 6: ('price_d', p.UVarintType, 0), 7: ('offer_id', p.UVarintType, 0), diff --git a/trezorlib/messages/StellarPathPaymentOp.py b/trezorlib/messages/StellarPathPaymentOp.py index 4381ecce17..906bf3499a 100644 --- a/trezorlib/messages/StellarPathPaymentOp.py +++ b/trezorlib/messages/StellarPathPaymentOp.py @@ -7,10 +7,10 @@ class StellarPathPaymentOp(p.MessageType): FIELDS = { 1: ('source_account', p.BytesType, 0), 2: ('send_asset', StellarAssetType, 0), - 3: ('send_max', p.UVarintType, 0), + 3: ('send_max', p.Sint64Type, 0), 4: ('destination_account', p.BytesType, 0), 5: ('destination_asset', StellarAssetType, 0), - 6: ('destination_amount', p.UVarintType, 0), + 6: ('destination_amount', p.Sint64Type, 0), 7: ('paths', StellarAssetType, p.FLAG_REPEATED), } MESSAGE_WIRE_TYPE = 212 diff --git a/trezorlib/messages/StellarPaymentOp.py b/trezorlib/messages/StellarPaymentOp.py index d4706dcd1f..ca15f51a90 100644 --- a/trezorlib/messages/StellarPaymentOp.py +++ b/trezorlib/messages/StellarPaymentOp.py @@ -8,6 +8,6 @@ class StellarPaymentOp(p.MessageType): 1: ('source_account', p.BytesType, 0), 2: ('destination_account', p.BytesType, 0), 3: ('asset', StellarAssetType, 0), - 4: ('amount', p.UVarintType, 0), + 4: ('amount', p.Sint64Type, 0), } MESSAGE_WIRE_TYPE = 211 diff --git a/trezorlib/messages/__init__.py b/trezorlib/messages/__init__.py index 07dc3fc858..6c69004719 100644 --- a/trezorlib/messages/__init__.py +++ b/trezorlib/messages/__init__.py @@ -30,6 +30,7 @@ from . import ButtonRequestType from . import PinMatrixRequestType from . import RecoveryDeviceType from . import WordRequestType +from . import PassphraseSourceType from . import NEMMosaicLevy from . import NEMSupplyChangeType from . import NEMModificationType From ea6ced4daef20852f65da4f1773186087d9bd189 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Fri, 20 Apr 2018 10:33:56 -0600 Subject: [PATCH 10/15] client.py - refactor Stellar signing loop to make the message flow clearer --- trezorlib/client.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index a09937d055..303c4414fc 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -1110,25 +1110,33 @@ class ProtocolMixin(object): if network_passphrase is None: network_passphrase = "Public Global Stellar Network ; September 2015" - parsed = stellar.parse_transaction_bytes(tx_envelope) + tx, operations = stellar.parse_transaction_bytes(tx_envelope) - tx = parsed["tx"] tx.network_passphrase = network_passphrase tx.address_n = address_n - # Will return a StellarTxOpRequest - resp = self.call(parsed["tx"]) - if not isinstance(resp, proto.StellarTxOpRequest): - raise CallException("Unexpected response to transaction") + # Signing loop works as follows: + # + # 1. Start with tx (header information for the transaction) and operations (an array of operation protobuf messagess) + # 2. Send the tx header to the device + # 3. Receive a StellarTxOpRequest message + # 4. Send operations one by one until all operations have been sent. If there are more operations to sign, the device will send a StellarTxOpRequest message + # 5. The final message received will be StellarSignedTx which is returned from this method + resp = self.call(tx) + try: + while isinstance(resp, proto.StellarTxOpRequest): + resp = self.call(operations.pop(0)) + except IndexError: + # pop from empty list + raise CallException("Stellar.UnexpectedEndOfOperations", + "Reached end of operations without a signature.") from None - # Send each operation to the device. - # The response to the last message should be a StellarSignedTx - for op in parsed["operations"]: - resp = self.call(op) - - # Verify expected response message StellarSignedTx and return it if not isinstance(resp, proto.StellarSignedTx): - raise CallException("Stellar.UnexpectedEndOfOperations", "Reached end of operations without a signature") + raise CallException(proto.FailureType.UnexpectedMessage, resp) + + if operations: + raise CallException("Stellar.UnprocessedOperations", + "Received a signature before processing all operations.") return resp From 61f5e1df61c833b0e11a77561c0a84605d1cc9ab Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Fri, 20 Apr 2018 10:34:26 -0600 Subject: [PATCH 11/15] stellar.py now returns a tuple from parse_transaction_bytes and no longer uses a reserved argument name --- trezorlib/stellar.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/trezorlib/stellar.py b/trezorlib/stellar.py index 62278cbe29..e235a13245 100644 --- a/trezorlib/stellar.py +++ b/trezorlib/stellar.py @@ -63,7 +63,7 @@ def address_to_public_key(address_str): return decoded[1:-2] -def parse_transaction_bytes(bytes): +def parse_transaction_bytes(tx_bytes): """Parses base64data into a map with the following keys: tx - a StellarSignTx describing the transaction header operations - an array of protobuf message objects for each operation @@ -72,7 +72,7 @@ def parse_transaction_bytes(bytes): protocol_version=1 ) operations = [] - unpacker = xdrlib.Unpacker(bytes) + unpacker = xdrlib.Unpacker(tx_bytes) tx.source_account = _xdr_read_address(unpacker) tx.fee = unpacker.unpack_uint() @@ -108,10 +108,7 @@ def parse_transaction_bytes(bytes): for i in range(tx.num_operations): operations.append(_parse_operation_bytes(unpacker)) - return { - "tx": tx, - "operations": operations - } + return tx, operations def _parse_operation_bytes(unpacker): """Returns a protobuf message representing the next operation as read from From 49025aad2bbfc664db0679dd3149958261d7d646 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Fri, 20 Apr 2018 10:35:01 -0600 Subject: [PATCH 12/15] stellar tests - fix imports and indicate that tests are currently expected to fail --- .../device_tests/test_msg_stellar_get_public_key.py | 5 +++-- .../test_msg_stellar_sign_transaction.py | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py index b3f3a012cb..91438e5565 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py @@ -13,9 +13,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * -import trezorlib.stellar as stellar +from .common import TrezorTest +from trezorlib import stellar +@pytest.mark.xfail # requires trezor-mcu PR #259 class TestMsgStellarGetPublicKey(TrezorTest): def test_stellar_get_address(self): diff --git a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py index f3b0df1045..88bc791be8 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py @@ -17,9 +17,10 @@ # https://www.stellar.org/laboratory/#xdr-viewer # -import base64 -from .common import * +from base64 import b64decode, b64encode +from .common import TrezorTest +@pytest.mark.xfail # requires trezor-mcu PR #259 class TestMsgStellarSignTransaction(TrezorTest): def get_network_passphrase(self): @@ -33,16 +34,16 @@ class TestMsgStellarSignTransaction(TrezorTest): def test_sign_tx_bump_sequence_op(self): self.setup_mnemonic_nopin_nopassphrase() - xdr = base64.b64decode("AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAt//////////wAAAAAAAAAA") + xdr = b64decode("AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAt//////////wAAAAAAAAAA") response = self.client.stellar_sign_transaction(xdr, self.get_address_n(), self.get_network_passphrase()) - assert base64.b64encode(response.signature) == b'UAOL4ZPYIOzEgM66kBrhyNjLR66dNXtuNrmvd3m0/pc8qCSoLmYY4TybS0lHiMtb+LFZESTaxrpErMHz1sZ6DQ==' + assert b64encode(response.signature) == b'UAOL4ZPYIOzEgM66kBrhyNjLR66dNXtuNrmvd3m0/pc8qCSoLmYY4TybS0lHiMtb+LFZESTaxrpErMHz1sZ6DQ==' def test_sign_tx_account_merge_op(self): self.setup_mnemonic_nopin_nopassphrase() - xdr = base64.b64decode("AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAgAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAAAAAAAQHNtGkAAABAgjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==") + xdr = b64decode("AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAgAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAAAAAAAQHNtGkAAABAgjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==") response = self.client.stellar_sign_transaction(xdr, self.get_address_n(), self.get_network_passphrase()) - assert base64.b64encode(response.signature) == b'gjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==' + assert b64encode(response.signature) == b'gjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==' From 2cf6414fa562afe6f5d03d85f5f1b1d2691ea41d Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Sat, 28 Apr 2018 12:39:55 -0600 Subject: [PATCH 13/15] stellar - minor cleanup and simplification of some boolean field checks --- trezorlib/stellar.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/trezorlib/stellar.py b/trezorlib/stellar.py index e235a13245..129856ccd8 100644 --- a/trezorlib/stellar.py +++ b/trezorlib/stellar.py @@ -71,7 +71,6 @@ def parse_transaction_bytes(tx_bytes): tx = proto.StellarSignTx( protocol_version=1 ) - operations = [] unpacker = xdrlib.Unpacker(tx_bytes) tx.source_account = _xdr_read_address(unpacker) @@ -79,8 +78,7 @@ def parse_transaction_bytes(tx_bytes): tx.sequence_number = unpacker.unpack_uhyper() # Timebounds is an optional field - has_timebounds = unpacker.unpack_bool() - if has_timebounds: + if unpacker.unpack_bool(): max_timebound = 2**32-1 # max unsigned 32-bit int (trezor does not support the full 64-bit time value) tx.timebounds_start = unpacker.unpack_uhyper() tx.timebounds_end = unpacker.unpack_uhyper() @@ -105,6 +103,7 @@ def parse_transaction_bytes(tx_bytes): tx.num_operations = unpacker.unpack_uint() + operations = [] for i in range(tx.num_operations): operations.append(_parse_operation_bytes(unpacker)) @@ -117,8 +116,7 @@ def _parse_operation_bytes(unpacker): # Check for and parse optional source account field source_account = None - has_source_account = unpacker.unpack_bool() - if has_source_account: + if unpacker.unpack_bool(): source_account = unpacker.unpack_fopaque(32) # Operation type (See OP_ constants) @@ -313,11 +311,11 @@ def _crc16_checksum(bytes): polynomial = 0x1021 for byte in bytes: - for i in range(0, 8): + for i in range(8): bit = ((byte >> (7 - i) & 1) == 1) c15 = ((crc >> 15 & 1) == 1) crc <<= 1 if c15 ^ bit: crc ^= polynomial - return crc & 0xffff \ No newline at end of file + return crc & 0xffff From 5fccc4381bb9900a82bccce22070cb92f6fce500 Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Sat, 28 Apr 2018 12:59:08 -0600 Subject: [PATCH 14/15] fix style to match flake8 --- trezorctl | 3 ++ trezorlib/stellar.py | 39 +++++++++++-------- .../test_msg_stellar_get_public_key.py | 3 +- .../test_msg_stellar_sign_transaction.py | 4 +- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/trezorctl b/trezorctl index c7bb5095e7..edb15fb23d 100755 --- a/trezorctl +++ b/trezorctl @@ -975,6 +975,7 @@ def stellar_get_address(connect, address): response = client.stellar_get_public_key(address_n) return stellar.address_from_public_key(response.public_key) + @cli.command(help='Sign a string with a Stellar key') @click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") @click.argument('message') @@ -985,6 +986,7 @@ def stellar_sign_message(connect, address, message): response = client.stellar_sign_message(address_n, message) return base64.b64encode(response.signature) + @cli.command(help='Verify that a signature is valid') @click.option('--message-is-b64/--no-message-is-b64', default=False, required=False, help="If set, the message argument will be interpreted as a base64-encoded string") @click.argument('address') @@ -1008,6 +1010,7 @@ def stellar_verify_message(connect, address, message_is_b64, signatureb64, messa print("ERROR: invalid signature, verification failed") sys.exit(1) + @cli.command(help='Sign a base64-encoded transaction envelope') @click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") @click.option('-n', '--network-passphrase', required=False, help="Network passphrase (blank for public network). Testnet is: 'Test SDF Network ; September 2015'") diff --git a/trezorlib/stellar.py b/trezorlib/stellar.py index 129856ccd8..292dae7c77 100644 --- a/trezorlib/stellar.py +++ b/trezorlib/stellar.py @@ -5,29 +5,30 @@ import xdrlib from . import messages as proto # Memo types -MEMO_TYPE_TEXT = 0 -MEMO_TYPE_ID = 1 -MEMO_TYPE_HASH = 2 +MEMO_TYPE_TEXT = 0 +MEMO_TYPE_ID = 1 +MEMO_TYPE_HASH = 2 MEMO_TYPE_RETURN = 4 # Asset types -ASSET_TYPE_NATIVE = 0 -ASSET_TYPE_ALPHA4 = 1 +ASSET_TYPE_NATIVE = 0 +ASSET_TYPE_ALPHA4 = 1 ASSET_TYPE_ALPHA12 = 2 # Operations -OP_CREATE_ACCOUNT = 0 -OP_PAYMENT = 1 -OP_PATH_PAYMENT = 2 -OP_MANAGE_OFFER = 3 +OP_CREATE_ACCOUNT = 0 +OP_PAYMENT = 1 +OP_PATH_PAYMENT = 2 +OP_MANAGE_OFFER = 3 OP_CREATE_PASSIVE_OFFER = 4 -OP_SET_OPTIONS = 5 -OP_CHANGE_TRUST = 6 -OP_ALLOW_TRUST = 7 -OP_ACCOUNT_MERGE = 8 -OP_INFLATION = 9 # Included for documentation purposes, not supported by Trezor -OP_MANAGE_DATA = 10 -OP_BUMP_SEQUENCE = 11 +OP_SET_OPTIONS = 5 +OP_CHANGE_TRUST = 6 +OP_ALLOW_TRUST = 7 +OP_ACCOUNT_MERGE = 8 +OP_INFLATION = 9 # Included for documentation purposes, not supported by Trezor +OP_MANAGE_DATA = 10 +OP_BUMP_SEQUENCE = 11 + def expand_path_or_default(client, address): """Uses client to parse address and returns an array of integers @@ -53,6 +54,7 @@ def address_from_public_key(pk_bytes): return base64.b32encode(final_bytes) + def address_to_public_key(address_str): """Returns the raw 32 bytes representing a public key by extracting it from the G... string @@ -79,7 +81,7 @@ def parse_transaction_bytes(tx_bytes): # Timebounds is an optional field if unpacker.unpack_bool(): - max_timebound = 2**32-1 # max unsigned 32-bit int (trezor does not support the full 64-bit time value) + max_timebound = 2**32 - 1 # max unsigned 32-bit int (trezor does not support the full 64-bit time value) tx.timebounds_start = unpacker.unpack_uhyper() tx.timebounds_end = unpacker.unpack_uhyper() @@ -109,6 +111,7 @@ def parse_transaction_bytes(tx_bytes): return tx, operations + def _parse_operation_bytes(unpacker): """Returns a protobuf message representing the next operation as read from the byte stream in unpacker @@ -272,6 +275,7 @@ def _parse_operation_bytes(unpacker): raise ValueError("Unknown operation type: " + type) + def _xdr_read_asset(unpacker): """Reads a stellar Asset from unpacker""" asset = proto.StellarAssetType( @@ -300,6 +304,7 @@ def _xdr_read_address(unpacker): return unpacker.unpack_fopaque(32) + def _crc16_checksum(bytes): """Returns the CRC-16 checksum of bytearray bytes diff --git a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py index 91438e5565..c5131d6cac 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py @@ -16,7 +16,8 @@ from .common import TrezorTest from trezorlib import stellar -@pytest.mark.xfail # requires trezor-mcu PR #259 + +@pytest.mark.xfail # requires trezor-mcu PR #259 class TestMsgStellarGetPublicKey(TrezorTest): def test_stellar_get_address(self): diff --git a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py index 88bc791be8..aaccafef6a 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py @@ -20,7 +20,8 @@ from base64 import b64decode, b64encode from .common import TrezorTest -@pytest.mark.xfail # requires trezor-mcu PR #259 + +@pytest.mark.xfail # requires trezor-mcu PR #259 class TestMsgStellarSignTransaction(TrezorTest): def get_network_passphrase(self): @@ -39,7 +40,6 @@ class TestMsgStellarSignTransaction(TrezorTest): response = self.client.stellar_sign_transaction(xdr, self.get_address_n(), self.get_network_passphrase()) assert b64encode(response.signature) == b'UAOL4ZPYIOzEgM66kBrhyNjLR66dNXtuNrmvd3m0/pc8qCSoLmYY4TybS0lHiMtb+LFZESTaxrpErMHz1sZ6DQ==' - def test_sign_tx_account_merge_op(self): self.setup_mnemonic_nopin_nopassphrase() From 7128347975f3bfe53ff9426b5b4039ce0d244edb Mon Sep 17 00:00:00 2001 From: ZuluCrypto Date: Sat, 28 Apr 2018 13:06:13 -0600 Subject: [PATCH 15/15] stellar tests - added missing pytest import --- trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py | 1 + .../tests/device_tests/test_msg_stellar_sign_transaction.py | 1 + 2 files changed, 2 insertions(+) diff --git a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py index c5131d6cac..b18d201373 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py @@ -15,6 +15,7 @@ from .common import TrezorTest from trezorlib import stellar +import pytest @pytest.mark.xfail # requires trezor-mcu PR #259 diff --git a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py index aaccafef6a..0c1a2802cc 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py @@ -19,6 +19,7 @@ from base64 import b64decode, b64encode from .common import TrezorTest +import pytest @pytest.mark.xfail # requires trezor-mcu PR #259