Skip to content

Commit

Permalink
[BNB]: Implement Transaction Compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan committed Dec 11, 2023
1 parent 8641124 commit 63f33cc
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 27 deletions.
50 changes: 42 additions & 8 deletions rust/chains/tw_binance/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use crate::modules::serializer::BinanceAminoSerializer;
use crate::modules::tx_builder::TxBuilder;
use crate::signature::BinanceSignature;
use crate::transaction::{JsonTxPreimage, SignerInfo};
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes};
use tw_coin_entry::common::compile_input::SingleSignaturePubkey;
use tw_coin_entry::error::SigningResult;
use tw_coin_entry::signing_output_error;
use tw_keypair::ecdsa::secp256k1;
use tw_proto::Binance::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;

Expand All @@ -24,10 +30,20 @@ impl BinanceCompiler {
}

fn preimage_hashes_impl(
_coin: &dyn CoinContext,
_input: Proto::SigningInput<'_>,
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
) -> SigningResult<CompilerProto::PreSigningOutput<'static>> {
todo!()
let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?;
let JsonTxPreimage {
tx_hash,
encoded_tx,
} = unsigned_tx.preimage_hash()?;

Ok(CompilerProto::PreSigningOutput {
data_hash: tx_hash.to_vec().into(),
data: encoded_tx.as_bytes().to_vec().into(),
..CompilerProto::PreSigningOutput::default()
})
}

#[inline]
Expand All @@ -42,11 +58,29 @@ impl BinanceCompiler {
}

fn compile_impl(
_coin: &dyn CoinContext,
_input: Proto::SigningInput<'_>,
_signatures: Vec<SignatureBytes>,
_public_keys: Vec<PublicKeyBytes>,
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
todo!()
let SingleSignaturePubkey {
signature,
public_key,
} = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?;
let signature = BinanceSignature::try_from(signature.as_slice())?;
let public_key = secp256k1::PublicKey::try_from(public_key.as_slice())?;

let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?;
let signed_tx = unsigned_tx.into_signed(SignerInfo {
public_key,
signature,
});

let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?;

Ok(Proto::SigningOutput {
encoded: encoded_tx.into(),
..Proto::SigningOutput::default()
})
}
}
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 @@ -9,5 +9,6 @@ pub mod amino;
pub mod compiler;
pub mod entry;
pub mod modules;
pub mod signature;
pub mod signer;
pub mod transaction;
4 changes: 2 additions & 2 deletions rust/chains/tw_binance/src/modules/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ impl BinanceAminoSerializer {

pub fn serialize_signature(signed: &SignedTransaction) -> SigningResult<Data> {
let sign_msg = Proto::Signature {
pub_key: Self::serialize_public_key(signed.signer.public_key).into(),
signature: signed.signer.signature.to_vec().into(),
pub_key: Self::serialize_public_key(signed.signer.public_key.compressed()).into(),
signature: signed.signer.signature.to_bytes().into(),
account_number: signed.unsigned.account_number,
sequence: signed.unsigned.sequence,
};
Expand Down
46 changes: 46 additions & 0 deletions rust/chains/tw_binance/src/signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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 tw_hash::{concat, H512, H520};
use tw_keypair::ecdsa::secp256k1;
use tw_keypair::KeyPairError;
use tw_memory::Data;
use tw_misc::traits::ToBytesVec;

pub struct BinanceSignature {
signature: H512,
}

impl BinanceSignature {
pub fn to_bytes(&self) -> Data {
self.signature.to_vec()
}
}

impl From<secp256k1::Signature> for BinanceSignature {
fn from(sign: secp256k1::Signature) -> Self {
BinanceSignature {
signature: concat(sign.r(), sign.s()),
}
}
}

impl<'a> TryFrom<&'a [u8]> for BinanceSignature {
type Error = KeyPairError;

fn try_from(signature_bytes: &'a [u8]) -> Result<Self, Self::Error> {
let signature_slice = if signature_bytes.len() == H520::len() {
// Discard the last `v` recovery byte.
&signature_bytes[0..H512::len()]
} else {
signature_bytes
};

let signature =
H512::try_from(signature_slice).map_err(|_| KeyPairError::InvalidSignature)?;
Ok(BinanceSignature { signature })
}
}
8 changes: 3 additions & 5 deletions rust/chains/tw_binance/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

use crate::modules::serializer::BinanceAminoSerializer;
use crate::modules::tx_builder::TxBuilder;
use crate::signature::BinanceSignature;
use crate::transaction::{JsonTxPreimage, SignerInfo};
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::error::SigningResult;
use tw_coin_entry::signing_output_error;
use tw_hash::{concat, H512};
use tw_keypair::ecdsa::secp256k1;
use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait};
use tw_proto::Binance::Proto;
Expand All @@ -35,10 +35,8 @@ impl BinanceSigner {

let key_pair = secp256k1::KeyPair::try_from(input.private_key.as_ref())?;

let signature = key_pair.sign(tx_hash)?;
let signature: H512 = concat(signature.r(), signature.s());

let public_key = key_pair.public().compressed();
let signature = BinanceSignature::from(key_pair.sign(tx_hash)?);
let public_key = key_pair.public().clone();

let signed_tx = unsigned_tx.into_signed(SignerInfo {
public_key,
Expand Down
8 changes: 5 additions & 3 deletions rust/chains/tw_binance/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use crate::signature::BinanceSignature;
use crate::transaction::message::BinanceMessageBox;
use serde::Serialize;
use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult};
use tw_hash::{sha2, H256, H264, H512};
use tw_hash::{sha2, H256};
use tw_keypair::ecdsa::secp256k1;
use tw_memory::Data;
use tw_misc::serde::as_string;

Expand Down Expand Up @@ -54,8 +56,8 @@ impl UnsignedTransaction {
}

pub struct SignerInfo {
pub public_key: H264,
pub signature: H512,
pub public_key: secp256k1::PublicKey,
pub signature: BinanceSignature,
}

pub struct SignedTransaction {
Expand Down
67 changes: 66 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 @@ -5,14 +5,19 @@
// 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_transaction_compiler::{
tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes,
};
use std::marker::PhantomData;
use tw_coin_registry::coin_type::CoinType;
use tw_memory::test_utils::tw_data_helper::TWDataHelper;
use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper;
use tw_memory::Data;
use tw_proto::{deserialize, serialize, MessageRead, MessageWrite};

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

Expand All @@ -29,3 +34,63 @@ impl<'a, Output: MessageRead<'a>> AnySignerHelper<'a, Output> {
output
}
}

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

impl<'a, Output: MessageRead<'a>> PreImageHelper<'a, Output> {
pub fn pre_image_hashes<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_transaction_compiler_pre_image_hashes(coin_type as u32, input_data.ptr())
})
.to_vec()
.expect("!tw_transaction_compiler_pre_image_hashes returned nullptr");

let output: Output = deserialize(&self.output_data).unwrap();
output
}
}

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

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

let signatures = TWDataVectorHelper::create(signatures);
let public_keys = TWDataVectorHelper::create(public_keys);

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

let output: Output = deserialize(&self.output_data).unwrap();
output
}
}
69 changes: 68 additions & 1 deletion rust/tw_any_coin/tests/chains/binance/binance_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,74 @@
// 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 tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper};
use tw_coin_registry::coin_type::CoinType;
use tw_encoding::hex::DecodeHex;
use tw_encoding::hex::ToHex;
use tw_proto::Binance::Proto;
use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderType;
use tw_proto::Common::Proto::SigningError;
use tw_proto::TxCompiler::Proto as CompilerProto;

#[test]
fn test_binance_compile() {
todo!()
// bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2
let from_address_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6";
// bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5
let to_address_key_hash = "bffe47abfaede50419c577f1074fee6dd1535cd1";

let send_order = Proto::SendOrder {
inputs: vec![Proto::mod_SendOrder::Input {
address: from_address_key_hash.decode_hex().unwrap().into(),
coins: vec![make_token("BNB", 1)],
}],
outputs: vec![Proto::mod_SendOrder::Output {
address: to_address_key_hash.decode_hex().unwrap().into(),
coins: vec![make_token("BNB", 1)],
}],
};
let input = Proto::SigningInput {
// testnet chainId
chain_id: "Binance-Chain-Nile".into(),
order_oneof: OrderType::send_order(send_order),
..Proto::SigningInput::default()
};

// Step 2: Obtain preimage hash
let mut pre_imager = PreImageHelper::<CompilerProto::PreSigningOutput>::default();
let preimage_output = pre_imager.pre_image_hashes(CoinType::Binance, &input);

assert_eq!(preimage_output.error, SigningError::OK);
assert_eq!(
preimage_output.data.to_hex(),
"7b226163636f756e745f6e756d626572223a2230222c22636861696e5f6964223a2242696e616e63652d436861696e2d4e696c65222c2264617461223a6e756c6c2c226d656d6f223a22222c226d736773223a5b7b22696e70757473223a5b7b2261646472657373223a22626e623167727066303935356830796b7a71336172356e6d756d3779366764666c366c78666e34366832222c22636f696e73223a5b7b22616d6f756e74223a312c2264656e6f6d223a22424e42227d5d7d5d2c226f757470757473223a5b7b2261646472657373223a22626e6231686c6c7930326c3661686a7367787739776c6373776e6c776468673478687833387978706435222c22636f696e73223a5b7b22616d6f756e74223a312c2264656e6f6d223a22424e42227d5d7d5d7d5d2c2273657175656e6365223a2230222c22736f75726365223a2230227d"
);
assert_eq!(
preimage_output.data_hash.to_hex(),
"3f3fece9059e714d303a9a1496ddade8f2c38fa78fc4cc2e505c5dbb0ea678d1"
);

// Step 3: Compile transaction info

// Simulate signature, normally obtained from signature server.
let signature = "1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"
.decode_hex()
.unwrap();
let public_key = "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502"
.decode_hex()
.unwrap();

let mut compiler = CompilerHelper::<Proto::SigningOutput>::default();
let output = compiler.compile(CoinType::Binance, &input, vec![signature], vec![public_key]);

assert_eq!(output.error, SigningError::OK);
let expected_tx = concat!(
"b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001",
"121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35",
"920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212401b1181faec30b60a2ddaa2804c",
"253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5",
"04f9e8310679",
);
assert_eq!(output.encoded.to_hex(), expected_tx);
}
9 changes: 2 additions & 7 deletions rust/tw_any_coin/tests/chains/binance/binance_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +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::chains::binance::make_token;
use tw_any_coin::test_utils::sign_utils::AnySignerHelper;
use tw_coin_registry::coin_type::CoinType;
use tw_encoding::hex::{DecodeHex, ToHex};
Expand All @@ -19,13 +20,6 @@ const ACCOUNT_15_PRIVATE_KEY: &str =
const ACCOUNT_16_PRIVATE_KEY: &str =
"851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a";

fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token {
Proto::mod_SendOrder::Token {
denom: denom.into(),
amount,
}
}

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

Expand Down
9 changes: 9 additions & 0 deletions rust/tw_any_coin/tests/chains/binance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

use tw_proto::Binance::Proto;

mod binance_address;
mod binance_compile;
mod binance_sign;

fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token {
Proto::mod_SendOrder::Token {
denom: denom.into(),
amount,
}
}

0 comments on commit 63f33cc

Please sign in to comment.