Skip to content

Commit

Permalink
[WalletConnect/BNB]: Add TWWalletConnectRequestParse C FFI
Browse files Browse the repository at this point in the history
* Improve WalletConnect.proto interface
  • Loading branch information
satoshiotomakan committed Jan 8, 2024
1 parent 77a917e commit cb61cea
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 53 deletions.
27 changes: 27 additions & 0 deletions include/TrustWalletCore/TWWalletConnectRequest.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion rust/chains/tw_binance/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
14 changes: 6 additions & 8 deletions rust/chains/tw_binance/src/modules/wallet_connect/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)),
}
}
Expand All @@ -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()
})
}
Expand Down
7 changes: 0 additions & 7 deletions rust/chains/tw_binance/src/modules/wallet_connect/methods.rs

This file was deleted.

1 change: 0 additions & 1 deletion rust/chains/tw_binance/src/modules/wallet_connect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@
// file LICENSE at the root of the source code distribution tree.

pub mod connector;
pub mod methods;
pub mod types;
4 changes: 2 additions & 2 deletions rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
29 changes: 8 additions & 21 deletions rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
12 changes: 7 additions & 5 deletions rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion rust/tw_cosmos_sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
24 changes: 24 additions & 0 deletions src/interface/TWWalletConnectRequest.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
22 changes: 16 additions & 6 deletions src/proto/WalletConnect.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,42 @@ 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.
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.
string payload = 3;
}

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;
}
}
54 changes: 54 additions & 0 deletions tests/chains/Binance/TWWalletConnectSigning.cpp
Original file line number Diff line number Diff line change
@@ -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
1 change: 0 additions & 1 deletion tests/chains/Binance/TransactionCompilerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));

Expand Down

0 comments on commit cb61cea

Please sign in to comment.