From 4a7db22f707940e02d1a7466dbd114e903dbb98e Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Fri, 24 Jan 2025 17:09:45 +0100 Subject: [PATCH 1/2] feature(ripple): Add support for non-standard currency code --- src/XRP/Transaction.cpp | 34 ++++++++++++++-------- src/XRP/Transaction.h | 5 ++-- tests/chains/XRP/TWAnySignerTests.cpp | 41 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp index c4f9298fd91..48bd04a0382 100644 --- a/src/XRP/Transaction.cpp +++ b/src/XRP/Transaction.cpp @@ -85,7 +85,7 @@ Data Transaction::serialize() const { (transaction_type == TransactionType::NFTokenCreateOffer)) { encodeType(FieldType::amount, 1, data); append(data, - (currency_amount.currency.size() > 0) ? + (!currency_amount.currency.empty()) ? serializeCurrencyAmount(currency_amount) : serializeAmount(amount)); } else if (transaction_type == TransactionType::TrustSet) { @@ -257,17 +257,7 @@ Data Transaction::serializeCurrencyAmount(const CurrencyAmount& currency_amount) // https://xrpl.org/serialization.html#amount-fields auto data = Data(); encode64BE(amount_cast.value, data); - - // ISO-4217 currency code - encodeZeros(1, data); // type code (0x00) - encodeZeros(11, data); // reserved - if (currency_amount.currency.size() == 3) { - data.insert(data.end(), currency_amount.currency.begin(), currency_amount.currency.end()); - } else { - encodeZeros(3, data); // none - } - - encodeZeros(5, data); // reserved + serializeCurrencyCode(data, currency_amount.currency); data.insert(data.end(), currency_amount.issuer.begin(), currency_amount.issuer.end()); return data; } @@ -278,4 +268,24 @@ Data Transaction::serializeAddress(Address address) { return data; } +void Transaction::serializeCurrencyCode(Data& out, const std::string& currency_code) { + if (currency_code.size() == 40) { + auto code_bytes = parse_hex(currency_code); + out.insert(out.end(), code_bytes.begin(), code_bytes.end()); + return; + } + + // Standard ISO-4217 currency code + encodeZeros(1, out); // type code (0x00) + encodeZeros(11, out); // reserved + + if (currency_code.size() == 3) { + out.insert(out.end(), currency_code.begin(), currency_code.end()); + } else { + encodeZeros(3, out); // none + } + + encodeZeros(5, out); // reserved +} + } // namespace TW::Ripple diff --git a/src/XRP/Transaction.h b/src/XRP/Transaction.h index 757d80b2e77..64cec70adaa 100644 --- a/src/XRP/Transaction.h +++ b/src/XRP/Transaction.h @@ -47,7 +47,7 @@ class Transaction { /// See https://github.com/trezor/trezor-core/tree/master/src/apps/ripple#transactions public: struct CurrencyAmount { - Data currency; + std::string currency; Data value; Data issuer; }; @@ -182,10 +182,11 @@ class Transaction { static Data serializeAmount(int64_t p_amount); static Data serializeCurrencyAmount(const CurrencyAmount& currency_amount); static Data serializeAddress(Address address); + static void serializeCurrencyCode(Data& out, const std::string& currency_code); private: void setCurrencyAmount(CurrencyAmount& p_currency_amount, const std::string& p_currency, const std::string& p_value, const std::string& p_issuer) { - p_currency_amount.currency = Data(p_currency.begin(), p_currency.end()); + p_currency_amount.currency = p_currency; p_currency_amount.value = Data(p_value.begin(), p_value.end()); setAccount(p_issuer, p_currency_amount.issuer); } diff --git a/tests/chains/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp index 3dd17ea7d1b..1bcce092e82 100644 --- a/tests/chains/XRP/TWAnySignerTests.cpp +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -80,6 +80,26 @@ TEST(TWAnySignerRipple, SignTrustSetPayment) { EXPECT_EQ(hex(output.encoded()), "12001422000000002401ec60b9201b01ec60ce63d4c38d7ea4c6800000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a732103dc4a0dae2d550de7cace9c26c1a331a114e3e7efee5577204b476d27e2dc683a7446304402206ebcc7a689845df373dd2566cd3789862d426d9ad4e6a09c2d2772b57e82696a022066b1f217a0f0d834d167613a313f74097423a9ccd11f1ae7f90ffab0d2fc26b58114308ea8e515b64f2e6616a33b42e1bbb9fa00bbd2"); } +TEST(TWAnySignerRipple, SignTrustSetPaymentNonStandardCurrencyCode) { + // https://livenet.xrpl.org/transactions/31ABD41ECAD459BCD008DBA4377047413AEE7A965517DB240016B66A3F4A97E1 + auto key = parse_hex("574e99f7946cfa2a6ca9368ca72fd37e42583cddb9ecc746aa4cb194ef4b2480"); + Proto::SigningInput input; + + input.mutable_op_trust_set()->mutable_limit_amount()->set_currency("524C555344000000000000000000000000000000"); + input.mutable_op_trust_set()->mutable_limit_amount()->set_value("1000000000"); + input.mutable_op_trust_set()->mutable_limit_amount()->set_issuer("rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De"); + input.set_fee(500); + input.set_sequence(93674950); + input.set_last_ledger_sequence(187349950); + input.set_account("rDgEGKXWkHHr1HYq2ETnNAs9MdV4R8Gyt"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12001422000000002405955dc6201b0b2abbbe63d6c38d7ea4c68000524c555344000000000000000000000000000000e5e961c6a025c9404aa7b662dd1df975be75d13e6840000000000001f47321039c77e9329017ced5f8673ebafcd29687a1fff181140c030062fa77865688fc5d74473045022100aa5f7ffc2e11008a3fe98173c66360937cd3a72cb0951aa1b46ba32675c36b2d02206bc02de3a609e5c4b9e1510a6431a7d7efc0fba4ab9586d6595b86047e46bac281140265c09d122fab2a261a80ee59f1f4cd8fba8cf8"); +} + TEST(TWAnySignerRipple, SignTokenPayment0) { // https://testnet.xrpl.org/transactions/8F7820892294598B58CFA2E1101D15ED98C179B25A2BA6DAEB4F5B727CB00D4E auto key = parse_hex("4ba5fd2ebf0f5d7e579b3c354c263ebb39cda4093845125786a280301af14e21"); @@ -119,6 +139,27 @@ TEST(TWAnySignerRipple, SignTokenPayment1) { EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec61e0201b01ec61f561d48a68d1c931200000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a73210348c331ab218ba964150490c83875b06ccad2100b1f5707f296764712738cf1ca74473045022100a938783258d33e2e3e6099d1ab68fd85c3fd21adfa00e136a67bed8fddec6c9a02206cc6784c1f212f19dc939207643d361ceaa8334eb366722cf33b24dc7669dd7a81143a2f2f189d05abb8519cc9dee0e2dbc6fa53924183148132e4e20aecf29090ac428a9c43f230a829220d"); } +TEST(TWAnySignerRipple, SignTokenPaymentNonStandardCurrencyCode) { + // https://livenet.xrpl.org/transactions/6A1229450BB795E450C4AFAA7B72B58962621C0B8760372634796B3941718BFB + auto key = parse_hex("574e99f7946cfa2a6ca9368ca72fd37e42583cddb9ecc746aa4cb194ef4b2480"); + Proto::SigningInput input; + + input.mutable_op_payment()->mutable_currency_amount()->set_currency("524C555344000000000000000000000000000000"); + input.mutable_op_payment()->mutable_currency_amount()->set_value("1"); + input.mutable_op_payment()->mutable_currency_amount()->set_issuer("rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De"); + input.mutable_op_payment()->set_destination("r4oPb529jpRA1tVTDARmBuZPYB2CJjKFac"); + input.set_fee(12); + input.set_sequence(93674951); + input.set_last_ledger_sequence(187349950); + input.set_account("rDgEGKXWkHHr1HYq2ETnNAs9MdV4R8Gyt"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12000022000000002405955dc7201b0b2abbbe61d4838d7ea4c68000524c555344000000000000000000000000000000e5e961c6a025c9404aa7b662dd1df975be75d13e68400000000000000c7321039c77e9329017ced5f8673ebafcd29687a1fff181140c030062fa77865688fc5d744630440220552e90f417c2cabe39368bb45cf7495ba6ebe395f259a6509c9f3a7296e76a0d02201b37dae0c4c77fa70a451cd4a61c10575c8b052c282c082a32c229e7624a05e381140265c09d122fab2a261a80ee59f1f4cd8fba8cf88314ef20a3d93b00cc729eec11a3058d3d1feb4465e0"); +} + TEST(TWAnySignerRipple, SignEscrowCreateMain) { // https://xrpscan.com/tx/3576E5D413CBDC228D13F281BB66304C1EE9DDEAA5563F1783EDB1848266D739 auto key = parse_hex("a3cf20a85b25be4c955f0814718cc7a02eae9195159bd72ede5dd5c4e60d22c4"); From 0a0ab628f6fc7b54e95cd5d7f5d047e11ec66f47 Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Mon, 27 Jan 2025 13:19:04 +0100 Subject: [PATCH 2/2] feature(ripple): Code improvements --- src/XRP/Transaction.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp index 48bd04a0382..55fd6b041d5 100644 --- a/src/XRP/Transaction.cpp +++ b/src/XRP/Transaction.cpp @@ -14,6 +14,8 @@ namespace TW::Ripple { const int NETWORK_PREFIX = 0x53545800; +const size_t CURRENCY_CODE_SIZE = 20; +const size_t CURRENCY_CODE_HEX_SIZE = CURRENCY_CODE_SIZE * 2; Data Transaction::serialize() const { // See https://xrpl.org/serialization.html @@ -269,7 +271,7 @@ Data Transaction::serializeAddress(Address address) { } void Transaction::serializeCurrencyCode(Data& out, const std::string& currency_code) { - if (currency_code.size() == 40) { + if (currency_code.size() == CURRENCY_CODE_HEX_SIZE) { auto code_bytes = parse_hex(currency_code); out.insert(out.end(), code_bytes.begin(), code_bytes.end()); return;