Skip to content

Commit

Permalink
[WalletConnect/BNB]: Implement cosmos_signAmino handler
Browse files Browse the repository at this point in the history
* TODO fix compilation error
  • Loading branch information
satoshiotomakan committed Dec 29, 2023
1 parent 183db68 commit 649cd54
Show file tree
Hide file tree
Showing 19 changed files with 390 additions and 28 deletions.
15 changes: 13 additions & 2 deletions rust/chains/tw_binance/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use serde::Serialize;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use tw_bech32_address::bech32_prefix::Bech32Prefix;
use tw_bech32_address::Bech32Address;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::CoinAddress;
use tw_coin_entry::error::{AddressError, AddressResult};
use tw_cosmos_sdk::address::CosmosAddress;
use tw_keypair::tw::PublicKey;
use tw_memory::Data;

Expand All @@ -21,7 +22,7 @@ const BNB_KNOWN_HRPS: [&str; 2] = [
"bca",
];

#[derive(Serialize)]
#[derive(Deserialize, PartialEq, Serialize)]
pub struct BinanceAddress(Bech32Address);

impl CoinAddress for BinanceAddress {
Expand All @@ -31,6 +32,16 @@ impl CoinAddress for BinanceAddress {
}
}

impl CosmosAddress for BinanceAddress {
fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult<Self>
where
Self: Sized,
{
let prefix = None;
Self::from_str_with_coin_and_prefix(coin, addr.to_string(), prefix)
}
}

impl BinanceAddress {
pub const VALIDATOR_HRP: &'static str = "bva";

Expand Down
24 changes: 24 additions & 0 deletions rust/chains/tw_binance/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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.

use crate::address::BinanceAddress;
use tw_cosmos_sdk::context::CosmosContext;
use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher;
use tw_cosmos_sdk::hasher::sha256_hasher::Sha256Hasher;
use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey;
use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey;
use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature;

pub struct BinanceContext;

impl CosmosContext for BinanceContext {
type Address = BinanceAddress;
/// Binance Beacon chain uses `sha256` hash.
type TxHasher = Sha256Hasher;
type PrivateKey = Secp256PrivateKey;
type PublicKey = Secp256PublicKey;
type Signature = Secp256k1Signature;
}
8 changes: 7 additions & 1 deletion rust/chains/tw_binance/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use crate::address::BinanceAddress;
use crate::compiler::BinanceCompiler;
use crate::modules::wallet_connect::signer::BinanceWalletConnectSigner;
use crate::signer::BinanceSigner;
use std::str::FromStr;
use tw_bech32_address::bech32_prefix::Bech32Prefix;
Expand Down Expand Up @@ -34,7 +35,7 @@ impl CoinEntry for BinanceEntry {
type JsonSigner = NoJsonSigner;
type PlanBuilder = NoPlanBuilder;
type MessageSigner = NoMessageSigner;
type WalletConnectSigner = NoWalletConnectSigner;
type WalletConnectSigner = BinanceWalletConnectSigner;

#[inline]
fn parse_address(
Expand Down Expand Up @@ -90,4 +91,9 @@ impl CoinEntry for BinanceEntry {
) -> Self::SigningOutput {
BinanceCompiler::compile(coin, input, signatures, public_keys)
}

#[inline]
fn wallet_connect_signer(&self) -> Option<Self::WalletConnectSigner> {
Some(BinanceWalletConnectSigner)
}
}
1 change: 1 addition & 0 deletions rust/chains/tw_binance/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
pub mod address;
pub mod amino;
pub mod compiler;
pub mod context;
pub mod entry;
pub mod modules;
pub mod signature;
Expand Down
1 change: 1 addition & 0 deletions rust/chains/tw_binance/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
pub mod preimager;
pub mod serializer;
pub mod tx_builder;
pub mod wallet_connect;
7 changes: 7 additions & 0 deletions rust/chains/tw_binance/src/modules/wallet_connect/methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 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";
9 changes: 9 additions & 0 deletions rust/chains/tw_binance/src/modules/wallet_connect/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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 mod methods;
pub mod signer;
pub mod types;
103 changes: 103 additions & 0 deletions rust/chains/tw_binance/src/modules/wallet_connect/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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.

use crate::address::BinanceAddress;
use crate::context::BinanceContext;
use crate::modules::preimager::{JsonPreimager, JsonTxPreimage};
use crate::modules::wallet_connect::methods::COSMOS_SIGN_AMINO;
use crate::modules::wallet_connect::types::{SignAminoRequest, SignAminoResponse};
use crate::signature::BinanceSignature;
use crate::transaction::UnsignedTransaction;
use std::borrow::Cow;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult};
use tw_coin_entry::modules::wallet_connect_signer::WalletConnectSigner;
use tw_coin_entry::signing_output_error;
use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer;
use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey;
use tw_cosmos_sdk::public_key::CosmosPublicKey;
use tw_keypair::ecdsa::secp256k1;
use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait};
use tw_keypair::tw;
use tw_keypair::tw::Curve;
use tw_misc::traits::ToBytesVec;
use tw_proto::WalletConnect::Proto as WCProto;

pub struct BinanceWalletConnectSigner;

impl WalletConnectSigner for BinanceWalletConnectSigner {
type SigningInput<'a> = WCProto::SigningInput<'a>;
type SigningOutput = WCProto::SigningOutput<'static>;

fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput {
Self::sign_impl(coin, input)
.unwrap_or_else(|e| signing_output_error!(WCProto::SigningOutput, e))
}
}

impl BinanceWalletConnectSigner {
pub fn sign_impl(
coin: &dyn CoinContext,
input: WCProto::SigningInput<'_>,
) -> SigningResult<WCProto::SigningOutput<'static>> {
match input.method.as_ref() {
COSMOS_SIGN_AMINO => Self::sign_amino(coin, input),
_ => Err(SigningError(SigningErrorType::Error_not_supported)),
}
}

pub fn sign_amino(
coin: &dyn CoinContext,
input: WCProto::SigningInput<'_>,
) -> SigningResult<WCProto::SigningOutput<'static>> {
let request: SignAminoRequest = serde_json::from_str(&input.payload)
.map_err(|_| SigningError(SigningErrorType::Error_input_parse))?;

let private_key = tw::PrivateKey::new(input.private_key.to_vec())?;

// First, check if the `SignAminoRequest::signer_address` belongs to the `SigningInput::private_key`.
if let Some(ref expected_addr) = request.signer_address {
Self::validate_signer_address(coin, &private_key, expected_addr)?;
}

let JsonTxPreimage { tx_hash, .. } = JsonPreimager::preimage_hash(&request.sign_doc)?;

let signature_bytes = private_key.sign(tx_hash.as_slice(), Curve::Secp256k1)?;

let public_key = Secp256PublicKey::from_private_key(coin, &private_key)?;
let signature =
JsonSerializer::<BinanceContext>::serialize_signature(&public_key, signature_bytes);

let result = SignAminoResponse {
signature,
signed: request.sign_doc,
};
let result_json = serde_json::to_string(&result)
.map_err(|_| SigningError(SigningErrorType::Error_internal))?;

Ok(WCProto::SigningOutput {
result: Cow::from(result_json),
..WCProto::SigningOutput::default()
})
}

fn validate_signer_address(
coin: &dyn CoinContext,
private_key: &tw::PrivateKey,
expected_addr: &BinanceAddress,
) -> SigningResult<()> {
let tw_public_key = private_key.get_public_key_by_type(coin.public_key_type())?;
let prefix = None;
let actual_address =
BinanceAddress::with_public_key_coin_context(coin, &tw_public_key, prefix)?;

if actual_address == *expected_addr {
return Ok(());
}

Err(SigningError(SigningErrorType::Error_invalid_address))
}
}
25 changes: 25 additions & 0 deletions rust/chains/tw_binance/src/modules/wallet_connect/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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.

use crate::address::BinanceAddress;
use crate::transaction::UnsignedTransaction;
use serde::{Deserialize, Serialize};
use tw_cosmos_sdk::modules::serializer::json_serializer::SignatureJson;
use tw_encoding::base64::Base64Encoded;

#[derive(Deserialize)]
pub struct SignAminoRequest {
#[serde(rename = "signerAddress")]
pub signer_address: Option<BinanceAddress>,
#[serde(rename = "signDoc")]
pub sign_doc: UnsignedTransaction,
}

#[derive(Serialize)]
pub struct SignAminoResponse {
pub signature: SignatureJson,
pub signed: UnsignedTransaction,
}
10 changes: 5 additions & 5 deletions rust/chains/tw_binance/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@

use crate::signature::BinanceSignature;
use crate::transaction::message::BinanceMessageBox;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use tw_keypair::ecdsa::secp256k1;
use tw_memory::Data;
use tw_misc::serde::as_string;

pub mod message;

#[derive(Serialize)]
#[derive(Deserialize, Serialize)]
pub struct UnsignedTransaction {
#[serde(serialize_with = "as_string")]
#[serde(with = "as_string")]
pub account_number: i64,
pub chain_id: String,
pub data: Option<Data>,
pub memo: String,
pub msgs: Vec<BinanceMessageBox>,
#[serde(serialize_with = "as_string")]
#[serde(with = "as_string")]
pub sequence: i64,
#[serde(serialize_with = "as_string")]
#[serde(with = "as_string")]
pub source: i64,
}

Expand Down
18 changes: 18 additions & 0 deletions rust/tw_any_coin/src/ffi/tw_any_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,21 @@ pub unsafe extern "C" fn tw_any_signer_plan(input: *const TWData, coin: u32) ->
.map(|output| TWData::from(output).into_ptr())
.unwrap_or_else(|_| std::ptr::null_mut())
}

/// Signs a transaction in WalletConnect format.
///
/// \param input The serialized data of a signing input.
/// \param coin The given coin type to plan the transaction for.
/// \return The serialized data of a `TransactionPlan` proto object.
#[no_mangle]
pub unsafe extern "C" fn tw_any_signer_sign_wallet_connect(
input: *const TWData,
coin: u32,
) -> *mut TWData {
let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut);
let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut);

AnySigner::sign_wallet_connect(input.as_slice(), coin)
.map(|output| TWData::from(output).into_ptr())
.unwrap_or_else(|_| std::ptr::null_mut())
}
27 changes: 26 additions & 1 deletion rust/tw_any_coin/src/test_utils/sign_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use crate::ffi::tw_any_signer::tw_any_signer_sign;
use crate::ffi::tw_any_signer::{tw_any_signer_sign, tw_any_signer_sign_wallet_connect};
use crate::ffi::tw_transaction_compiler::{
tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes,
};
Expand Down Expand Up @@ -94,3 +94,28 @@ impl<'a, Output: MessageRead<'a>> CompilerHelper<'a, Output> {
output
}
}

#[derive(Default)]
pub struct WalletConnectSignHelper<'a, Output: MessageRead<'a>> {
output_data: Data,
_output_type: PhantomData<&'a Output>,
}

impl<'a, Output: MessageRead<'a>> WalletConnectSignHelper<'a, Output> {
pub fn sign_wallet_connect<Input: MessageWrite>(
&'a mut self,
coin_type: CoinType,
input: Input,
) -> Output {
let input_data = TWDataHelper::create(serialize(input).unwrap());

self.output_data = TWDataHelper::wrap(unsafe {
tw_any_signer_sign_wallet_connect(input_data.ptr(), coin_type as u32)
})
.to_vec()
.expect("!tw_any_signer_sign_wallet_connect returned nullptr");

let output: Output = deserialize(&self.output_data).unwrap();
output
}
}
15 changes: 5 additions & 10 deletions rust/tw_any_coin/tests/chains/binance/binance_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,17 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use crate::chains::binance::make_token;
use crate::chains::binance::{
make_token, ACCOUNT_12_PRIVATE_KEY, ACCOUNT_15_PRIVATE_KEY, ACCOUNT_16_PRIVATE_KEY,
ACCOUNT_19_PRIVATE_KEY,
};
use tw_any_coin::test_utils::sign_utils::AnySignerHelper;
use tw_coin_registry::coin_type::CoinType;
use tw_encoding::hex::{DecodeHex, ToHex};
use tw_proto::Binance::Proto;
use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderEnum;
use tw_proto::Common::Proto::SigningError;

const ACCOUNT_12_PRIVATE_KEY: &str =
"90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9";
const ACCOUNT_19_PRIVATE_KEY: &str =
"95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832";
const ACCOUNT_15_PRIVATE_KEY: &str =
"eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d";
const ACCOUNT_16_PRIVATE_KEY: &str =
"851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a";

#[test]
fn test_binance_sign_trade_order() {
// bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu
Expand Down Expand Up @@ -97,6 +91,7 @@ fn test_binance_sign_send_order() {
let amount = 1_001_000_000;
// bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2
let from_address_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6";
// TODO
let to_address_key_hash = "88b37d5e05f3699e2a1406468e5d87cb9dcceb95";

let send_order = Proto::SendOrder {
Expand Down
Loading

0 comments on commit 649cd54

Please sign in to comment.