diff --git a/common/protob/messages-stellar.proto b/common/protob/messages-stellar.proto index 5be931ec4d2..8970a33002c 100644 --- a/common/protob/messages-stellar.proto +++ b/common/protob/messages-stellar.proto @@ -74,9 +74,11 @@ message StellarSignTx { * Response: device is ready for client to send the next operation * @next StellarPaymentOp * @next StellarCreateAccountOp - * @next StellarPathPaymentOp - * @next StellarManageOfferOp - * @next StellarCreatePassiveOfferOp + * @next StellarPathPaymentStrictReceiveOp + * @next StellarPathPaymentStrictSendOp + * @next StellarManageSellOfferOp + * @next StellarManageBuyOfferOp + * @next StellarCreatePassiveSellOfferOp * @next StellarSetOptionsOp * @next StellarChangeTrustOp * @next StellarAllowTrustOp @@ -115,14 +117,14 @@ message StellarCreateAccountOp { * @next StellarTxOpRequest * @next StellarSignedTx */ -message StellarPathPaymentOp { - optional string source_account = 1; // (optional) source address - required StellarAsset send_asset = 2; - required sint64 send_max = 3; - required string destination_account = 4; - required StellarAsset destination_asset = 5; - required sint64 destination_amount = 6; - repeated StellarAsset paths = 7; +message StellarPathPaymentStrictReceiveOp { + optional string source_account = 1; // (optional) source address + required StellarAsset send_asset = 2; // asset we pay with + required sint64 send_max = 3; // the maximum amount of sendAsset to send (excluding fees) + required string destination_account = 4; // recipient of the payment + required StellarAsset destination_asset = 5; // what they end up with + required sint64 destination_amount = 6; // amount they end up with + repeated StellarAsset paths = 7; // additional hops it must go through to get there } /** @@ -130,7 +132,37 @@ message StellarPathPaymentOp { * @next StellarTxOpRequest * @next StellarSignedTx */ -message StellarManageOfferOp { +message StellarPathPaymentStrictSendOp { + optional string source_account = 1; // (optional) source address + required StellarAsset send_asset = 2; // asset we pay with + required sint64 send_amount = 3; // amount of sendAsset to send (excluding fees) + required string destination_account = 4; // recipient of the payment + required StellarAsset destination_asset = 5; // what they end up with + required sint64 destination_min = 6; // the minimum amount of dest asset to be received + repeated StellarAsset paths = 7; //additional hops it must go through to get there +} + +/** + * Request: ask device to confirm this operation type + * @next StellarTxOpRequest + * @next StellarSignedTx + */ +message StellarManageSellOfferOp { + optional string source_account = 1; // (optional) source account address + required StellarAsset selling_asset = 2; + required StellarAsset buying_asset = 3; + required sint64 amount = 4; + required uint32 price_n = 5; // Price numerator + required uint32 price_d = 6; // Price denominator + required uint64 offer_id = 7; // Offer ID for updating an existing offer +} + +/** + * Request: ask device to confirm this operation type + * @next StellarTxOpRequest + * @next StellarSignedTx + */ +message StellarManageBuyOfferOp { optional string source_account = 1; // (optional) source account address required StellarAsset selling_asset = 2; required StellarAsset buying_asset = 3; @@ -145,7 +177,7 @@ message StellarManageOfferOp { * @next StellarTxOpRequest * @next StellarSignedTx */ -message StellarCreatePassiveOfferOp { +message StellarCreatePassiveSellOfferOp { optional string source_account = 1; // (optional) source account address required StellarAsset selling_asset = 2; required StellarAsset buying_asset = 3; diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 138f9b190a0..fa2f9fd6335 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -229,9 +229,9 @@ enum MessageType { MessageType_StellarAddress = 208 [(wire_out) = true]; MessageType_StellarCreateAccountOp = 210 [(wire_in) = true]; MessageType_StellarPaymentOp = 211 [(wire_in) = true]; - MessageType_StellarPathPaymentOp = 212 [(wire_in) = true]; - MessageType_StellarManageOfferOp = 213 [(wire_in) = true]; - MessageType_StellarCreatePassiveOfferOp = 214 [(wire_in) = true]; + MessageType_StellarPathPaymentStrictReceiveOp = 212 [(wire_in) = true]; + MessageType_StellarManageSellOfferOp = 213 [(wire_in) = true]; + MessageType_StellarCreatePassiveSellOfferOp = 214 [(wire_in) = true]; MessageType_StellarSetOptionsOp = 215 [(wire_in) = true]; MessageType_StellarChangeTrustOp = 216 [(wire_in) = true]; MessageType_StellarAllowTrustOp = 217 [(wire_in) = true]; @@ -239,6 +239,8 @@ enum MessageType { // omitted: StellarInflationOp is not a supported operation, would be 219 MessageType_StellarManageDataOp = 220 [(wire_in) = true]; MessageType_StellarBumpSequenceOp = 221 [(wire_in) = true]; + MessageType_StellarManageBuyOfferOp = 222 [(wire_in) = true]; + MessageType_StellarPathPaymentStrictSendOp = 223 [(wire_in) = true]; MessageType_StellarSignedTx = 230 [(wire_out) = true]; // Cardano diff --git a/common/tests/fixtures/stellar/sign_tx.json b/common/tests/fixtures/stellar/sign_tx.json index 81e7f17cf4c..d3e6353fd54 100644 --- a/common/tests/fixtures/stellar/sign_tx.json +++ b/common/tests/fixtures/stellar/sign_tx.json @@ -298,7 +298,7 @@ } }, { - "name": "StellarCreatePassiveOfferOp", + "name": "StellarCreatePassiveSellOfferOp", "parameters": { "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAAD6AAAAAEAAAAAG4J3zQAAAABd5CqEAAAAAAAAAAEAAAAAAAAABAAAAAJBQkNERUZHSElKS0wAAAAAKYSWAsIOWDZfEjwS2HocpFUNEM0hsK4OGNROPlb9ahUAAAABWAAAAAAAAAAphJYCwg5YNl8SPBLYehykVQ0QzSGwrg4Y1E4+Vv1qFQAAAAAdzxaYAAAAAwAAAAQAAAAAAAAAAA==", "address_n": "m/44'/148'/0'", @@ -313,7 +313,7 @@ }, "operations": [ { - "_message_type": "StellarCreatePassiveOfferOp", + "_message_type": "StellarCreatePassiveSellOfferOp", "selling_asset": { "type": "ALPHANUM12", "code": "ABCDEFGHIJKL", @@ -336,7 +336,46 @@ } }, { - "name": "StellarManageOfferOp", + "name": "StellarManageBuyOfferOp", + "parameters": { + "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAAD6AAAAAEAAAAAG4J3zQAAAABd5CqEAAAAAAAAAAEAAAAAAAAADAAAAAJBQkNERUZHSElKS0wAAAAAKYSWAsIOWDZfEjwS2HocpFUNEM0hsK4OGNROPlb9ahUAAAABWAAAAAAAAABwi6oxX35cFm2EtGS/s4/WJXj+OtJyJ+dsy7ehecRRIQAAAAAdzxaYAAAAAwAAAAQAAAAAAAAFOQAAAAAAAAAA", + "address_n": "m/44'/148'/0'", + "network_passphrase": "Test SDF Network ; September 2015", + "tx": { + "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV", + "fee": 100, + "sequence_number": 1000, + "timebounds_start": 461535181, + "timebounds_end": 1575234180, + "memo_type": "NONE" + }, + "operations": [ + { + "_message_type": "StellarManageBuyOfferOp", + "selling_asset": { + "type": "ALPHANUM12", + "code": "ABCDEFGHIJKL", + "issuer": "GAUYJFQCYIHFQNS7CI6BFWD2DSSFKDIQZUQ3BLQODDKE4PSW7VVBKENC" + }, + "buying_asset": { + "type": "ALPHANUM4", + "code": "X", + "issuer": "GBYIXKRRL57FYFTNQS2GJP5TR7LCK6H6HLJHEJ7HNTF3PILZYRISDLNQ" + }, + "amount": 500111000, + "price_n": 3, + "price_d": 4, + "offer_id": 1337 + } + ] + }, + "result": { + "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292", + "signature": "sDQuKEm7j6Lsuw+QUTrotSloZiF+8LrXsuoLCBadewWpArO8+qmMgonrG3bJfaZ4dYdD8WcpfP5LNLOfU+lDBA==" + } + }, + { + "name": "StellarManageSellOfferOp", "parameters": { "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAAD6AAAAAEAAAAAG4J3zQAAAABd5CqEAAAAAAAAAAEAAAAAAAAAAwAAAAJBQkNERUZHSElKS0wAAAAAKYSWAsIOWDZfEjwS2HocpFUNEM0hsK4OGNROPlb9ahUAAAABWAAAAAAAAAAphJYCwg5YNl8SPBLYehykVQ0QzSGwrg4Y1E4+Vv1qFQAAAAAdzxaYAAAAAwAAAAQAAAAAAAAFOQAAAAAAAAAA", "address_n": "m/44'/148'/0'", @@ -351,7 +390,7 @@ }, "operations": [ { - "_message_type": "StellarManageOfferOp", + "_message_type": "StellarManageSellOfferOp", "selling_asset": { "type": "ALPHANUM12", "code": "ABCDEFGHIJKL", @@ -375,7 +414,7 @@ } }, { - "name": "StellarPathPaymentOp", + "name": "StellarPathPaymentStrictReceiveOp", "parameters": { "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAAD6AAAAAEAAAAAG4J3zQAAAABd5CqEAAAAAAAAAAEAAAAAAAAAAgAAAAFYAAAAAAAAACmElgLCDlg2XxI8Eth6HKRVDRDNIbCuDhjUTj5W/WoVAAAAAB3PFpgAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAACQUJDREVGR0hJSktMAAAAACmElgLCDlg2XxI8Eth6HKRVDRDNIbCuDhjUTj5W/WoVAAAAAAAB4kAAAAAAAAAAAAAAAAA=", "address_n": "m/44'/148'/0'", @@ -390,7 +429,7 @@ }, "operations": [ { - "_message_type": "StellarPathPaymentOp", + "_message_type": "StellarPathPaymentStrictReceiveOp", "send_asset": { "type": "ALPHANUM4", "code": "X", @@ -412,6 +451,56 @@ "signature": "A/ccrRMTEy3GXaZ7Lo5frX3ME5fy3bDMrmYaZ8oPtpPk+cnRStbcSAgdTKnRq/dPGRLfh2btvPJD9ETMe1ajDA==" } }, + { + "name": "StellarPathPaymentStrictSendOp", + "parameters": { + "xdr": "AAAAAgAAAAAvIrnGLwi3dPPr5t1ufbk8PsLL3gJ5Vho9nFIluMMikgAAAGQAAAAAAAAD6AAAAAEAAAAAG4J3zQAAAABd5CqEAAAAAAAAAAEAAAAAAAAADQAAAAFYAAAAAAAAAHCLqjFfflwWbYS0ZL+zj9YleP460nIn52zLt6F5xFEhAAAAAB3PFpgAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAACQUJDREVGR0hJSktMAAAAACmElgLCDlg2XxI8Eth6HKRVDRDNIbCuDhjUTj5W/WoVAAAAAAAB4kAAAAACAAAAAUpQWQAAAAAA/Pr8d4Jv8Bq3LDTJiXMR/LjsnkO0R2sDyP5fodnIhB4AAAACQkFOQU5BAAAAAAAAAAAAAL5FO387ZKhuvo51/hHfkV1gShE677dHpUZV6jK+Wgg7AAAAAAAAAAA=", + "address_n": "m/44'/148'/0'", + "network_passphrase": "Test SDF Network ; September 2015", + "tx": { + "source_account": "GAXSFOOGF4ELO5HT5PTN23T5XE6D5QWL3YBHSVQ2HWOFEJNYYMRJENBV", + "fee": 100, + "sequence_number": 1000, + "timebounds_start": 461535181, + "timebounds_end": 1575234180, + "memo_type": "NONE" + }, + "operations": [ + { + "_message_type": "StellarPathPaymentStrictSendOp", + "send_asset": { + "type": "ALPHANUM4", + "code": "X", + "issuer": "GBYIXKRRL57FYFTNQS2GJP5TR7LCK6H6HLJHEJ7HNTF3PILZYRISDLNQ" + }, + "send_amount": 500111000, + "destination_account": "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V", + "destination_asset": { + "type": "ALPHANUM12", + "code": "ABCDEFGHIJKL", + "issuer": "GAUYJFQCYIHFQNS7CI6BFWD2DSSFKDIQZUQ3BLQODDKE4PSW7VVBKENC" + }, + "destination_min": 123456, + "paths": [ + { + "type": "ALPHANUM4", + "code": "JPY", + "issuer": "GD6PV7DXQJX7AGVXFQ2MTCLTCH6LR3E6IO2EO2YDZD7F7IOZZCCB5DSQ" + }, + { + "type": "ALPHANUM12", + "code": "BANANA", + "issuer": "GC7EKO37HNSKQ3V6RZ274EO7SFOWASQRHLX3OR5FIZK6UMV6LIEDXHGZ" + } + ] + } + ] + }, + "result": { + "public_key": "2f22b9c62f08b774f3ebe6dd6e7db93c3ec2cbde0279561a3d9c5225b8c32292", + "signature": "ZoJRiq9zQsbv/w5CA4IOqRPsPY46kzrQ0uMgY+Y9Ec6kKk/0ktFt2icEKNvVAKZUYmfEEigKhki/Rt9meN43CQ==" + } + }, { "name": "StellarManageDataOp", "parameters": { @@ -799,7 +888,7 @@ "starting_balance": 100000000 }, { - "_message_type": "StellarCreatePassiveOfferOp", + "_message_type": "StellarCreatePassiveSellOfferOp", "selling_asset": { "type": "ALPHANUM12", "code": "ABCDEFGHIJKL", diff --git a/core/.changelog.d/1838.added b/core/.changelog.d/1838.added new file mode 100644 index 00000000000..43800dd7673 --- /dev/null +++ b/core/.changelog.d/1838.added @@ -0,0 +1 @@ +Stellar: add support for StellarManageBuyOfferOp and StellarPathPaymentStrictSendOp. diff --git a/core/.changelog.d/1838.incompatible b/core/.changelog.d/1838.incompatible new file mode 100644 index 00000000000..3c6f73c6d6b --- /dev/null +++ b/core/.changelog.d/1838.incompatible @@ -0,0 +1 @@ +Stellar: rename StellarManageOfferOp to StellarManageSellOfferOp, StellarPathPaymentOp to StellarPathPaymentStrictReceiveOp and StellarCreatePassiveOfferOp to StellarCreatePassiveSellOfferOp. diff --git a/core/src/apps/stellar/README.md b/core/src/apps/stellar/README.md index 9d5ef2d4ef1..3ad19ad8c9f 100644 --- a/core/src/apps/stellar/README.md +++ b/core/src/apps/stellar/README.md @@ -23,10 +23,12 @@ Stellar transaction is composed of one or more operations. We support all [opera - Bump Sequence - Change Trust - Create Account -- Create Passive Offer +- Create Passive Sell Offer - Manage Data -- Manage Offer -- Path Payment +- Manage Buy Offer +- Manage Sell Offer +- Path Payment Strict Receive +- Path Payment Strict Send - Payment - Set Options diff --git a/core/src/apps/stellar/consts.py b/core/src/apps/stellar/consts.py index 04ebe99ba67..0a08476d07f 100644 --- a/core/src/apps/stellar/consts.py +++ b/core/src/apps/stellar/consts.py @@ -12,10 +12,12 @@ StellarBumpSequenceOp, StellarChangeTrustOp, StellarCreateAccountOp, - StellarCreatePassiveOfferOp, + StellarCreatePassiveSellOfferOp, StellarManageDataOp, - StellarManageOfferOp, - StellarPathPaymentOp, + StellarManageBuyOfferOp, + StellarManageSellOfferOp, + StellarPathPaymentStrictReceiveOp, + StellarPathPaymentStrictSendOp, StellarPaymentOp, StellarSetOptionsOp, ) @@ -26,10 +28,12 @@ StellarBumpSequenceOp, StellarChangeTrustOp, StellarCreateAccountOp, - StellarCreatePassiveOfferOp, + StellarCreatePassiveSellOfferOp, StellarManageDataOp, - StellarManageOfferOp, - StellarPathPaymentOp, + StellarManageBuyOfferOp, + StellarManageSellOfferOp, + StellarPathPaymentStrictReceiveOp, + StellarPathPaymentStrictSendOp, StellarPaymentOp, StellarSetOptionsOp, ] @@ -37,7 +41,7 @@ TX_TYPE = b"\x00\x00\x00\x02" -# source: https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/xdr/Stellar-transaction.x#L16 +# source: https://github.com/stellar/go/blob/a1db2a6b1f/xdr/Stellar-transaction.x#L35 # Inflation not supported see https://github.com/trezor/trezor-core/issues/202#issuecomment-393342089 op_codes: dict[int, int] = { MessageType.StellarAccountMergeOp: 8, @@ -45,10 +49,12 @@ MessageType.StellarBumpSequenceOp: 11, MessageType.StellarChangeTrustOp: 6, MessageType.StellarCreateAccountOp: 0, - MessageType.StellarCreatePassiveOfferOp: 4, + MessageType.StellarCreatePassiveSellOfferOp: 4, MessageType.StellarManageDataOp: 10, - MessageType.StellarManageOfferOp: 3, - MessageType.StellarPathPaymentOp: 2, + MessageType.StellarManageBuyOfferOp: 12, + MessageType.StellarManageSellOfferOp: 3, + MessageType.StellarPathPaymentStrictReceiveOp: 2, + MessageType.StellarPathPaymentStrictSendOp: 13, MessageType.StellarPaymentOp: 1, MessageType.StellarSetOptionsOp: 5, } @@ -59,10 +65,12 @@ MessageType.StellarBumpSequenceOp, MessageType.StellarChangeTrustOp, MessageType.StellarCreateAccountOp, - MessageType.StellarCreatePassiveOfferOp, + MessageType.StellarCreatePassiveSellOfferOp, MessageType.StellarManageDataOp, - MessageType.StellarManageOfferOp, - MessageType.StellarPathPaymentOp, + MessageType.StellarManageBuyOfferOp, + MessageType.StellarManageSellOfferOp, + MessageType.StellarPathPaymentStrictReceiveOp, + MessageType.StellarPathPaymentStrictSendOp, MessageType.StellarPaymentOp, MessageType.StellarSetOptionsOp, ] diff --git a/core/src/apps/stellar/operations/__init__.py b/core/src/apps/stellar/operations/__init__.py index dbac9834cc5..bf5c6b62534 100644 --- a/core/src/apps/stellar/operations/__init__.py +++ b/core/src/apps/stellar/operations/__init__.py @@ -28,18 +28,24 @@ async def process_operation( elif serialize.StellarCreateAccountOp.is_type_of(op): await layout.confirm_create_account_op(ctx, op) serialize.write_create_account_op(w, op) - elif serialize.StellarCreatePassiveOfferOp.is_type_of(op): - await layout.confirm_create_passive_offer_op(ctx, op) - serialize.write_create_passive_offer_op(w, op) + elif serialize.StellarCreatePassiveSellOfferOp.is_type_of(op): + await layout.confirm_create_passive_sell_offer_op(ctx, op) + serialize.write_create_passive_sell_offer_op(w, op) elif serialize.StellarManageDataOp.is_type_of(op): await layout.confirm_manage_data_op(ctx, op) serialize.write_manage_data_op(w, op) - elif serialize.StellarManageOfferOp.is_type_of(op): - await layout.confirm_manage_offer_op(ctx, op) - serialize.write_manage_offer_op(w, op) - elif serialize.StellarPathPaymentOp.is_type_of(op): - await layout.confirm_path_payment_op(ctx, op) - serialize.write_path_payment_op(w, op) + elif serialize.StellarManageBuyOfferOp.is_type_of(op): + await layout.confirm_manage_buy_offer_op(ctx, op) + serialize.write_manage_buy_offer_op(w, op) + elif serialize.StellarManageSellOfferOp.is_type_of(op): + await layout.confirm_manage_sell_offer_op(ctx, op) + serialize.write_manage_sell_offer_op(w, op) + elif serialize.StellarPathPaymentStrictReceiveOp.is_type_of(op): + await layout.confirm_path_payment_strict_receive_op(ctx, op) + serialize.write_path_payment_strict_receive_op(w, op) + elif serialize.StellarPathPaymentStrictSendOp.is_type_of(op): + await layout.confirm_path_payment_strict_send_op(ctx, op) + serialize.write_path_payment_strict_send_op(w, op) elif serialize.StellarPaymentOp.is_type_of(op): await layout.confirm_payment_op(ctx, op) serialize.write_payment_op(w, op) diff --git a/core/src/apps/stellar/operations/layout.py b/core/src/apps/stellar/operations/layout.py index 7d627fcb34f..a6f447f6920 100644 --- a/core/src/apps/stellar/operations/layout.py +++ b/core/src/apps/stellar/operations/layout.py @@ -6,10 +6,12 @@ StellarBumpSequenceOp, StellarChangeTrustOp, StellarCreateAccountOp, - StellarCreatePassiveOfferOp, + StellarCreatePassiveSellOfferOp, + StellarManageBuyOfferOp, StellarManageDataOp, - StellarManageOfferOp, - StellarPathPaymentOp, + StellarManageSellOfferOp, + StellarPathPaymentStrictReceiveOp, + StellarPathPaymentStrictSendOp, StellarPaymentOp, StellarSetOptionsOp, ) @@ -96,8 +98,8 @@ async def confirm_create_account_op(ctx: Context, op: StellarCreateAccountOp) -> ) -async def confirm_create_passive_offer_op( - ctx: Context, op: StellarCreatePassiveOfferOp +async def confirm_create_passive_sell_offer_op( + ctx: Context, op: StellarCreatePassiveSellOfferOp ) -> None: if op.amount == 0: text = "Delete Passive Offer" @@ -106,7 +108,21 @@ async def confirm_create_passive_offer_op( await _confirm_offer(ctx, text, op) -async def confirm_manage_offer_op(ctx: Context, op: StellarManageOfferOp) -> None: +async def confirm_manage_buy_offer_op( + ctx: Context, op: StellarManageBuyOfferOp +) -> None: + await _confirm_manage_offer_op_common(ctx, op) + + +async def confirm_manage_sell_offer_op( + ctx: Context, op: StellarManageSellOfferOp +) -> None: + await _confirm_manage_offer_op_common(ctx, op) + + +async def _confirm_manage_offer_op_common( + ctx: Context, op: StellarManageBuyOfferOp | StellarManageSellOfferOp +) -> None: if op.offer_id == 0: text = "New Offer" else: @@ -121,21 +137,37 @@ async def confirm_manage_offer_op(ctx: Context, op: StellarManageOfferOp) -> Non async def _confirm_offer( ctx: Context, title: str, - op: StellarCreatePassiveOfferOp | StellarManageOfferOp, + op: StellarCreatePassiveSellOfferOp + | StellarManageSellOfferOp + | StellarManageBuyOfferOp, ) -> None: - await confirm_properties( - ctx, - "op_offer", - title=title, - props=( - ("Selling:", format_amount(op.amount, op.selling_asset)), - ("Buying:", format_asset(op.buying_asset)), - ( - f"Price per {format_asset(op.buying_asset)}:", - str(op.price_n / op.price_d), - ), - ), - ) + if StellarManageBuyOfferOp.is_type_of(op): + buying = ("Buying:", format_amount(op.amount, op.buying_asset)) + selling = ("Selling:", format_asset(op.selling_asset)) + price = ( + f"Price per {format_asset(op.selling_asset)}:", + str(op.price_n / op.price_d), + ) + await confirm_properties( + ctx, + "op_offer", + title=title, + props=(buying, selling, price), + ) + else: + selling = ("Selling:", format_amount(op.amount, op.selling_asset)) + buying = ("Buying:", format_asset(op.buying_asset)) + price = ( + f"Price per {format_asset(op.buying_asset)}:", + str(op.price_n / op.price_d), + ) + await confirm_properties( + ctx, + "op_offer", + title=title, + props=(selling, buying, price), + ) + await confirm_asset_issuer(ctx, op.selling_asset) await confirm_asset_issuer(ctx, op.buying_asset) @@ -161,7 +193,9 @@ async def confirm_manage_data_op(ctx: Context, op: StellarManageDataOp) -> None: ) -async def confirm_path_payment_op(ctx: Context, op: StellarPathPaymentOp) -> None: +async def confirm_path_payment_strict_receive_op( + ctx: Context, op: StellarPathPaymentStrictReceiveOp +) -> None: await confirm_output( ctx, address=op.destination_account, @@ -175,7 +209,28 @@ async def confirm_path_payment_op(ctx: Context, op: StellarPathPaymentOp) -> Non title="Debited amount", amount=format_amount(op.send_max, op.send_asset), description="Pay at most:", - br_type="op_path_payment", + br_type="op_path_payment_strict_receive", + ) + await confirm_asset_issuer(ctx, op.send_asset) + + +async def confirm_path_payment_strict_send_op( + ctx: Context, op: StellarPathPaymentStrictSendOp +) -> None: + await confirm_output( + ctx, + address=op.destination_account, + amount=format_amount(op.destination_min, op.destination_asset), + title="Path Pay at least", + ) + await confirm_asset_issuer(ctx, op.destination_asset) + # confirm what the sender is using to pay + await confirm_amount( + ctx, + title="Debited amount", + amount=format_amount(op.send_amount, op.send_asset), + description="Pay:", + br_type="op_path_payment_strict_send", ) await confirm_asset_issuer(ctx, op.send_asset) diff --git a/core/src/apps/stellar/operations/serialize.py b/core/src/apps/stellar/operations/serialize.py index a7546e182a6..d18fcb5b122 100644 --- a/core/src/apps/stellar/operations/serialize.py +++ b/core/src/apps/stellar/operations/serialize.py @@ -6,10 +6,12 @@ StellarBumpSequenceOp, StellarChangeTrustOp, StellarCreateAccountOp, - StellarCreatePassiveOfferOp, + StellarCreatePassiveSellOfferOp, + StellarManageBuyOfferOp, StellarManageDataOp, - StellarManageOfferOp, - StellarPathPaymentOp, + StellarManageSellOfferOp, + StellarPathPaymentStrictReceiveOp, + StellarPathPaymentStrictSendOp, StellarPaymentOp, StellarSetOptionsOp, ) @@ -48,7 +50,9 @@ def write_create_account_op(w: Writer, msg: StellarCreateAccountOp) -> None: writers.write_uint64(w, msg.starting_balance) -def write_create_passive_offer_op(w: Writer, msg: StellarCreatePassiveOfferOp) -> None: +def write_create_passive_sell_offer_op( + w: Writer, msg: StellarCreatePassiveSellOfferOp +) -> None: _write_asset(w, msg.selling_asset) _write_asset(w, msg.buying_asset) writers.write_uint64(w, msg.amount) @@ -65,16 +69,28 @@ def write_manage_data_op(w: Writer, msg: StellarManageDataOp) -> None: writers.write_string(w, msg.value) -def write_manage_offer_op(w: Writer, msg: StellarManageOfferOp) -> None: +def write_manage_buy_offer_op(w: Writer, msg: StellarManageBuyOfferOp) -> None: + _write_manage_offer_op_common(w, msg) + + +def write_manage_sell_offer_op(w: Writer, msg: StellarManageSellOfferOp) -> None: + _write_manage_offer_op_common(w, msg) + + +def _write_manage_offer_op_common( + w: Writer, msg: StellarManageSellOfferOp | StellarManageBuyOfferOp +) -> None: _write_asset(w, msg.selling_asset) _write_asset(w, msg.buying_asset) - writers.write_uint64(w, msg.amount) # amount to sell + writers.write_uint64(w, msg.amount) # amount to sell / buy writers.write_uint32(w, msg.price_n) # numerator writers.write_uint32(w, msg.price_d) # denominator writers.write_uint64(w, msg.offer_id) -def write_path_payment_op(w: Writer, msg: StellarPathPaymentOp) -> None: +def write_path_payment_strict_receive_op( + w: Writer, msg: StellarPathPaymentStrictReceiveOp +) -> None: _write_asset(w, msg.send_asset) writers.write_uint64(w, msg.send_max) writers.write_pubkey(w, msg.destination_account) @@ -86,6 +102,20 @@ def write_path_payment_op(w: Writer, msg: StellarPathPaymentOp) -> None: _write_asset(w, p) +def write_path_payment_strict_send_op( + w: Writer, msg: StellarPathPaymentStrictSendOp +) -> None: + _write_asset(w, msg.send_asset) + writers.write_uint64(w, msg.send_amount) + writers.write_pubkey(w, msg.destination_account) + + _write_asset(w, msg.destination_asset) + writers.write_uint64(w, msg.destination_min) + writers.write_uint32(w, len(msg.paths)) + for p in msg.paths: + _write_asset(w, p) + + def write_payment_op(w: Writer, msg: StellarPaymentOp) -> None: writers.write_pubkey(w, msg.destination_account) _write_asset(w, msg.asset) diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index dec7d1ddf4f..8d9ea143eb0 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -121,15 +121,17 @@ StellarAddress = 208 StellarCreateAccountOp = 210 StellarPaymentOp = 211 - StellarPathPaymentOp = 212 - StellarManageOfferOp = 213 - StellarCreatePassiveOfferOp = 214 + StellarPathPaymentStrictReceiveOp = 212 + StellarManageSellOfferOp = 213 + StellarCreatePassiveSellOfferOp = 214 StellarSetOptionsOp = 215 StellarChangeTrustOp = 216 StellarAllowTrustOp = 217 StellarAccountMergeOp = 218 StellarManageDataOp = 220 StellarBumpSequenceOp = 221 + StellarManageBuyOfferOp = 222 + StellarPathPaymentStrictSendOp = 223 StellarSignedTx = 230 CardanoSignTx = 303 CardanoGetPublicKey = 305 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 1f5b23d3f32..babfefc3824 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -126,15 +126,17 @@ class MessageType(IntEnum): StellarAddress = 208 StellarCreateAccountOp = 210 StellarPaymentOp = 211 - StellarPathPaymentOp = 212 - StellarManageOfferOp = 213 - StellarCreatePassiveOfferOp = 214 + StellarPathPaymentStrictReceiveOp = 212 + StellarManageSellOfferOp = 213 + StellarCreatePassiveSellOfferOp = 214 StellarSetOptionsOp = 215 StellarChangeTrustOp = 216 StellarAllowTrustOp = 217 StellarAccountMergeOp = 218 StellarManageDataOp = 220 StellarBumpSequenceOp = 221 + StellarManageBuyOfferOp = 222 + StellarPathPaymentStrictSendOp = 223 StellarSignedTx = 230 CardanoSignTx = 303 CardanoGetPublicKey = 305 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 289776c986f..82c9294a578 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -4772,7 +4772,7 @@ def __init__( def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarCreateAccountOp"]: return isinstance(msg, cls) - class StellarPathPaymentOp(protobuf.MessageType): + class StellarPathPaymentStrictReceiveOp(protobuf.MessageType): source_account: "str | None" send_asset: "StellarAsset" send_max: "int" @@ -4795,10 +4795,62 @@ def __init__( pass @classmethod - def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarPathPaymentOp"]: + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarPathPaymentStrictReceiveOp"]: return isinstance(msg, cls) - class StellarManageOfferOp(protobuf.MessageType): + class StellarPathPaymentStrictSendOp(protobuf.MessageType): + source_account: "str | None" + send_asset: "StellarAsset" + send_amount: "int" + destination_account: "str" + destination_asset: "StellarAsset" + destination_min: "int" + paths: "list[StellarAsset]" + + def __init__( + self, + *, + send_asset: "StellarAsset", + send_amount: "int", + destination_account: "str", + destination_asset: "StellarAsset", + destination_min: "int", + paths: "list[StellarAsset] | None" = None, + source_account: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarPathPaymentStrictSendOp"]: + return isinstance(msg, cls) + + class StellarManageSellOfferOp(protobuf.MessageType): + source_account: "str | None" + selling_asset: "StellarAsset" + buying_asset: "StellarAsset" + amount: "int" + price_n: "int" + price_d: "int" + offer_id: "int" + + def __init__( + self, + *, + selling_asset: "StellarAsset", + buying_asset: "StellarAsset", + amount: "int", + price_n: "int", + price_d: "int", + offer_id: "int", + source_account: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarManageSellOfferOp"]: + return isinstance(msg, cls) + + class StellarManageBuyOfferOp(protobuf.MessageType): source_account: "str | None" selling_asset: "StellarAsset" buying_asset: "StellarAsset" @@ -4821,10 +4873,10 @@ def __init__( pass @classmethod - def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarManageOfferOp"]: + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarManageBuyOfferOp"]: return isinstance(msg, cls) - class StellarCreatePassiveOfferOp(protobuf.MessageType): + class StellarCreatePassiveSellOfferOp(protobuf.MessageType): source_account: "str | None" selling_asset: "StellarAsset" buying_asset: "StellarAsset" @@ -4845,7 +4897,7 @@ def __init__( pass @classmethod - def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarCreatePassiveOfferOp"]: + def is_type_of(cls, msg: protobuf.MessageType) -> TypeGuard["StellarCreatePassiveSellOfferOp"]: return isinstance(msg, cls) class StellarSetOptionsOp(protobuf.MessageType): diff --git a/legacy/firmware/.changelog.d/1838.added b/legacy/firmware/.changelog.d/1838.added new file mode 100644 index 00000000000..43800dd7673 --- /dev/null +++ b/legacy/firmware/.changelog.d/1838.added @@ -0,0 +1 @@ +Stellar: add support for StellarManageBuyOfferOp and StellarPathPaymentStrictSendOp. diff --git a/legacy/firmware/.changelog.d/1838.incompatible b/legacy/firmware/.changelog.d/1838.incompatible new file mode 100644 index 00000000000..3c6f73c6d6b --- /dev/null +++ b/legacy/firmware/.changelog.d/1838.incompatible @@ -0,0 +1 @@ +Stellar: rename StellarManageOfferOp to StellarManageSellOfferOp, StellarPathPaymentOp to StellarPathPaymentStrictReceiveOp and StellarCreatePassiveOfferOp to StellarCreatePassiveSellOfferOp. diff --git a/legacy/firmware/fsm.h b/legacy/firmware/fsm.h index 156057c87bd..bffb24ad8f0 100644 --- a/legacy/firmware/fsm.h +++ b/legacy/firmware/fsm.h @@ -118,9 +118,14 @@ void fsm_msgStellarGetAddress(const StellarGetAddress *msg); void fsm_msgStellarSignTx(const StellarSignTx *msg); void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg); void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg); -void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg); -void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg); -void fsm_msgStellarCreatePassiveOfferOp(const StellarCreatePassiveOfferOp *msg); +void fsm_msgStellarPathPaymentStrictReceiveOp( + const StellarPathPaymentStrictReceiveOp *msg); +void fsm_msgStellarPathPaymentStrictSendOp( + const StellarPathPaymentStrictSendOp *msg); +void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg); +void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg); +void fsm_msgStellarCreatePassiveSellOfferOp( + const StellarCreatePassiveSellOfferOp *msg); void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg); void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg); void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg); diff --git a/legacy/firmware/fsm_msg_stellar.h b/legacy/firmware/fsm_msg_stellar.h index 06582de135f..5eb4e41ac34 100644 --- a/legacy/firmware/fsm_msg_stellar.h +++ b/legacy/firmware/fsm_msg_stellar.h @@ -109,8 +109,9 @@ void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) { } } -void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg) { - if (!stellar_confirmPathPaymentOp(msg)) return; +void fsm_msgStellarPathPaymentStrictReceiveOp( + const StellarPathPaymentStrictReceiveOp *msg) { + if (!stellar_confirmPathPaymentStrictReceiveOp(msg)) return; if (stellar_allOperationsConfirmed()) { RESP_INIT(StellarSignedTx); @@ -127,8 +128,9 @@ void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg) { } } -void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg) { - if (!stellar_confirmManageOfferOp(msg)) return; +void fsm_msgStellarPathPaymentStrictSendOp( + const StellarPathPaymentStrictSendOp *msg) { + if (!stellar_confirmPathPaymentStrictSendOp(msg)) return; if (stellar_allOperationsConfirmed()) { RESP_INIT(StellarSignedTx); @@ -145,9 +147,45 @@ void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg) { } } -void fsm_msgStellarCreatePassiveOfferOp( - const StellarCreatePassiveOfferOp *msg) { - if (!stellar_confirmCreatePassiveOfferOp(msg)) return; +void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg) { + if (!stellar_confirmManageBuyOfferOp(msg)) return; + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg) { + if (!stellar_confirmManageSellOfferOp(msg)) return; + + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } +} + +void fsm_msgStellarCreatePassiveSellOfferOp( + const StellarCreatePassiveSellOfferOp *msg) { + if (!stellar_confirmCreatePassiveSellOfferOp(msg)) return; if (stellar_allOperationsConfirmed()) { RESP_INIT(StellarSignedTx); diff --git a/legacy/firmware/protob/messages-stellar.options b/legacy/firmware/protob/messages-stellar.options index d76de77a5ed..1e26c4827e4 100644 --- a/legacy/firmware/protob/messages-stellar.options +++ b/legacy/firmware/protob/messages-stellar.options @@ -17,14 +17,19 @@ StellarPaymentOp.destination_account max_size:57 StellarCreateAccountOp.source_account max_size:57 StellarCreateAccountOp.new_account max_size:57 -StellarPathPaymentOp.source_account max_size:57 -StellarPathPaymentOp.destination_account max_size:57 -StellarPathPaymentOp.paths max_count:5 +StellarPathPaymentStrictReceiveOp.source_account max_size:57 +StellarPathPaymentStrictReceiveOp.destination_account max_size:57 +StellarPathPaymentStrictReceiveOp.paths max_count:5 +StellarPathPaymentStrictSendOp.source_account max_size:57 +StellarPathPaymentStrictSendOp.destination_account max_size:57 +StellarPathPaymentStrictSendOp.paths max_count:5 -StellarManageOfferOp.source_account max_size:57 +StellarManageBuyOfferOp.source_account max_size:57 -StellarCreatePassiveOfferOp.source_account max_size:57 +StellarManageSellOfferOp.source_account max_size:57 + +StellarCreatePassiveSellOfferOp.source_account max_size:57 StellarSetOptionsOp.source_account max_size:57 StellarSetOptionsOp.inflation_destination_account max_size:57 diff --git a/legacy/firmware/stellar.c b/legacy/firmware/stellar.c index c09c543527c..886a68d624e 100644 --- a/legacy/firmware/stellar.c +++ b/legacy/firmware/stellar.c @@ -289,7 +289,8 @@ bool stellar_confirmPaymentOp(const StellarPaymentOp *msg) { return true; } -bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg) { +bool stellar_confirmPathPaymentStrictReceiveOp( + const StellarPathPaymentStrictReceiveOp *msg) { if (!stellar_signing) return false; if (!stellar_confirmSourceAccount(msg->has_source_account, @@ -355,9 +356,9 @@ bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg) { strlcpy(str_source_amount, _("Pay Using "), sizeof(str_source_amount)); strlcat(str_source_amount, str_source_number, sizeof(str_source_amount)); - stellar_layoutTransactionDialog(str_source_amount, str_send_asset, NULL, - _("This is the amount debited"), - _("from your account.")); + stellar_layoutTransactionDialog(str_source_amount, str_send_asset, + _("This is the max"), + _("amount debited from your"), _("account.")); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { stellar_signingAbort(_("User canceled")); return false; @@ -387,7 +388,192 @@ bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg) { return true; } -bool stellar_confirmManageOfferOp(const StellarManageOfferOp *msg) { +bool stellar_confirmPathPaymentStrictSendOp( + const StellarPathPaymentStrictSendOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(13); + + // Validate destination account and convert to bytes + uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0}; + if (!stellar_getAddressBytes(msg->destination_account, + destination_account_bytes)) { + stellar_signingAbort(_("Invalid destination account")); + return false; + } + const char **str_dest_rows = + stellar_lineBreakAddress(destination_account_bytes); + + // To: G... + char str_to[32] = {0}; + strlcpy(str_to, _("To: "), sizeof(str_to)); + strlcat(str_to, str_dest_rows[0], sizeof(str_to)); + + char str_send_asset[32] = {0}; + char str_dest_asset[32] = {0}; + stellar_format_asset(&(msg->send_asset), str_send_asset, + sizeof(str_send_asset)); + stellar_format_asset(&(msg->destination_asset), str_dest_asset, + sizeof(str_dest_asset)); + + char str_pay_amount[32] = {0}; + char str_amount[32] = {0}; + stellar_format_stroops(msg->destination_min, str_amount, sizeof(str_amount)); + + strlcat(str_pay_amount, str_amount, sizeof(str_pay_amount)); + + // Confirm what the receiver will get + /* + Path Pay at least + 100.0000000 + JPY (G1234ABCDEF) + To: G.... + .... + .... + */ + stellar_layoutTransactionDialog(_("Path Pay at least"), str_pay_amount, + str_dest_asset, str_to, str_dest_rows[1]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Confirm what the sender is using to pay + char str_source_amount[32] = {0}; + char str_source_number[32] = {0}; + stellar_format_stroops(msg->send_amount, str_source_number, + sizeof(str_source_number)); + + strlcpy(str_source_amount, _("Pay Using "), sizeof(str_source_amount)); + strlcat(str_source_amount, str_source_number, sizeof(str_source_amount)); + + stellar_layoutTransactionDialog( + str_dest_rows[2], str_source_amount, str_send_asset, + _("This is the amount debited"), _("from your account.")); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + // Note: no confirmation for intermediate steps since they don't impact the + // user + + // Hash send asset + stellar_hashupdate_asset(&(msg->send_asset)); + // send amount (signed vs. unsigned doesn't matter wrt hashing) + stellar_hashupdate_uint64(msg->send_amount); + // destination account + stellar_hashupdate_address(destination_account_bytes); + // destination asset + stellar_hashupdate_asset(&(msg->destination_asset)); + // destination amount + stellar_hashupdate_uint64(msg->destination_min); + + // paths are stored as an array so hash the number of elements as a uint32 + stellar_hashupdate_uint32(msg->paths_count); + for (uint8_t i = 0; i < msg->paths_count; i++) { + stellar_hashupdate_asset(&(msg->paths[i])); + } + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} + +bool stellar_confirmManageBuyOfferOp(const StellarManageBuyOfferOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(12); + + // New Offer / Delete #123 / Update #123 + char str_offer[32] = {0}; + if (msg->offer_id == 0) { + strlcpy(str_offer, _("New Offer"), sizeof(str_offer)); + } else { + char str_offer_id[20] = {0}; + stellar_format_uint64(msg->offer_id, str_offer_id, sizeof(str_offer_id)); + + if (msg->amount == 0) { + strlcpy(str_offer, _("Delete #"), sizeof(str_offer)); + } else { + strlcpy(str_offer, _("Update #"), sizeof(str_offer)); + } + + strlcat(str_offer, str_offer_id, sizeof(str_offer)); + } + + char str_buying[32] = {0}; + char str_buying_amount[32] = {0}; + char str_buying_asset[32] = {0}; + + stellar_format_asset(&(msg->buying_asset), str_buying_asset, + sizeof(str_buying_asset)); + stellar_format_stroops(msg->amount, str_buying_amount, + sizeof(str_buying_amount)); + + /* + Buy 200 + XLM (Native Asset) + */ + strlcpy(str_buying, _("Buy "), sizeof(str_buying)); + strlcat(str_buying, str_buying_amount, sizeof(str_buying)); + + char str_selling[32] = {0}; + char str_selling_asset[32] = {0}; + char str_price[32] = {0}; + + stellar_format_asset(&(msg->selling_asset), str_selling_asset, + sizeof(str_selling_asset)); + stellar_format_price(msg->price_n, msg->price_d, str_price, + sizeof(str_price)); + + /* + For 0.675952 Per + USD (G12345678) + */ + strlcpy(str_selling, _("For "), sizeof(str_selling)); + strlcat(str_selling, str_price, sizeof(str_selling)); + strlcat(str_selling, _(" Per"), sizeof(str_selling)); + + stellar_layoutTransactionDialog(str_offer, str_buying, str_buying_asset, + str_selling, str_selling_asset); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash selling asset + stellar_hashupdate_asset(&(msg->selling_asset)); + // buying asset + stellar_hashupdate_asset(&(msg->buying_asset)); + // amount to buy (signed vs. unsigned doesn't matter wrt hashing) + stellar_hashupdate_uint64(msg->amount); + // numerator + stellar_hashupdate_uint32(msg->price_n); + // denominator + stellar_hashupdate_uint32(msg->price_d); + // offer ID + stellar_hashupdate_uint64(msg->offer_id); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} + +bool stellar_confirmManageSellOfferOp(const StellarManageSellOfferOp *msg) { if (!stellar_signing) return false; if (!stellar_confirmSourceAccount(msg->has_source_account, @@ -473,8 +659,8 @@ bool stellar_confirmManageOfferOp(const StellarManageOfferOp *msg) { return true; } -bool stellar_confirmCreatePassiveOfferOp( - const StellarCreatePassiveOfferOp *msg) { +bool stellar_confirmCreatePassiveSellOfferOp( + const StellarCreatePassiveSellOfferOp *msg) { if (!stellar_signing) return false; if (!stellar_confirmSourceAccount(msg->has_source_account, diff --git a/legacy/firmware/stellar.h b/legacy/firmware/stellar.h index 5f67d55219c..7ba4e8353ff 100644 --- a/legacy/firmware/stellar.h +++ b/legacy/firmware/stellar.h @@ -59,10 +59,14 @@ bool stellar_confirmSourceAccount(bool has_source_account, const char *str_account); bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg); bool stellar_confirmPaymentOp(const StellarPaymentOp *msg); -bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg); -bool stellar_confirmManageOfferOp(const StellarManageOfferOp *msg); -bool stellar_confirmCreatePassiveOfferOp( - const StellarCreatePassiveOfferOp *msg); +bool stellar_confirmPathPaymentStrictReceiveOp( + const StellarPathPaymentStrictReceiveOp *msg); +bool stellar_confirmPathPaymentStrictSendOp( + const StellarPathPaymentStrictSendOp *msg); +bool stellar_confirmManageBuyOfferOp(const StellarManageBuyOfferOp *msg); +bool stellar_confirmManageSellOfferOp(const StellarManageSellOfferOp *msg); +bool stellar_confirmCreatePassiveSellOfferOp( + const StellarCreatePassiveSellOfferOp *msg); bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg); bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg); bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg); diff --git a/python/.changelog.d/1838.changed b/python/.changelog.d/1838.changed new file mode 100644 index 00000000000..5589843da0b --- /dev/null +++ b/python/.changelog.d/1838.changed @@ -0,0 +1 @@ +`trezorlib.stellar` will refuse to process transactions containing MuxedAccount diff --git a/python/requirements-optional.txt b/python/requirements-optional.txt index cd93c76756d..21a4f936764 100644 --- a/python/requirements-optional.txt +++ b/python/requirements-optional.txt @@ -2,4 +2,4 @@ hidapi >= 0.7.99.post20 rlp >= 1.1.0 web3 >= 4.8 Pillow -stellar-sdk>=4.0.0,<5.0.0 +stellar-sdk>=4.0.0,<6.0.0 diff --git a/python/setup.py b/python/setup.py index 3c49b9ada9a..4253102400b 100755 --- a/python/setup.py +++ b/python/setup.py @@ -22,7 +22,7 @@ "ethereum": ["rlp>=1.1.0", "web3>=4.8"], "qt-widgets": ["PyQt5"], "extra": ["Pillow"], - "stellar": ["stellar-sdk>=4.0.0,<5.0.0"], + "stellar": ["stellar-sdk>=4.0.0,<6.0.0"], } extras_require["full"] = sum(extras_require.values(), []) diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index c9cf4e5c865..484e78c40ce 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -147,15 +147,17 @@ class MessageType(IntEnum): StellarAddress = 208 StellarCreateAccountOp = 210 StellarPaymentOp = 211 - StellarPathPaymentOp = 212 - StellarManageOfferOp = 213 - StellarCreatePassiveOfferOp = 214 + StellarPathPaymentStrictReceiveOp = 212 + StellarManageSellOfferOp = 213 + StellarCreatePassiveSellOfferOp = 214 StellarSetOptionsOp = 215 StellarChangeTrustOp = 216 StellarAllowTrustOp = 217 StellarAccountMergeOp = 218 StellarManageDataOp = 220 StellarBumpSequenceOp = 221 + StellarManageBuyOfferOp = 222 + StellarPathPaymentStrictSendOp = 223 StellarSignedTx = 230 CardanoSignTx = 303 CardanoGetPublicKey = 305 @@ -6324,7 +6326,7 @@ def __init__( self.source_account = source_account -class StellarPathPaymentOp(protobuf.MessageType): +class StellarPathPaymentStrictReceiveOp(protobuf.MessageType): MESSAGE_WIRE_TYPE = 212 FIELDS = { 1: protobuf.Field("source_account", "string", repeated=False, required=False), @@ -6356,7 +6358,39 @@ def __init__( self.source_account = source_account -class StellarManageOfferOp(protobuf.MessageType): +class StellarPathPaymentStrictSendOp(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 223 + FIELDS = { + 1: protobuf.Field("source_account", "string", repeated=False, required=False), + 2: protobuf.Field("send_asset", "StellarAsset", repeated=False, required=True), + 3: protobuf.Field("send_amount", "sint64", repeated=False, required=True), + 4: protobuf.Field("destination_account", "string", repeated=False, required=True), + 5: protobuf.Field("destination_asset", "StellarAsset", repeated=False, required=True), + 6: protobuf.Field("destination_min", "sint64", repeated=False, required=True), + 7: protobuf.Field("paths", "StellarAsset", repeated=True, required=False), + } + + def __init__( + self, + *, + send_asset: "StellarAsset", + send_amount: "int", + destination_account: "str", + destination_asset: "StellarAsset", + destination_min: "int", + paths: Optional[List["StellarAsset"]] = None, + source_account: Optional["str"] = None, + ) -> None: + self.paths = paths if paths is not None else [] + self.send_asset = send_asset + self.send_amount = send_amount + self.destination_account = destination_account + self.destination_asset = destination_asset + self.destination_min = destination_min + self.source_account = source_account + + +class StellarManageSellOfferOp(protobuf.MessageType): MESSAGE_WIRE_TYPE = 213 FIELDS = { 1: protobuf.Field("source_account", "string", repeated=False, required=False), @@ -6388,7 +6422,39 @@ def __init__( self.source_account = source_account -class StellarCreatePassiveOfferOp(protobuf.MessageType): +class StellarManageBuyOfferOp(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 222 + FIELDS = { + 1: protobuf.Field("source_account", "string", repeated=False, required=False), + 2: protobuf.Field("selling_asset", "StellarAsset", repeated=False, required=True), + 3: protobuf.Field("buying_asset", "StellarAsset", repeated=False, required=True), + 4: protobuf.Field("amount", "sint64", repeated=False, required=True), + 5: protobuf.Field("price_n", "uint32", repeated=False, required=True), + 6: protobuf.Field("price_d", "uint32", repeated=False, required=True), + 7: protobuf.Field("offer_id", "uint64", repeated=False, required=True), + } + + def __init__( + self, + *, + selling_asset: "StellarAsset", + buying_asset: "StellarAsset", + amount: "int", + price_n: "int", + price_d: "int", + offer_id: "int", + source_account: Optional["str"] = None, + ) -> None: + self.selling_asset = selling_asset + self.buying_asset = buying_asset + self.amount = amount + self.price_n = price_n + self.price_d = price_d + self.offer_id = offer_id + self.source_account = source_account + + +class StellarCreatePassiveSellOfferOp(protobuf.MessageType): MESSAGE_WIRE_TYPE = 214 FIELDS = { 1: protobuf.Field("source_account", "string", repeated=False, required=False), diff --git a/python/src/trezorlib/stellar.py b/python/src/trezorlib/stellar.py index b664e55d006..d6c4ad156c1 100644 --- a/python/src/trezorlib/stellar.py +++ b/python/src/trezorlib/stellar.py @@ -35,6 +35,7 @@ NoneMemo, Operation, PathPaymentStrictReceive, + PathPaymentStrictSend, Payment, ReturnHashMemo, SetOptions, @@ -43,6 +44,8 @@ TrustLineEntryFlag, Price, Network, + ManageBuyOffer, + MuxedAccount, ) from stellar_sdk.xdr.signer_key_type import SignerKeyType @@ -90,6 +93,7 @@ def from_envelope(envelope: "TransactionEnvelope"): else: raise ValueError("Unsupported memo type") + _raise_if_account_muxed_id_exists(parsed_tx.source) tx = messages.StellarSignTx( source_account=parsed_tx.source.account_id, fee=parsed_tx.fee, @@ -111,6 +115,7 @@ def from_envelope(envelope: "TransactionEnvelope"): def _read_operation(op: "Operation"): # TODO: Let's add muxed account support later. if op.source: + _raise_if_account_muxed_id_exists(op.source) source_account = op.source.account_id else: source_account = None @@ -121,6 +126,7 @@ def _read_operation(op: "Operation"): starting_balance=_read_amount(op.starting_balance), ) if isinstance(op, Payment): + _raise_if_account_muxed_id_exists(op.destination) return messages.StellarPaymentOp( source_account=source_account, destination_account=op.destination.account_id, @@ -128,7 +134,8 @@ def _read_operation(op: "Operation"): amount=_read_amount(op.amount), ) if isinstance(op, PathPaymentStrictReceive): - operation = messages.StellarPathPaymentOp( + _raise_if_account_muxed_id_exists(op.destination) + operation = messages.StellarPathPaymentStrictReceiveOp( source_account=source_account, send_asset=_read_asset(op.send_asset), send_max=_read_amount(op.send_max), @@ -140,7 +147,7 @@ def _read_operation(op: "Operation"): return operation if isinstance(op, ManageSellOffer): price = _read_price(op.price) - return messages.StellarManageOfferOp( + return messages.StellarManageSellOfferOp( source_account=source_account, selling_asset=_read_asset(op.selling), buying_asset=_read_asset(op.buying), @@ -151,7 +158,7 @@ def _read_operation(op: "Operation"): ) if isinstance(op, CreatePassiveSellOffer): price = _read_price(op.price) - return messages.StellarCreatePassiveOfferOp( + return messages.StellarCreatePassiveSellOfferOp( source_account=source_account, selling_asset=_read_asset(op.selling), buying_asset=_read_asset(op.buying), @@ -210,6 +217,7 @@ def _read_operation(op: "Operation"): is_authorized=bool(op.authorize.value), ) if isinstance(op, AccountMerge): + _raise_if_account_muxed_id_exists(op.destination) return messages.StellarAccountMergeOp( source_account=source_account, destination_account=op.destination.account_id, @@ -225,15 +233,45 @@ def _read_operation(op: "Operation"): return messages.StellarBumpSequenceOp( source_account=source_account, bump_to=op.bump_to ) + if isinstance(op, ManageBuyOffer): + price = _read_price(op.price) + return messages.StellarManageBuyOfferOp( + source_account=source_account, + selling_asset=_read_asset(op.selling), + buying_asset=_read_asset(op.buying), + amount=_read_amount(op.amount), + price_n=price.n, + price_d=price.d, + offer_id=op.offer_id, + ) + if isinstance(op, PathPaymentStrictSend): + _raise_if_account_muxed_id_exists(op.destination) + operation = messages.StellarPathPaymentStrictSendOp( + source_account=source_account, + send_asset=_read_asset(op.send_asset), + send_amount=_read_amount(op.send_amount), + destination_account=op.destination.account_id, + destination_asset=_read_asset(op.dest_asset), + destination_min=_read_amount(op.dest_min), + paths=[_read_asset(asset) for asset in op.path], + ) + return operation raise ValueError(f"Unknown operation type: {op.__class__.__name__}") +def _raise_if_account_muxed_id_exists(account: "MuxedAccount"): + # Currently Trezor firmware does not support MuxedAccount, + # so we throw an exception here. + if account.account_muxed_id is not None: + raise ValueError("MuxedAccount is not supported") + + def _read_amount(amount: str) -> int: return Operation.to_xdr_amount(amount) def _read_price(price: Union["Price", str, Decimal]) -> "Price": - # In the coming stellar-sdk 5.x, the type of price must be Price, + # In the coming stellar-sdk 6.x, the type of price must be Price, # at that time we can remove this function if isinstance(price, Price): return price diff --git a/python/tests/test_stellar.py b/python/tests/test_stellar.py index e0b9d254c1e..f6667b32e12 100644 --- a/python/tests/test_stellar.py +++ b/python/tests/test_stellar.py @@ -14,7 +14,14 @@ # You should have received a copy of the License along with this library. # If not, see . import pytest -from stellar_sdk import Account, Asset, Network, TransactionBuilder, TrustLineEntryFlag +from stellar_sdk import ( + Account, + Asset, + Network, + TransactionBuilder, + TrustLineEntryFlag, + MuxedAccount, +) from stellar_sdk.strkey import StrKey from trezorlib import messages, stellar @@ -325,11 +332,12 @@ def test_path_payment_strict_receive(): tx, operations = stellar.from_envelope(envelope) assert len(operations) == 1 - assert isinstance(operations[0], messages.StellarPathPaymentOp) + assert isinstance(operations[0], messages.StellarPathPaymentStrictReceiveOp) assert operations[0].source_account == operation_source assert operations[0].destination_account == destination assert operations[0].send_asset.type == messages.StellarAssetType.NATIVE assert operations[0].send_max == 500111000 + assert operations[0].destination_amount == 1000000000 assert operations[0].destination_asset.type == messages.StellarAssetType.ALPHANUM4 assert operations[0].destination_asset.code == dest_code assert operations[0].destination_asset.issuer == dest_issuer @@ -368,7 +376,7 @@ def test_manage_sell_offer_new_offer(): tx, operations = stellar.from_envelope(envelope) assert len(operations) == 1 - assert isinstance(operations[0], messages.StellarManageOfferOp) + assert isinstance(operations[0], messages.StellarManageSellOfferOp) assert operations[0].source_account == operation_source assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 @@ -408,7 +416,7 @@ def test_manage_sell_offer_update_offer(): tx, operations = stellar.from_envelope(envelope) assert len(operations) == 1 - assert isinstance(operations[0], messages.StellarManageOfferOp) + assert isinstance(operations[0], messages.StellarManageSellOfferOp) assert operations[0].source_account == operation_source assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 @@ -446,7 +454,7 @@ def test_create_passive_sell_offer(): tx, operations = stellar.from_envelope(envelope) assert len(operations) == 1 - assert isinstance(operations[0], messages.StellarCreatePassiveOfferOp) + assert isinstance(operations[0], messages.StellarCreatePassiveSellOfferOp) assert operations[0].source_account == operation_source assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 @@ -712,3 +720,287 @@ def test_bump_sequence(): assert isinstance(operations[0], messages.StellarBumpSequenceOp) assert operations[0].source_account == operation_source assert operations[0].bump_to == bump_to + + +def test_manage_buy_offer_new_offer(): + tx = make_default_tx() + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = tx.append_manage_buy_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + source=operation_source, + ).build() + + tx, operations = stellar.from_envelope(envelope) + assert len(operations) == 1 + assert isinstance(operations[0], messages.StellarManageBuyOfferOp) + assert operations[0].source_account == operation_source + assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE + assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 + assert operations[0].buying_asset.code == buying_code + assert operations[0].buying_asset.issuer == buying_issuer + assert operations[0].amount == 500111000 + assert operations[0].price_n == 1 + assert operations[0].price_d == 2 + assert operations[0].offer_id == 0 # indicates a new offer + + +def test_manage_buy_offer_update_offer(): + tx = make_default_tx() + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + offer_id = 12345 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = tx.append_manage_buy_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + offer_id=offer_id, + source=operation_source, + ).build() + + tx, operations = stellar.from_envelope(envelope) + assert len(operations) == 1 + assert isinstance(operations[0], messages.StellarManageBuyOfferOp) + assert operations[0].source_account == operation_source + assert operations[0].selling_asset.type == messages.StellarAssetType.NATIVE + assert operations[0].buying_asset.type == messages.StellarAssetType.ALPHANUM4 + assert operations[0].buying_asset.code == buying_code + assert operations[0].buying_asset.issuer == buying_issuer + assert operations[0].amount == 500111000 + assert operations[0].price_n == 1 + assert operations[0].price_d == 2 + assert operations[0].offer_id == offer_id + + +def test_path_payment_strict_send(): + tx = make_default_tx() + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + send_amount = "50.0112" + dest_min = "120" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + path_asset1 = Asset( + "JPY", "GD6PV7DXQJX7AGVXFQ2MTCLTCH6LR3E6IO2EO2YDZD7F7IOZZCCB5DSQ" + ) + path_asset2 = Asset( + "BANANA", "GC7EKO37HNSKQ3V6RZ274EO7SFOWASQRHLX3OR5FIZK6UMV6LIEDXHGZ" + ) + + envelope = ( + tx + .append_path_payment_strict_send_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_amount=send_amount, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_min=dest_min, + path=[path_asset1, path_asset2], + source=operation_source, + ) + .build() + ) + + tx, operations = stellar.from_envelope(envelope) + assert len(operations) == 1 + + assert isinstance(operations[0], messages.StellarPathPaymentStrictSendOp) + assert operations[0].source_account == operation_source + assert operations[0].destination_account == destination + assert operations[0].send_asset.type == messages.StellarAssetType.NATIVE + assert operations[0].send_amount == 500112000 + assert operations[0].destination_min == 1200000000 + assert operations[0].destination_asset.type == messages.StellarAssetType.ALPHANUM4 + assert operations[0].destination_asset.code == dest_code + assert operations[0].destination_asset.issuer == dest_issuer + assert len(operations[0].paths) == 2 + assert operations[0].paths[0].type == messages.StellarAssetType.ALPHANUM4 + assert operations[0].paths[0].code == path_asset1.code + assert operations[0].paths[0].issuer == path_asset1.issuer + assert operations[0].paths[1].type == messages.StellarAssetType.ALPHANUM12 + assert operations[0].paths[1].code == path_asset2.code + assert operations[0].paths[1].issuer == path_asset2.issuer + + +def test_payment_muxed_account_not_support_raise(): + tx = make_default_tx() + destination = MuxedAccount( + "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", 1 + ) + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = tx.append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ).build() + + with pytest.raises(ValueError, match="MuxedAccount is not supported"): + stellar.from_envelope(envelope) + + +def test_path_payment_strict_send_muxed_account_not_support_raise(): + tx = make_default_tx() + destination = MuxedAccount( + "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", 1 + ) + send_amount = "50.0112" + dest_min = "120" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + path_asset1 = Asset( + "JPY", "GD6PV7DXQJX7AGVXFQ2MTCLTCH6LR3E6IO2EO2YDZD7F7IOZZCCB5DSQ" + ) + path_asset2 = Asset( + "BANANA", "GC7EKO37HNSKQ3V6RZ274EO7SFOWASQRHLX3OR5FIZK6UMV6LIEDXHGZ" + ) + + envelope = tx.append_path_payment_strict_send_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_amount=send_amount, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_min=dest_min, + path=[path_asset1, path_asset2], + source=operation_source, + ).build() + + with pytest.raises(ValueError, match="MuxedAccount is not supported"): + stellar.from_envelope(envelope) + + +def test_path_payment_strict_receive_muxed_account_not_support_raise(): + tx = make_default_tx() + destination = MuxedAccount( + "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", 1 + ) + send_max = "50.0111" + dest_amount = "100" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + path_asset1 = Asset( + "JPY", "GD6PV7DXQJX7AGVXFQ2MTCLTCH6LR3E6IO2EO2YDZD7F7IOZZCCB5DSQ" + ) + path_asset2 = Asset( + "BANANA", "GC7EKO37HNSKQ3V6RZ274EO7SFOWASQRHLX3OR5FIZK6UMV6LIEDXHGZ" + ) + + envelope = tx.append_path_payment_strict_receive_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_max=send_max, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_amount=dest_amount, + path=[path_asset1, path_asset2], + source=operation_source, + ).build() + + with pytest.raises(ValueError, match="MuxedAccount is not supported"): + stellar.from_envelope(envelope) + + +def test_account_merge_muxed_account_not_support_raise(): + tx = make_default_tx() + destination = MuxedAccount( + "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6", 1 + ) + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = tx.append_account_merge_op( + destination=destination, source=operation_source + ).build() + + with pytest.raises(ValueError, match="MuxedAccount is not supported"): + stellar.from_envelope(envelope) + + +def test_op_source_muxed_account_not_support_raise(): + tx = make_default_tx() + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation_source = MuxedAccount( + "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V", 2 + ) + + envelope = tx.append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ).build() + + with pytest.raises(ValueError, match="MuxedAccount is not supported"): + stellar.from_envelope(envelope) + + +def test_tx_source_muxed_account_not_support_raise(): + source_account = Account( + account_id=MuxedAccount(TX_SOURCE, 123456), sequence=SEQUENCE + ) + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, + base_fee=BASE_FEE, + ) + .add_time_bounds(TIMEBOUNDS_START, TIMEBOUNDS_END) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() + ) + + with pytest.raises(ValueError, match="MuxedAccount is not supported"): + stellar.from_envelope(envelope) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 9bef01a9dc3..381e442626c 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -844,10 +844,12 @@ "test_stellar.py::test_sign_tx[StellarChangeTrustOp-add]": "7a8d420121a13e34f6ca7ad224e01a3b189ceb8e8d104cf9ec9b329222670936", "test_stellar.py::test_sign_tx[StellarChangeTrustOp-delete]": "ec26e6883ba9405412674431e0ec07504b3f25682ce7d76ebd445b809194198a", "test_stellar.py::test_sign_tx[StellarCreateAccountOp]": "2582717c25974d2b3ee156624b00375148ff7fd12eeea73625a7c367fa610373", -"test_stellar.py::test_sign_tx[StellarCreatePassiveOfferOp]": "6b0f0d2b746f98e2c85006ea7e2d5c49cd9277662e47f223138ff418066791e3", +"test_stellar.py::test_sign_tx[StellarCreatePassiveSellOfferOp]": "6b0f0d2b746f98e2c85006ea7e2d5c49cd9277662e47f223138ff418066791e3", "test_stellar.py::test_sign_tx[StellarManageDataOp]": "8fbec6547a8f9d1f002181db0cbe57fe86abef8d365b1c06fd14292cd0b068a7", -"test_stellar.py::test_sign_tx[StellarManageOfferOp]": "6ed84765b2ed46711be0ed1219d91c27e927119d352f37b2baf8c6501186bbce", -"test_stellar.py::test_sign_tx[StellarPathPaymentOp]": "58f3bfaece0706bc172d6e6564b728ec0b7f8e2629d8c64dc60672786586076d", +"test_stellar.py::test_sign_tx[StellarManageBuyOfferOp]": "fc57e1ca8b65588aa16cc3524d6dc0f01e094ad5d16a6f7e739a69c101b554bc", +"test_stellar.py::test_sign_tx[StellarManageSellOfferOp]": "6ed84765b2ed46711be0ed1219d91c27e927119d352f37b2baf8c6501186bbce", +"test_stellar.py::test_sign_tx[StellarPathPaymentStrictReceiveOp]": "58f3bfaece0706bc172d6e6564b728ec0b7f8e2629d8c64dc60672786586076d", +"test_stellar.py::test_sign_tx[StellarPathPaymentStrictSendOp]": "fdd36a59520317d514e03f535dfeb93339af0f7ea5ee07c556bee3c8784c94ed", "test_stellar.py::test_sign_tx[StellarPaymentOp-asset12]": "1d8e9d5d65420a259f7e2deef1efaf0ce5be966a0f1e5b8e95b832f176f00de2", "test_stellar.py::test_sign_tx[StellarPaymentOp-asset4]": "0de0b815dad5d348a3b9d06e37da94800363e5de8e6ca9cd0f84e5070f7e1b22", "test_stellar.py::test_sign_tx[StellarPaymentOp-native_asset]": "b2015b9e0f9ff60e2ea4fca2942e97b70a320386c2043fb36acde4a830272098",