From cb61ceadafdffb497f41c52cf60b2e461bdaa03e Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan <satoshiotomakan8@gmail.com> Date: Mon, 8 Jan 2024 17:22:12 +0700 Subject: [PATCH] [WalletConnect/BNB]: Add `TWWalletConnectRequestParse` C FFI * Improve WalletConnect.proto interface --- .../TrustWalletCore/TWWalletConnectRequest.h | 27 ++++++++++ rust/chains/tw_binance/Cargo.toml | 2 +- .../src/modules/wallet_connect/connector.rs | 14 +++-- .../src/modules/wallet_connect/methods.rs | 7 --- .../src/modules/wallet_connect/mod.rs | 1 - .../src/ffi/tw_wallet_connect_request.rs | 4 +- .../src/test_utils/wallet_connect_utils.rs | 29 +++------- .../chains/binance/binance_wallet_connect.rs | 12 +++-- rust/tw_cosmos_sdk/Cargo.toml | 2 +- src/interface/TWWalletConnectRequest.cpp | 24 +++++++++ src/proto/WalletConnect.proto | 22 +++++--- .../chains/Binance/TWWalletConnectSigning.cpp | 54 +++++++++++++++++++ .../Binance/TransactionCompilerTests.cpp | 1 - 13 files changed, 146 insertions(+), 53 deletions(-) create mode 100644 include/TrustWalletCore/TWWalletConnectRequest.h delete mode 100644 rust/chains/tw_binance/src/modules/wallet_connect/methods.rs create mode 100644 src/interface/TWWalletConnectRequest.cpp create mode 100644 tests/chains/Binance/TWWalletConnectSigning.cpp diff --git a/include/TrustWalletCore/TWWalletConnectRequest.h b/include/TrustWalletCore/TWWalletConnectRequest.h new file mode 100644 index 00000000000..a65e7d9ed3d --- /dev/null +++ b/include/TrustWalletCore/TWWalletConnectRequest.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWData.h" + +TW_EXTERN_C_BEGIN + +/// Represents a WalletConnect signing request. +TW_EXPORT_CLASS +struct TWWalletConnectRequest; + +/// Parses the WalletConnect signing request as a `SigningInput`. +/// +/// \param coin The given coin type to plan the transaction for. +/// \param input The serialized data of a `WalletConnect::Proto::ParseRequestInput` proto object. +/// \return The serialized data of `WalletConnect::Proto::ParseRequestOutput` proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input); + +TW_EXTERN_C_END diff --git a/rust/chains/tw_binance/Cargo.toml b/rust/chains/tw_binance/Cargo.toml index 9a1d5003651..e5c9a10aa2c 100644 --- a/rust/chains/tw_binance/Cargo.toml +++ b/rust/chains/tw_binance/Cargo.toml @@ -17,5 +17,5 @@ tw_evm = { path = "../../tw_evm" } tw_hash = { path = "../../tw_hash" } tw_keypair = { path = "../../tw_keypair" } tw_memory = { path = "../../tw_memory" } -tw_misc = { path = "../../tw_misc" } +tw_misc = { path = "../../tw_misc", features = ["serde"] } tw_proto = { path = "../../tw_proto" } diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs index 48f9afc763b..13a9a95de7c 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs @@ -5,14 +5,14 @@ // file LICENSE at the root of the source code distribution tree. use crate::modules::tx_builder::TxBuilder; -use crate::modules::wallet_connect::methods::COSMOS_SIGN_AMINO; use crate::modules::wallet_connect::types::SignAminoRequest; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::modules::wallet_connect_signer::WalletConnector; use tw_coin_entry::signing_output_error; -use tw_proto::serialize; -use tw_proto::WalletConnect::Proto as WCProto; +use tw_proto::WalletConnect::Proto::{ + self as WCProto, mod_ParseRequestOutput::OneOfsigning_input_oneof as SigningInputEnum, +}; pub struct BinanceWalletConnector; @@ -32,8 +32,8 @@ impl BinanceWalletConnector { coin: &dyn CoinContext, request: WCProto::ParseRequestInput<'_>, ) -> SigningResult<WCProto::ParseRequestOutput<'static>> { - match request.method.as_ref() { - COSMOS_SIGN_AMINO => Self::parse_sign_amino_request(coin, request), + match request.method { + WCProto::Method::CosmosSignAmino => Self::parse_sign_amino_request(coin, request), _ => Err(SigningError(SigningErrorType::Error_not_supported)), } } @@ -47,11 +47,9 @@ impl BinanceWalletConnector { // Parse a `SigningInput` from the given `signDoc`. let signing_input = TxBuilder::unsigned_tx_to_proto(&amino_req.sign_doc)?; - let signing_input_bytes = serialize(&signing_input) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(WCProto::ParseRequestOutput { - signing_input: signing_input_bytes.into(), + signing_input_oneof: SigningInputEnum::binance(signing_input), ..WCProto::ParseRequestOutput::default() }) } diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/methods.rs b/rust/chains/tw_binance/src/modules/wallet_connect/methods.rs deleted file mode 100644 index a57ace82339..00000000000 --- a/rust/chains/tw_binance/src/modules/wallet_connect/methods.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -pub const COSMOS_SIGN_AMINO: &str = "cosmos_signAmino"; diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs index 2227ef977f7..204b40aaa32 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs @@ -5,5 +5,4 @@ // file LICENSE at the root of the source code distribution tree. pub mod connector; -pub mod methods; pub mod types; diff --git a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs index 637bf92a7af..6d97e358ec9 100644 --- a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs +++ b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs @@ -15,8 +15,8 @@ use tw_misc::try_or_else; /// Parses the WalletConnect signing request as a `SigningInput`. /// /// \param coin The given coin type to plan the transaction for. -/// \param input The serialized data of a signing input. -/// \return The serialized data of a `TransactionPlan` proto object. +/// \param input The serialized data of a `WalletConnect::Proto::ParseRequestInput` proto object. +/// \return The serialized data of `WalletConnect::Proto::ParseRequestOutput` proto object. #[no_mangle] pub unsafe extern "C" fn tw_wallet_connect_request_parse( coin: u32, diff --git a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs index 2f19724ca51..7d6efd0fffa 100644 --- a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs +++ b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs @@ -5,43 +5,30 @@ // file LICENSE at the root of the source code distribution tree. use crate::ffi::tw_wallet_connect_request::tw_wallet_connect_request_parse; -use std::marker::PhantomData; use tw_coin_registry::coin_type::CoinType; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_memory::Data; -use tw_proto::{ - deserialize, serialize, Common::Proto as CommonProto, MessageRead, - WalletConnect::Proto as WCProto, -}; +use tw_proto::{deserialize, serialize, WalletConnect::Proto as WCProto}; #[derive(Default)] -pub struct WalletConnectRequestHelper<'a, SigningInput: MessageRead<'a>> { - signing_input_data: Data, - _signing_input_type: PhantomData<&'a SigningInput>, +pub struct WalletConnectRequestHelper { + output_data: Data, } -impl<'a, SigningInput: MessageRead<'a>> WalletConnectRequestHelper<'a, SigningInput> { - pub fn parse( +impl WalletConnectRequestHelper { + pub fn parse<'a>( &'a mut self, coin_type: CoinType, input: &WCProto::ParseRequestInput, - ) -> Result<SigningInput, CommonProto::SigningError> { + ) -> WCProto::ParseRequestOutput<'a> { let input_data = TWDataHelper::create(serialize(input).unwrap()); - let output_data = TWDataHelper::wrap(unsafe { + self.output_data = TWDataHelper::wrap(unsafe { tw_wallet_connect_request_parse(coin_type as u32, input_data.ptr()) }) .to_vec() .expect("!tw_wallet_connect_request_parse returned nullptr"); - let output: WCProto::ParseRequestOutput = deserialize(&output_data).unwrap(); - - if output.error != CommonProto::SigningError::OK { - return Err(output.error); - } - - self.signing_input_data = output.signing_input.to_vec(); - let signing_input: SigningInput = deserialize(&self.signing_input_data).unwrap(); - Ok(signing_input) + deserialize(&self.output_data).unwrap() } } diff --git a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs index 694b5e8af8e..9d10a12486b 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs @@ -20,15 +20,17 @@ const WC_SIGN_REQUEST_CASE_1: &str = include_str!("data/wc_sign_request_case_1.j fn test_binance_sign_wallet_connect_case_1() { let input = WCProto::ParseRequestInput { protocol: WCProto::Protocol::V2, - method: "cosmos_signAmino".into(), + method: WCProto::Method::CosmosSignAmino, payload: WC_SIGN_REQUEST_CASE_1.to_string().into(), }; - let mut parser = WalletConnectRequestHelper::<Proto::SigningInput>::default(); + let mut parser = WalletConnectRequestHelper::default(); + let parsing_output = parser.parse(CoinType::Binance, &input); - let mut signing_input = parser - .parse(CoinType::Binance, &input) - .expect("Unexpected error on parsing WalletConnect request"); + let mut signing_input = match parsing_output.signing_input_oneof { + WCProto::mod_ParseRequestOutput::OneOfsigning_input_oneof::binance(input) => input, + _ => unreachable!(), + }; // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 let expected_from_addr_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6"; diff --git a/rust/tw_cosmos_sdk/Cargo.toml b/rust/tw_cosmos_sdk/Cargo.toml index 3e209b4f6df..ffd6abf455b 100644 --- a/rust/tw_cosmos_sdk/Cargo.toml +++ b/rust/tw_cosmos_sdk/Cargo.toml @@ -17,7 +17,7 @@ tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } -tw_number = { path = "../tw_number", features = ["serde"] } +tw_number = { path = "../tw_number" } tw_proto = { path = "../tw_proto" } [dev-dependencies] diff --git a/src/interface/TWWalletConnectRequest.cpp b/src/interface/TWWalletConnectRequest.cpp new file mode 100644 index 00000000000..0630536f6b9 --- /dev/null +++ b/src/interface/TWWalletConnectRequest.cpp @@ -0,0 +1,24 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include <TrustWalletCore/TWWalletConnectRequest.h> + +#include "rust/Wrapper.h" + +using namespace TW; + +TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input) { + try { + const Data& inputData = *reinterpret_cast<const Data*>(input); + Rust::TWDataWrapper twInputData = inputData; + + Rust::TWDataWrapper twOutputData = Rust::tw_wallet_connect_request_parse(static_cast<uint32_t>(coin), twInputData.get()); + auto outputData = twOutputData.toDataOrDefault(); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); + } catch (...) { + return nullptr; + } +} diff --git a/src/proto/WalletConnect.proto b/src/proto/WalletConnect.proto index 0cf4726d8ab..37b8a9796c0 100644 --- a/src/proto/WalletConnect.proto +++ b/src/proto/WalletConnect.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package TW.WalletConnect.Proto; option java_package = "wallet.core.jni.proto"; +import "Binance.proto"; import "Common.proto"; // The transaction protocol may differ from version to version. @@ -10,12 +11,19 @@ enum Protocol { V2 = 0; } +// WalletConnect request method. +enum Method { + Unknown = 0; + // cosmos_signAmino + CosmosSignAmino = 1; +} + message ParseRequestInput { // A protocol version. Protocol protocol = 1; // A signing method like "cosmos_signAmino" or "eth_signTransaction". - string method = 2; + Method method = 2; // Transaction payload to sign. // Basically, a JSON object. @@ -23,12 +31,14 @@ message ParseRequestInput { } message ParseRequestOutput { - // Prepared unsigned transaction input needs to be parsed accordingly. - bytes signing_input = 1; - // OK (=0) or other codes in case of error - Common.Proto.SigningError error = 2; + Common.Proto.SigningError error = 1; // error description in case of error - string error_message = 3; + string error_message = 2; + + // Prepared unsigned transaction input, on the source chain. Some fields must be completed, and it has to be signed. + oneof signing_input_oneof { + Binance.Proto.SigningInput binance = 3; + } } diff --git a/tests/chains/Binance/TWWalletConnectSigning.cpp b/tests/chains/Binance/TWWalletConnectSigning.cpp new file mode 100644 index 00000000000..85161072322 --- /dev/null +++ b/tests/chains/Binance/TWWalletConnectSigning.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Binance.pb.h" +#include "proto/WalletConnect.pb.h" +#include "Coin.h" +#include <TrustWalletCore/TWAnySigner.h> +#include <TrustWalletCore/TWWalletConnectRequest.h> + +#include "TestUtilities.h" +#include <gtest/gtest.h> + +namespace TW::Binance { + +TEST(TWWalletConnectSign, SendOrder) { + auto private_key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); + const auto payload = R"({"signerAddress":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","signDoc":{"account_number":"19","chain_id":"chain-bnb","memo":"","data":null,"msgs":[{"inputs":[{"address":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","coins":[{"amount":1001000000,"denom":"BNB"}]}],"outputs":[{"address":"bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf","coins":[{"amount":1001000000,"denom":"BNB"}]}]}],"sequence":"23","source":"1"}})"; + + WalletConnect::Proto::ParseRequestInput parsingInput; + parsingInput.set_method(WalletConnect::Proto::Method::CosmosSignAmino); + parsingInput.set_payload(payload); + + const auto parsinginputData = parsingInput.SerializeAsString(); + const auto parsingInputDataPtr = WRAPD(TWDataCreateWithBytes(reinterpret_cast<const uint8_t *>(parsinginputData.c_str()), parsinginputData.size())); + + const auto outputDataPtr = WRAPD(TWWalletConnectRequestParse(TWCoinTypeBinance, parsingInputDataPtr.get())); + + WalletConnect::Proto::ParseRequestOutput parsingOutput; + parsingOutput.ParseFromArray( + TWDataBytes(outputDataPtr.get()), + static_cast<int>(TWDataSize(outputDataPtr.get())) + ); + + EXPECT_EQ(parsingOutput.error(), Common::Proto::SigningError::OK); + + // Step 2: Set missing fields. + ASSERT_TRUE(parsingOutput.has_binance()); + Proto::SigningInput signingInput = parsingOutput.binance(); + + signingInput.set_private_key(private_key.data(), private_key.size()); + + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeBinance); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_EQ(hex(output.signature()), "3c24c784c6bbf99d54ffabb153edcb6d3c4a774936df5c72a5d32897256f8e062f320fb4753302fb0a96f08c475974d20edfd1a27bbeeda73587f58ddc958975"); + EXPECT_EQ(output.signature_json(), R"({"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="})"); +} + +} // namespace TW::Binance diff --git a/tests/chains/Binance/TransactionCompilerTests.cpp b/tests/chains/Binance/TransactionCompilerTests.cpp index 5b5a4e7ef19..efcb375ac30 100644 --- a/tests/chains/Binance/TransactionCompilerTests.cpp +++ b/tests/chains/Binance/TransactionCompilerTests.cpp @@ -82,7 +82,6 @@ TEST(BinanceCompiler, CompileWithSignatures) { "253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5" "04f9e8310679"; { - EXPECT_EQ(outputData.size(), 189ul); Binance::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size()));