Skip to content

Commit

Permalink
feat(ZetaChain): Slightly refactor NativeEvmos and NativeInjective pu…
Browse files Browse the repository at this point in the history
…blic key types
  • Loading branch information
satoshiotomakan committed Jan 25, 2024
1 parent b5beab8 commit 14dfabd
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 43 deletions.
49 changes: 23 additions & 26 deletions rust/chains/tw_native_evmos/src/ethermint_public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,56 @@

use tw_coin_entry::coin_context::CoinContext;
use tw_cosmos_sdk::proto::ethermint;
use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey;
use tw_cosmos_sdk::public_key::{
CosmosPublicKey, JsonPublicKey, ProtobufPublicKey, PublicKeyParams,
};
use tw_keypair::ecdsa::secp256k1;
use tw_keypair::KeyPairResult;
use tw_keypair::{tw, KeyPairResult};
use tw_memory::Data;
use tw_proto::{google, to_any};
use tw_proto::{google, type_url};

pub struct EthermintEthSecp256PublicKey {
public_key: Data,
}
const ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE: &str = "ethermint/PubKeyEthSecp256k1";

pub struct EthermintEthSecp256PublicKey(Secp256PublicKey);

impl EthermintEthSecp256PublicKey {
pub fn new(public_key: &secp256k1::PublicKey) -> KeyPairResult<EthermintEthSecp256PublicKey> {
Ok(EthermintEthSecp256PublicKey {
// NativeEvmos chain requires the public key to be compressed.
// This trick is needed because `registry.json` contains extended public key type.
public_key: public_key.compressed().to_vec(),
})
fn default_public_key_params() -> PublicKeyParams {
PublicKeyParams {
// `NativeEvmos` requires the public key to be compressed,
// however the uncompressed public key is used to generate an address.
public_key_type: tw::PublicKeyType::Secp256k1,
json_type: ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE.to_string(),
protobuf_type_url: type_url::<ethermint::crypto::v1::ethsecp256k1::PubKey>(),
}
}
}

impl CosmosPublicKey for EthermintEthSecp256PublicKey {
fn from_bytes(
_coin: &dyn CoinContext,
coin: &dyn CoinContext,
public_key_bytes: &[u8],
// Ignore custom public key parameters.
_params: Option<PublicKeyParams>,
maybe_params: Option<PublicKeyParams>,
) -> KeyPairResult<Self> {
// `NativeEvmos` requires the public key to be compressed,
// Use default Ethermint public key parameters if otherwise is not specified,
// however the uncompressed public key is used to generate an address.
let public_key = secp256k1::PublicKey::try_from(public_key_bytes)?;
EthermintEthSecp256PublicKey::new(&public_key)
let params = maybe_params.unwrap_or_else(Self::default_public_key_params);
Secp256PublicKey::from_bytes(coin, public_key_bytes, Some(params))
.map(EthermintEthSecp256PublicKey)
}

fn to_bytes(&self) -> Data {
self.public_key.clone()
self.0.to_bytes()
}
}

impl JsonPublicKey for EthermintEthSecp256PublicKey {
fn public_key_type(&self) -> String {
const ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE: &str = "ethermint/PubKeyEthSecp256k1";

ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE.to_string()
self.0.public_key_type()
}
}

impl ProtobufPublicKey for EthermintEthSecp256PublicKey {
fn to_proto(&self) -> google::protobuf::Any {
let proto = ethermint::crypto::v1::ethsecp256k1::PubKey {
key: self.public_key.clone(),
};
to_any(&proto)
self.0.to_proto()
}
}
42 changes: 25 additions & 17 deletions rust/chains/tw_native_injective/src/injective_public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,56 @@

use tw_coin_entry::coin_context::CoinContext;
use tw_cosmos_sdk::proto::injective;
use tw_cosmos_sdk::public_key::secp256k1::prepare_secp256k1_public_key;
use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey;
use tw_cosmos_sdk::public_key::{
CosmosPublicKey, JsonPublicKey, ProtobufPublicKey, PublicKeyParams,
};
use tw_keypair::KeyPairResult;
use tw_memory::Data;
use tw_proto::{google, to_any};
use tw_proto::{google, type_url};

pub struct InjectiveEthSecp256PublicKey {
public_key: Data,
/// https://github.com/cosmostation/cosmostation-chrome-extension/blob/e2fd27d71a17993f8eef07ce30f7a04a32e52788/src/constants/cosmos.ts#L4
const INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE: &str = "injective/PubKeyEthSecp256k1";

pub struct InjectiveEthSecp256PublicKey(Secp256PublicKey);

impl InjectiveEthSecp256PublicKey {
fn default_public_key_params(coin: &dyn CoinContext) -> PublicKeyParams {
PublicKeyParams {
// `NativeInjective` uses the same public key type as specified in `registry.json`.
public_key_type: coin.public_key_type(),
json_type: INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE.to_string(),
protobuf_type_url: type_url::<injective::crypto::v1beta1::ethsecp256k1::PubKey>(),
}
}
}

impl CosmosPublicKey for InjectiveEthSecp256PublicKey {
fn from_bytes(
coin: &dyn CoinContext,
public_key_bytes: &[u8],
// Ignore custom public key parameters.
_params: Option<PublicKeyParams>,
maybe_params: Option<PublicKeyParams>,
) -> KeyPairResult<Self> {
let public_key = prepare_secp256k1_public_key(coin.public_key_type(), public_key_bytes)?;
Ok(InjectiveEthSecp256PublicKey { public_key })
// Use default Ethermint public key parameters if otherwise is not specified,
// however the uncompressed public key is used to generate an address.
let params = maybe_params.unwrap_or_else(|| Self::default_public_key_params(coin));
Secp256PublicKey::from_bytes(coin, public_key_bytes, Some(params))
.map(InjectiveEthSecp256PublicKey)
}

fn to_bytes(&self) -> Data {
self.public_key.clone()
self.0.to_bytes()
}
}

impl JsonPublicKey for InjectiveEthSecp256PublicKey {
fn public_key_type(&self) -> String {
/// https://github.com/cosmostation/cosmostation-chrome-extension/blob/e2fd27d71a17993f8eef07ce30f7a04a32e52788/src/constants/cosmos.ts#L4
const INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE: &str = "injective/PubKeyEthSecp256k1";

INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE.to_string()
self.0.public_key_type()
}
}

impl ProtobufPublicKey for InjectiveEthSecp256PublicKey {
fn to_proto(&self) -> google::protobuf::Any {
let proto = injective::crypto::v1beta1::ethsecp256k1::PubKey {
key: self.public_key.clone(),
};
to_any(&proto)
self.0.to_proto()
}
}
65 changes: 65 additions & 0 deletions rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,68 @@ fn test_any_signer_sign_cosmos() {
let expected = r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"}"#;
assert_eq!(output.serialized, expected);
}

/// Tests if it is possible to sign a transaction for a custom chain using `CoinType::Cosmos`.
/// Successfully broadcasted (testnet):
/// https://explorer.zetachain.com/cosmos/tx/A2FC8816657856ED274C4418C3CAEAEE645561275F6C63AB5F8B1DCFB37341A0
#[test]
fn test_any_signer_sign_custom_chain() {
use tw_proto::Cosmos::Proto;
use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum;

let send_msg = Proto::mod_Message::Send {
from_address: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne".into(),
to_address: "zeta1cscf4ldnkkz7f0wpveur6dpd0d6p2zxnsuu70y".into(),
amounts: vec![Proto::Amount {
denom: "azeta".into(),
// 0.3 ZETA
amount: "300000000000000000".into(),
}],
..Proto::mod_Message::Send::default()
};

let input = Proto::SigningInput {
signing_mode: Proto::SigningMode::Protobuf,
account_number: 2726346,
chain_id: "athens_7001-1".into(),
sequence: 2,
fee: Some(Proto::Fee {
gas: 200000,
amounts: vec![],
}),
private_key: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed"
.decode_hex()
.unwrap()
.into(),
messages: vec![Proto::Message {
message_oneof: MessageEnum::send_coins_message(send_msg),
}],
// Use a different Transaction hashing algorithm.
tx_hasher: Proto::TxHasher::Keccak256,
signer_info: Some(Proto::SignerInfo {
// Zetachain requires a compressed public key to sign a transaction,
// however an uncompressed public key is used to generate address.
public_key_type: Proto::SignerPublicKeyType::Secp256k1,
json_type: "ethermint/PubKeyEthSecp256k1".into(),
protobuf_type: "/ethermint.crypto.v1.ethsecp256k1.PubKey".into(),
}),
..Proto::SigningInput::default()
};

let input_data = TWDataHelper::create(serialize(&input).unwrap());

let output = TWDataHelper::wrap(unsafe {
tw_any_signer_sign(input_data.ptr(), CoinType::Cosmos as u32)
})
.to_vec()
.expect("!tw_any_signer_sign returned nullptr");

let output: Proto::SigningOutput = deserialize(&output).unwrap();
assert_eq!(output.error, SigningErrorType::OK);
assert!(output.error_message.is_empty());

assert_eq!(
output.serialized,
r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKK3pldGExNHB5MzZzeDU3dWQ4MnQ5eXJrczl6Nmhkc3JwbjV4NmtteHMwbmUSK3pldGExY3NjZjRsZG5ra3o3ZjB3cHZldXI2ZHBkMGQ2cDJ6eG5zdXU3MHkaGwoFYXpldGESEjMwMDAwMDAwMDAwMDAwMDAwMBJhClkKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiECho5+FjRBfbKt/Z/jggW/oP6gGJin/TBWXRP3BWo3wGUSBAoCCAEYAhIEEMCaDBpAgGvqca0w2N9wnHnnxS9HiVud4aQ9lNCumzgNIW6wOR4kvPScacGS1G3kwCw7wyI2NJL8M1eVYjafFIt2FpKl3w=="}"#
);
}

0 comments on commit 14dfabd

Please sign in to comment.