diff --git a/registry.json b/registry.json index 76a4ce09650..9549eaf618e 100644 --- a/registry.json +++ b/registry.json @@ -4335,7 +4335,7 @@ "coinId": 10000060, "symbol": "INJ", "decimals": 18, - "blockchain": "Cosmos", + "blockchain": "NativeInjective", "derivation": [ { "path": "m/44'/60'/0'/0/0" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2508ba241b6..d4bc750c6d2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1519,6 +1519,7 @@ dependencies = [ "tw_memory", "tw_misc", "tw_native_evmos", + "tw_native_injective", "tw_ronin", ] @@ -1696,6 +1697,17 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_native_injective" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_keypair", + "tw_memory", + "tw_proto", +] + [[package]] name = "tw_number" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 3257cf37364..9a6c299e1d6 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,6 +2,7 @@ members = [ "chains/tw_cosmos", "chains/tw_native_evmos", + "chains/tw_native_injective", "tw_any_coin", "tw_bech32_address", "tw_bitcoin", diff --git a/rust/chains/tw_native_injective/Cargo.toml b/rust/chains/tw_native_injective/Cargo.toml new file mode 100644 index 00000000000..d793539a241 --- /dev/null +++ b/rust/chains/tw_native_injective/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tw_native_injective" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk", features = ["test-utils"] } diff --git a/rust/chains/tw_native_injective/src/context.rs b/rust/chains/tw_native_injective/src/context.rs new file mode 100644 index 00000000000..350b8d1f69a --- /dev/null +++ b/rust/chains/tw_native_injective/src/context.rs @@ -0,0 +1,20 @@ +// 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::injective_public_key::InjectiveEthSecp256PublicKey; +use tw_cosmos_sdk::address::Address; +use tw_cosmos_sdk::context::CosmosContext; +use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; +use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; + +pub struct NativeInjectiveContext; + +impl CosmosContext for NativeInjectiveContext { + type Address = Address; + type TxHasher = Keccak256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = InjectiveEthSecp256PublicKey; +} diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs new file mode 100644 index 00000000000..e14012c468c --- /dev/null +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -0,0 +1,74 @@ +// 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::NativeInjectiveContext; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_cosmos_sdk::address::{Address, CosmosAddress}; +use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; +use tw_keypair::tw; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct NativeInjectiveEntry; + +impl CoinEntry for NativeInjectiveEntry { + type AddressPrefix = NoPrefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Address::from_str_with_coin(coin, address) + } + + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: tw::PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + Address::with_public_key_coin_context(coin, &public_key) + } + + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + TWSigner::::sign(coin, input) + } + + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + _input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + todo!() + } + + fn compile( + &self, + _coin: &dyn CoinContext, + _input: Self::SigningInput<'_>, + _signatures: Vec, + _public_keys: Vec, + ) -> Self::SigningOutput { + todo!() + } +} diff --git a/rust/tw_cosmos_sdk/src/public_key/injective_eth_secp256k1.rs b/rust/chains/tw_native_injective/src/injective_public_key.rs similarity index 79% rename from rust/tw_cosmos_sdk/src/public_key/injective_eth_secp256k1.rs rename to rust/chains/tw_native_injective/src/injective_public_key.rs index 5ac1dfecfba..084d47e94f0 100644 --- a/rust/tw_cosmos_sdk/src/public_key/injective_eth_secp256k1.rs +++ b/rust/chains/tw_native_injective/src/injective_public_key.rs @@ -4,22 +4,15 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -// 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::proto::injective; -use crate::public_key::secp256k1::prepare_secp256k1_public_key; -use crate::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; 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::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; use tw_keypair::tw::PrivateKey; use tw_keypair::KeyPairResult; use tw_memory::Data; use tw_proto::{google, to_any}; -// TODO move to tw_injective blockchain pub struct InjectiveEthSecp256PublicKey { public_key: Data, } diff --git a/rust/chains/tw_native_injective/src/lib.rs b/rust/chains/tw_native_injective/src/lib.rs new file mode 100644 index 00000000000..a61663bd72c --- /dev/null +++ b/rust/chains/tw_native_injective/src/lib.rs @@ -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 context; +pub mod entry; +pub mod injective_public_key; diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index d376e463622..d8b5bdcee53 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -5,3 +5,4 @@ // file LICENSE at the root of the source code distribution tree. mod native_evmos; +mod native_injective; diff --git a/rust/tw_any_coin/tests/chains/native_injective/mod.rs b/rust/tw_any_coin/tests/chains/native_injective/mod.rs new file mode 100644 index 00000000000..e0042b31351 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_injective/mod.rs @@ -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 mod native_injective_sign; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs new file mode 100644 index 00000000000..f5d7e448a27 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs @@ -0,0 +1,97 @@ +// 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 std::borrow::Cow; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; +use tw_proto::{deserialize, serialize}; + +const NATIVE_INJECTIVE_COIN_TYPE: u32 = 10000060; + +fn account_17396_private_key() -> Cow<'static, [u8]> { + "9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1" + .decode_hex() + .unwrap() + .into() +} + +fn send_tx_input() -> Proto::SigningInput<'static> { + let send_msg = Proto::mod_Message::Send { + from_address: "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a".into(), + to_address: "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd".into(), + amounts: vec![make_amount("inj", "10000000000")], + ..Proto::mod_Message::Send::default() + }; + Proto::SigningInput { + account_number: 17396, + chain_id: "injective-1".into(), + sequence: 1, + fee: Some(make_fee(110000, make_amount("inj", "100000000000000"))), + private_key: account_17396_private_key(), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + } +} + +#[test] +fn test_sign_native_injective_tx_protobuf() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + ..send_tx_input() + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), NATIVE_INJECTIVE_COIN_TYPE) + }) + .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()); + + // https://www.mintscan.io/injective/txs/135DD2C4A1910E4334A9C0F15125DA992E724EBF23FEB9638FCB71218BB064A5 + assert_eq!( + output.serialized, + r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajEzdTZnN3ZxZ3cwNzRtZ21mMnplMmNhZHp2a3o5c25sd2NydHE4YRIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASngEKfgp0Ci0vaW5qZWN0aXZlLmNyeXB0by52MWJldGExLmV0aHNlY3AyNTZrMS5QdWJLZXkSQwpBBFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXMSBAoCCAEYARIcChYKA2luahIPMTAwMDAwMDAwMDAwMDAwELDbBhpAx2vkplmzeK7n3puCFGPWhLd0l/ZC/CYkGl+stH+3S3hiCvIe7uwwMpUlNaSwvT8HwF1kNUp+Sx2m0Uo1x5xcFw=="}"# + ); + assert_eq!(output.signature.to_hex(), "c76be4a659b378aee7de9b821463d684b77497f642fc26241a5facb47fb74b78620af21eeeec3032952535a4b0bd3f07c05d64354a7e4b1da6d14a35c79c5c17"); +} + +#[test] +fn test_sign_native_injective_tx_json() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::JSON, + ..send_tx_input() + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), NATIVE_INJECTIVE_COIN_TYPE) + }) + .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()); + + // This transaction hasn't been broadcasted. + assert_eq!( + output.json, + r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"100000000000000","denom":"inj"}],"gas":"110000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"10000000000","denom":"inj"}],"from_address":"inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a","to_address":"inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd"}}],"signatures":[{"pub_key":{"type":"injective/PubKeyEthSecp256k1","value":"BFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXM="},"signature":"7wwedceebL95DwbClz5AzEp2Z74itHC7raiV976DcacfjrJ58oDfjbAO5UOZQAlihiYBP7PpyISFQ72FPRhdZA=="}]}}"# + ); + assert_eq!( + output.signature_json, + r#"[{"pub_key":{"type":"injective/PubKeyEthSecp256k1","value":"BFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXM="},"signature":"7wwedceebL95DwbClz5AzEp2Z74itHC7raiV976DcacfjrJ58oDfjbAO5UOZQAlihiYBP7PpyISFQ72FPRhdZA=="}]"# + ); +} diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index a568c0b23b0..4618c552691 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -44,6 +44,7 @@ fn test_any_address_derive() { BlockchainType::Cosmos => continue, BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", BlockchainType::NativeEvmos => "evmos14s0vgnj0pjnazu4hsqlksdk7slah9vcfvt8ssm", + BlockchainType::NativeInjective => "inj14s0vgnj0pjnazu4hsqlksdk7slah9vcfyrp6ct", BlockchainType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", BlockchainType::InternetComputer => { "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa" @@ -88,6 +89,10 @@ fn test_any_address_normalize_eth() { "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", ), + BlockchainType::NativeInjective => ( + "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + ), BlockchainType::Ronin => ( "0xb16db98b365b1f89191996942612b14f1da4bd5f", "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", @@ -139,6 +144,10 @@ fn test_any_address_is_valid_coin() { "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34" ], + BlockchainType::NativeInjective => vec![ + "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", + "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd" + ], BlockchainType::Ronin => vec![ "0xb16db98b365b1f89191996942612b14f1da4bd5f", "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", @@ -182,6 +191,7 @@ fn test_any_address_is_valid_coin_invalid() { "553357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278", ], BlockchainType::NativeEvmos => vec!["evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw"], + BlockchainType::NativeInjective => vec!["ini13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a"], BlockchainType::Unsupported => unreachable!(), }; diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index a73a2963020..29fe4958e1a 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -18,4 +18,5 @@ tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } tw_native_evmos = { path = "../chains/tw_native_evmos" } +tw_native_injective = { path = "../chains/tw_native_injective" } tw_ronin = { path = "../tw_ronin" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index a5d49efb885..ca8cf6805f4 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -18,6 +18,7 @@ pub enum BlockchainType { Ethereum, InternetComputer, NativeEvmos, + NativeInjective, Ronin, Unsupported, } @@ -43,6 +44,7 @@ impl FromStr for BlockchainType { "InternetComputer" => Ok(BlockchainType::InternetComputer), "Ronin" => Ok(BlockchainType::Ronin), "NativeEvmos" => Ok(BlockchainType::NativeEvmos), + "NativeInjective" => Ok(BlockchainType::NativeInjective), _ => Ok(BlockchainType::Unsupported), } } diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 59e30e497f1..12fe0d5f4be 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -16,6 +16,7 @@ use tw_ethereum::entry::EthereumEntry; use tw_evm::evm_entry::EvmEntryExt; use tw_internet_computer::entry::InternetComputerEntry; use tw_native_evmos::entry::NativeEvmosEntry; +use tw_native_injective::entry::NativeInjectiveEntry; use tw_ronin::entry::RoninEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; @@ -26,6 +27,7 @@ const COSMOS: CosmosEntry = CosmosEntry; const ETHEREUM: EthereumEntry = EthereumEntry; const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; const NATIVE_EVMOS: NativeEvmosEntry = NativeEvmosEntry; +const NATIVE_INJECTIVE: NativeInjectiveEntry = NativeInjectiveEntry; const RONIN: RoninEntry = RoninEntry; pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { @@ -35,6 +37,7 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(ÐEREUM), BlockchainType::InternetComputer => Ok(&INTERNET_COMPUTER), BlockchainType::NativeEvmos => Ok(&NATIVE_EVMOS), + BlockchainType::NativeInjective => Ok(&NATIVE_INJECTIVE), BlockchainType::Ronin => Ok(&RONIN), BlockchainType::Unsupported => Err(RegistryError::Unsupported), } @@ -57,6 +60,7 @@ pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Err(RegistryError::Unsupported), BlockchainType::NativeEvmos => Err(RegistryError::Unsupported), + BlockchainType::NativeInjective => Err(RegistryError::Unsupported), BlockchainType::Ronin => Ok(&RONIN), BlockchainType::Unsupported => Err(RegistryError::Unsupported), } diff --git a/rust/tw_cosmos_sdk/src/public_key/mod.rs b/rust/tw_cosmos_sdk/src/public_key/mod.rs index c03a84f74b3..d3a4e20f5e8 100644 --- a/rust/tw_cosmos_sdk/src/public_key/mod.rs +++ b/rust/tw_cosmos_sdk/src/public_key/mod.rs @@ -9,7 +9,6 @@ use tw_keypair::{tw, KeyPairResult}; use tw_memory::Data; use tw_proto::google; -pub mod injective_eth_secp256k1; pub mod secp256k1; pub trait CosmosPublicKey: JsonPublicKey + ProtobufPublicKey + Sized {