Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Rust/Greenfield]: Move BNB Greenfield to Rust #3597

Merged
merged 22 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9072394
[Greenfield]: Generate Rust skeleton for BNB Greenfield
satoshiotomakan Dec 12, 2023
04218b8
[Greenfield]: Add `Eip712Preimager`
satoshiotomakan Dec 13, 2023
b99db7a
[Greenfield]: Continue implementing tx signing
satoshiotomakan Dec 15, 2023
ad597a2
[Greenfield]: Small refactoring
satoshiotomakan Dec 18, 2023
89b3343
[Greenfield]: Add a signer test, fix bugs
satoshiotomakan Dec 18, 2023
99ef08b
[Greenfield]: Add support for TransferOut message
satoshiotomakan Dec 18, 2023
d92d3db
[Greenfield]: Finish implementing `Signer` and `Compiler`
satoshiotomakan Dec 18, 2023
94e2a17
Merge branch 'master' into s/rust-greenfield
satoshiotomakan Dec 18, 2023
efe54f7
[Greenfield]: Add a signing test, fix Signer
satoshiotomakan Dec 18, 2023
d1ad27b
[Greenfield]: Add missing tests, mainnet as well
satoshiotomakan Dec 19, 2023
db7dc15
[Greenfield]: Add compile test
satoshiotomakan Dec 19, 2023
13c8cce
[Greenfield]: Fix TODO
satoshiotomakan Dec 19, 2023
b095a17
[Greenfield]: Remove C++ implementation
satoshiotomakan Dec 19, 2023
cf84b8d
[Greenfield]: Minor changes
satoshiotomakan Dec 19, 2023
64df15f
[Greenfield]: Move Cosmos protobuf directory to `tw_cosmos_sdk`
satoshiotomakan Dec 19, 2023
31077de
[Greenfield]: Add fuzz test
satoshiotomakan Dec 19, 2023
637f35d
[Greenfield]: Update chainId
satoshiotomakan Dec 19, 2023
bf57d74
[CI] Trigger CI
satoshiotomakan Dec 19, 2023
53f7c5d
[Greenfield]: Fix fmt
satoshiotomakan Dec 19, 2023
584d575
[Greenfield]: Fix kmp sample
satoshiotomakan Dec 19, 2023
47b4e6d
Merge branch 'master' into s/rust-greenfield
satoshiotomakan Dec 20, 2023
cb7fd4b
Merge branch 'master' into s/rust-greenfield
satoshiotomakan Dec 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's happening here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuzz/Cargo.toml is generated by calling cargo fuzz init command.
So this is a workaround for the tw_greenfield/fuzz to not be affected by the parent 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
Loading