Skip to content

Commit

Permalink
[Rust/Greenfield]: Move BNB Greenfield to Rust (#3597)
Browse files Browse the repository at this point in the history
* [Greenfield]: Generate Rust skeleton for BNB Greenfield

* [Greenfield]: Add `Eip712Preimager`

* [Greenfield]: Continue implementing tx signing

* Add `TxBuilder`
* TODO add `TransferOut` message

* [Greenfield]: Small refactoring

* [Greenfield]: Add a signer test, fix bugs

* [Greenfield]: Add support for TransferOut message

* [Greenfield]: Finish implementing `Signer` and `Compiler`

* [Greenfield]: Add a signing test, fix Signer

* [Greenfield]: Add missing tests, mainnet as well

* [Greenfield]: Add compile test

* [Greenfield]: Fix TODO

* [Greenfield]: Remove C++ implementation

* [Greenfield]: Minor changes

* [Greenfield]: Move Cosmos protobuf directory to `tw_cosmos_sdk`

* [Greenfield]: Add fuzz test

* [Greenfield]: Update chainId

* [CI] Trigger CI

* [Greenfield]: Fix fmt

* [Greenfield]: Fix kmp sample
  • Loading branch information
satoshiotomakan authored Dec 21, 2023
1 parent 1d00318 commit d2d66f2
Show file tree
Hide file tree
Showing 86 changed files with 2,417 additions and 1,322 deletions.
2 changes: 1 addition & 1 deletion registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -4180,7 +4180,7 @@
"coinId": 5600,
"symbol": "BNB",
"decimals": 18,
"chainId": "9000",
"chainId": "1017",
"blockchain": "Greenfield",
"derivation": [
{
Expand Down
19 changes: 19 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"chains/tw_binance",
"chains/tw_cosmos",
"chains/tw_greenfield",
"chains/tw_native_evmos",
"chains/tw_native_injective",
"chains/tw_thorchain",
Expand Down
22 changes: 22 additions & 0 deletions rust/chains/tw_greenfield/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "tw_greenfield"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tw_coin_entry = { path = "../../tw_coin_entry" }
tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" }
tw_encoding = { path = "../../tw_encoding" }
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_number = { path = "../../tw_number" }
tw_proto = { path = "../../tw_proto" }

[dev-dependencies]
tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] }
tw_misc = { path = "../../tw_misc", features = ["test-utils"] }
5 changes: 5 additions & 0 deletions rust/chains/tw_greenfield/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target
corpus
artifacts
coverage
Cargo.lock
30 changes: 30 additions & 0 deletions rust/chains/tw_greenfield/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "tw_greenfield-fuzz"
version = "0.0.0"
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
tw_any_coin = { path = "../../../tw_any_coin", features = ["test-utils"] }
tw_coin_registry = { path = "../../../tw_coin_registry" }
tw_proto = { path = "../../../tw_proto", features = ["fuzz"] }

[dependencies.tw_greenfield]
path = ".."

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[profile.release]
debug = 1

[[bin]]
name = "sign"
path = "fuzz_targets/sign.rs"
test = false
doc = false
11 changes: 11 additions & 0 deletions rust/chains/tw_greenfield/fuzz/fuzz_targets/sign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![no_main]

use libfuzzer_sys::fuzz_target;
use tw_any_coin::test_utils::sign_utils::AnySignerHelper;
use tw_coin_registry::coin_type::CoinType;
use tw_proto::Greenfield::Proto;

fuzz_target!(|input: Proto::SigningInput<'_>| {
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
let _ = signer.sign(CoinType::Greenfield, input);
});
56 changes: 56 additions & 0 deletions rust/chains/tw_greenfield/src/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 serde::Serialize;
use std::fmt;
use std::str::FromStr;
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_evm::address::Address as EthereumAddress;
use tw_keypair::ecdsa::secp256k1;
use tw_memory::Data;

#[derive(Clone, Serialize)]
pub struct GreenfieldAddress(EthereumAddress);

impl GreenfieldAddress {
/// Initializes an address with a `secp256k1` public key.
pub fn with_secp256k1_pubkey(pubkey: &secp256k1::PublicKey) -> GreenfieldAddress {
GreenfieldAddress(EthereumAddress::with_secp256k1_pubkey(pubkey))
}
}

impl CosmosAddress for GreenfieldAddress {
fn from_str_with_coin(_coin: &dyn CoinContext, addr: &str) -> AddressResult<Self>
where
Self: Sized,
{
GreenfieldAddress::from_str(addr)
}
}

impl CoinAddress for GreenfieldAddress {
#[inline]
fn data(&self) -> Data {
self.0.data()
}
}

impl FromStr for GreenfieldAddress {
type Err = AddressError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
EthereumAddress::from_str(s).map(GreenfieldAddress)
}
}

impl fmt::Display for GreenfieldAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
111 changes: 111 additions & 0 deletions rust/chains/tw_greenfield/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// 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::context::GreenfieldContext;
use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage};
use crate::modules::tx_builder::TxBuilder;
use crate::public_key::GreenfieldPublicKey;
use crate::signature::GreenfieldSignature;
use std::borrow::Cow;
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::{SigningError, SigningErrorType, SigningResult};
use tw_coin_entry::signing_output_error;
use tw_cosmos_sdk::modules::broadcast_msg::{BroadcastMode, BroadcastMsg};
use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer;
use tw_cosmos_sdk::modules::serializer::protobuf_serializer::ProtobufSerializer;
use tw_cosmos_sdk::public_key::CosmosPublicKey;
use tw_misc::traits::ToBytesVec;
use tw_proto::Greenfield::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;

pub struct GreenfieldCompiler;

impl GreenfieldCompiler {
/// Please note that [`Proto::SigningInput::public_key`] must be set.
/// If the public key should be derived from a private key, please do it before this method is called.
#[inline]
pub fn preimage_hashes(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
) -> CompilerProto::PreSigningOutput<'static> {
Self::preimage_hashes_impl(coin, input)
.unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e))
}

fn preimage_hashes_impl(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
) -> SigningResult<CompilerProto::PreSigningOutput<'static>> {
let unsigned = TxBuilder::unsigned_tx_from_proto(coin, &input)?;
let Eip712TxPreimage { eip712_tx, tx_hash } = Eip712Signer::preimage_hash(&unsigned)?;

Ok(CompilerProto::PreSigningOutput {
data: Cow::from(eip712_tx.to_vec()),
data_hash: Cow::from(tx_hash.to_vec()),
..CompilerProto::PreSigningOutput::default()
})
}

#[inline]
pub fn compile(
coin: &dyn CoinContext,
input: Proto::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> Proto::SigningOutput<'static> {
Self::compile_impl(coin, input, signatures, public_keys)
.unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e))
}

pub(crate) fn compile_impl(
coin: &dyn CoinContext,
mut input: Proto::SigningInput<'_>,
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
let SingleSignaturePubkey {
signature: raw_signature,
public_key,
} = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?;

let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key)?;
let signature = GreenfieldSignature::try_from(raw_signature.as_slice())?;
let signature_bytes = signature.to_vec();

// Set the public key. It will be used to construct a signer info.
input.public_key = Cow::from(public_key.to_bytes());
let unsigned = TxBuilder::unsigned_tx_from_proto(coin, &input)?;

let signed_tx = unsigned.into_signed(signature);
let signed_tx_raw = ProtobufSerializer::<GreenfieldContext>::build_signed_tx(&signed_tx)?;

let broadcast_mode = Self::broadcast_mode(input.mode);
let broadcast_tx = BroadcastMsg::raw(broadcast_mode, &signed_tx_raw).to_json_string();

let signature_json = JsonSerializer::<GreenfieldContext>::serialize_signature(
&public_key,
signature_bytes.clone(),
);
let signature_json = serde_json::to_string(&[signature_json])
.map_err(|_| SigningError(SigningErrorType::Error_internal))?;

Ok(Proto::SigningOutput {
signature: Cow::from(signature_bytes),
signature_json: Cow::from(signature_json),
serialized: Cow::from(broadcast_tx),
..Proto::SigningOutput::default()
})
}

fn broadcast_mode(input: Proto::BroadcastMode) -> BroadcastMode {
match input {
Proto::BroadcastMode::SYNC => BroadcastMode::Sync,
Proto::BroadcastMode::ASYNC => BroadcastMode::Async,
}
}
}
23 changes: 23 additions & 0 deletions rust/chains/tw_greenfield/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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::GreenfieldAddress;
use crate::public_key::GreenfieldPublicKey;
use tw_cosmos_sdk::context::CosmosContext;
use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher;
use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey;
use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature;

pub struct GreenfieldContext;

impl CosmosContext for GreenfieldContext {
type Address = GreenfieldAddress;
/// Greenfield uses EIP712 message signing algorithm built upon `keccak256` hash.
type TxHasher = Keccak256Hasher;
type PrivateKey = Secp256PrivateKey;
type PublicKey = GreenfieldPublicKey;
type Signature = Secp256k1Signature;
}
Loading

0 comments on commit d2d66f2

Please sign in to comment.