diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d1e1d9c7423..b1f26257f82 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1899,6 +1899,7 @@ dependencies = [ "tw_solana", "tw_substrate", "tw_sui", + "tw_tezos", "tw_thorchain", "tw_ton", "tw_utxo", @@ -2352,12 +2353,28 @@ dependencies = [ "tw_number", "tw_proto", "tw_solana", + "tw_tezos", "tw_ton", "tw_ton_sdk", "tw_utxo", "wallet-core-rs", ] +[[package]] +name = "tw_tezos" +version = "0.1.0" +dependencies = [ + "tw_base58_address", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_number", + "tw_proto", +] + [[package]] name = "tw_thorchain" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 09ab546a720..9eb06d74f78 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -21,6 +21,7 @@ members = [ "chains/tw_ronin", "chains/tw_solana", "chains/tw_sui", + "chains/tw_tezos", "chains/tw_thorchain", "chains/tw_ton", "chains/tw_zcash", diff --git a/rust/chains/tw_tezos/Cargo.toml b/rust/chains/tw_tezos/Cargo.toml new file mode 100644 index 00000000000..755f43bf5ab --- /dev/null +++ b/rust/chains/tw_tezos/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tw_tezos" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_base58_address = { path = "../../tw_base58_address" } +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_encoding = { path = "../../tw_encoding" } +tw_keypair = { path = "../../tw_keypair" } +tw_hash = { path = "../../tw_hash" } +tw_memory = { path = "../../tw_memory" } +tw_misc = { path = "../../tw_misc", features = ["serde"] } +tw_proto = { path = "../../tw_proto" } +zeroize = "1.8.1" + +[dev-dependencies] +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } +tw_number = { path = "../../tw_number", features = ["helpers"] } diff --git a/rust/chains/tw_tezos/src/address.rs b/rust/chains/tw_tezos/src/address.rs new file mode 100644 index 00000000000..a2e736688eb --- /dev/null +++ b/rust/chains/tw_tezos/src/address.rs @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::fmt; +use std::str::FromStr; +use tw_base58_address::Base58Address; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::prelude::*; +use tw_encoding::base58::Alphabet; +use tw_hash::blake2::blake2_b; +use tw_hash::sha2::Sha256d; +use tw_hash::H160; +use tw_keypair::{ + ecdsa, ed25519, + tw::{PublicKey, PublicKeyType}, +}; +use tw_memory::Data; + +use crate::forging::forge_public_key_hash; + +pub const TEZOS_ADDRESS_SIZE: usize = 23; +pub const TEZOS_ADDRESS_PREFIX_SIZE: usize = 3; +pub const TEZOS_ADDRESS_PUBLIC_KEY_HASH_SIZE: usize = 20; +pub const TEZOS_ADDRESS_CHECKSUM_SIZE: usize = 4; + +pub const TEZOS_ADDRESS_SECP256K1_PREFIX: [u8; TEZOS_ADDRESS_PREFIX_SIZE] = [0x06, 0xa1, 0xa1]; +pub const TEZOS_ADDRESS_ED25519_PREFIX: [u8; TEZOS_ADDRESS_PREFIX_SIZE] = [0x06, 0xa1, 0x9f]; +pub const TEZOS_ADDRESS_OTHER_PREFIX: [u8; TEZOS_ADDRESS_PREFIX_SIZE] = [0x06, 0xa1, 0xa4]; +pub const TEZOS_ADDRESS_KT1_PREFIX: [u8; TEZOS_ADDRESS_PREFIX_SIZE] = [0x02, 0x5a, 0x79]; + +pub const ACCOUNT_ZERO_BYTES: [u8; TEZOS_ADDRESS_SIZE] = [0; TEZOS_ADDRESS_SIZE]; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TezosAddress(Base58Address); + +impl TezosAddress { + fn new(public_key_hash: &[u8], prefix: &[u8]) -> AddressResult { + if prefix.len() != TEZOS_ADDRESS_PREFIX_SIZE { + return Err(AddressError::InvalidInput); + } + if public_key_hash.len() != TEZOS_ADDRESS_PUBLIC_KEY_HASH_SIZE { + return Err(AddressError::InvalidInput); + } + let mut bytes: Data = Vec::with_capacity(prefix.len() + public_key_hash.len()); + bytes.extend_from_slice(prefix); + bytes.extend_from_slice(public_key_hash); + Base58Address::new(&bytes, Alphabet::Bitcoin).map(TezosAddress) + } + + pub fn with_public_key(public_key: &PublicKey) -> AddressResult { + if public_key.public_key_type() == PublicKeyType::Secp256k1 { + TezosAddress::with_secp256k1_public_key( + public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)?, + ) + } else if public_key.public_key_type() == PublicKeyType::Ed25519 { + TezosAddress::with_ed25519_public_key( + public_key + .to_ed25519() + .ok_or(AddressError::PublicKeyTypeMismatch)?, + ) + } else { + Err(AddressError::InvalidInput) + } + } + + pub fn with_secp256k1_public_key( + public_key: &ecdsa::secp256k1::PublicKey, + ) -> AddressResult { + let bytes = blake2_b( + public_key.compressed().as_slice(), + TEZOS_ADDRESS_PUBLIC_KEY_HASH_SIZE, + )?; + TezosAddress::new(&bytes, &TEZOS_ADDRESS_SECP256K1_PREFIX) + } + + pub fn with_ed25519_public_key( + public_key: &ed25519::sha512::PublicKey, + ) -> AddressResult { + let bytes = blake2_b(public_key.as_slice(), TEZOS_ADDRESS_PUBLIC_KEY_HASH_SIZE)?; + TezosAddress::new(&bytes, &TEZOS_ADDRESS_ED25519_PREFIX) + } + + /// Address bytes excluding the prefix (skip first 3 bytes). + pub fn bytes(&self) -> &[u8] { + &self.0.as_ref()[TEZOS_ADDRESS_PREFIX_SIZE..] + } + + /// Returns public key hash associated with the address. + pub fn public_key_hash(&self) -> H160 { + H160::try_from(&self.0.as_ref()[TEZOS_ADDRESS_PREFIX_SIZE..]) + .expect("Expected exactly 20 bytes public key hash") + } + + pub fn forge(&self) -> AddressResult { + // normal address + // https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/codec.ts#L183 + let prefix = &self.0.bytes[..TEZOS_ADDRESS_PREFIX_SIZE]; + if prefix == TEZOS_ADDRESS_SECP256K1_PREFIX + || prefix == TEZOS_ADDRESS_ED25519_PREFIX + || prefix == TEZOS_ADDRESS_OTHER_PREFIX + { + let mut forged = vec![0x00]; + let forged_public_key_hash = + forge_public_key_hash(&self.to_string()).map_err(|_| AddressError::InvalidInput)?; + forged.extend_from_slice(&forged_public_key_hash); + Ok(forged) + } + // contract address + // https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/codec.ts#L183 + else if prefix == TEZOS_ADDRESS_KT1_PREFIX { + let mut forged = vec![0x01]; + forged.extend_from_slice(self.bytes()); + forged.push(0x00); + Ok(forged) + } else { + Err(AddressError::InvalidInput) + } + } +} + +impl Default for TezosAddress { + fn default() -> Self { + Base58Address::new(ACCOUNT_ZERO_BYTES.as_slice(), Alphabet::Bitcoin) + .map(TezosAddress) + .expect("'ACCOUNT_ZERO_BYTES' is expected to be valid address bytes") + } +} + +impl CoinAddress for TezosAddress { + #[inline] + fn data(&self) -> Data { + self.bytes().to_vec() + } +} + +impl FromStr for TezosAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let base58_addr = Base58Address::::from_str_with_alphabet(s, Alphabet::Bitcoin)?; + let prefix = &base58_addr.bytes[..TEZOS_ADDRESS_PREFIX_SIZE]; + if prefix != TEZOS_ADDRESS_SECP256K1_PREFIX + && prefix != TEZOS_ADDRESS_ED25519_PREFIX + && prefix != TEZOS_ADDRESS_OTHER_PREFIX + && prefix != TEZOS_ADDRESS_KT1_PREFIX + { + return Err(AddressError::UnexpectedAddressPrefix); + } + Ok(TezosAddress(base58_addr)) + } +} + +impl fmt::Display for TezosAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/rust/chains/tw_tezos/src/binary_coding.rs b/rust/chains/tw_tezos/src/binary_coding.rs new file mode 100644 index 00000000000..dcb544c924a --- /dev/null +++ b/rust/chains/tw_tezos/src/binary_coding.rs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_encoding::{ + base58::{self, Alphabet, CHECKSUM_LEN}, + hex, EncodingError, +}; +use tw_hash::sha2::sha256_d; +use tw_keypair::{ecdsa, ed25519, KeyPairError}; +use tw_memory::Data; +use zeroize::Zeroizing; + +use crate::address::TEZOS_ADDRESS_PREFIX_SIZE; + +const TEZOS_KEY_PREFIX_SIZE: usize = 4; + +pub fn decode_check(input: &str, alphabet: Alphabet) -> Result { + let data = base58::decode(input, alphabet)?; + if data.len() < CHECKSUM_LEN { + return Err(EncodingError::InvalidInput); + } + + let checksum = &data[data.len() - CHECKSUM_LEN..]; + let hash = sha256_d(&data[..data.len() - CHECKSUM_LEN]); + if checksum != &hash[..CHECKSUM_LEN] { + return Err(EncodingError::InvalidInput); + } + Ok(Data::from(&data[..data.len() - CHECKSUM_LEN])) +} + +pub fn encode_check(data: &[u8], alphabet: Alphabet) -> String { + let hash = sha256_d(data); + let mut to_be_encoded = Vec::from(data); + to_be_encoded.extend_from_slice(&hash[..CHECKSUM_LEN]); + base58::encode(&to_be_encoded, alphabet) +} + +pub fn base58_to_hex(input: &str, prefix_length: usize) -> Result { + let decoded = match decode_check(input, Alphabet::Bitcoin) { + Ok(d) => d, + Err(_) => return Err(EncodingError::InvalidInput), + }; + + if decoded.len() < prefix_length { + return Err(EncodingError::InvalidInput); + } + + Ok(hex::encode(&decoded[prefix_length..], false)) +} + +pub fn parse_public_key(public_key: &str) -> Result { + let decoded = + decode_check(public_key, Alphabet::Bitcoin).map_err(|_| KeyPairError::InvalidPublicKey)?; + + let ed25519_prefix = [13, 15, 37, 217]; + let secp256k1_prefix = [3, 254, 226, 86]; + + if decoded.starts_with(&ed25519_prefix) { + if decoded.len() != 32 + TEZOS_KEY_PREFIX_SIZE { + return Err(KeyPairError::InvalidPublicKey); + } + Ok(tw_keypair::tw::PublicKey::Ed25519( + ed25519::sha512::PublicKey::try_from(&decoded[TEZOS_KEY_PREFIX_SIZE..])?, + )) + } else if decoded.starts_with(&secp256k1_prefix) { + if decoded.len() != 33 + TEZOS_KEY_PREFIX_SIZE { + return Err(KeyPairError::InvalidPublicKey); + } + Ok(tw_keypair::tw::PublicKey::Secp256k1( + ecdsa::secp256k1::PublicKey::try_from(&decoded[TEZOS_KEY_PREFIX_SIZE..])?, + )) + } else { + Err(KeyPairError::InvalidPublicKey) + } +} + +pub fn parse_private_key(private_key: &str) -> Result { + let decoded = Zeroizing::new( + decode_check(private_key, Alphabet::Bitcoin).map_err(|_| KeyPairError::InvalidSecretKey)?, + ); + + if decoded.len() != 32 + TEZOS_KEY_PREFIX_SIZE { + return Err(KeyPairError::InvalidSecretKey); + } + + tw_keypair::tw::PrivateKey::new((decoded[TEZOS_KEY_PREFIX_SIZE..]).to_vec()) +} + +pub fn encode_prefix(address: &str, forged: &mut Data) -> Result<(), EncodingError> { + let decoded = decode_check(address, Alphabet::Bitcoin)?; + forged.extend_from_slice(&decoded[TEZOS_ADDRESS_PREFIX_SIZE..]); + Ok(()) +} diff --git a/rust/chains/tw_tezos/src/compiler.rs b/rust/chains/tw_tezos/src/compiler.rs new file mode 100644 index 00000000000..ca0bc95988b --- /dev/null +++ b/rust/chains/tw_tezos/src/compiler.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::signing_output_error; +use tw_hash::blake2::blake2_b; +use tw_proto::Tezos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +use crate::operation_list::OperationList; +use crate::signer::TezosSigner; + +const WATERMARK_PREFIX: u8 = 0x03; + +pub struct TezosCompiler; + +impl TezosCompiler { + #[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> { + let mut operation_list = OperationList::new( + input + .operation_list + .clone() + .ok_or(SigningErrorType::Error_invalid_params)? + .branch + .into(), + ); + for operation in input + .operation_list + .clone() + .ok_or(SigningErrorType::Error_invalid_params)? + .operations + { + operation_list.add_operation(operation); + } + + let pre_image = TezosSigner::build_unsigned_tx(&operation_list) + .map_err(|_| SigningErrorType::Error_internal)?; + + let mut watermarked_data = Vec::new(); + watermarked_data.push(WATERMARK_PREFIX); + watermarked_data.extend_from_slice(&pre_image); + let pre_image_hash = + blake2_b(&watermarked_data, 32).map_err(|_| SigningErrorType::Error_internal)?; + + let output = CompilerProto::PreSigningOutput { + data_hash: pre_image_hash.into(), + data: pre_image.into(), + ..CompilerProto::PreSigningOutput::default() + }; + Ok(output) + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + _coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + _public_keys: Vec, + ) -> SigningResult> { + if signatures.len() != 1 { + return TWError::err(SigningErrorType::Error_signatures_count) + .context("Expected exactly one signature"); + } + + let signature = signatures + .into_iter() + .next() + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Expected exactly one signature")?; + + let mut operation_list = OperationList::new( + input + .operation_list + .clone() + .ok_or(SigningErrorType::Error_invalid_params)? + .branch + .into(), + ); + for operation in input + .operation_list + .clone() + .ok_or(SigningErrorType::Error_invalid_params)? + .operations + { + operation_list.add_operation(operation); + } + let tx = TezosSigner::build_signed_tx(&operation_list, signature) + .map_err(|_| SigningErrorType::Error_internal)?; + + Ok(Proto::SigningOutput { + encoded: tx.into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/chains/tw_tezos/src/entry.rs b/rust/chains/tw_tezos/src/entry.rs new file mode 100644 index 00000000000..da26000dbbc --- /dev/null +++ b/rust/chains/tw_tezos/src/entry.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::str::FromStr; +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::prelude::*; +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::modules::transaction_decoder::NoTransactionDecoder; +use tw_coin_entry::modules::transaction_util::NoTransactionUtil; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_proto::Tezos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +use crate::address::TezosAddress; +use crate::compiler::TezosCompiler; +use crate::signer::TezosSigner; + +pub struct TezosEntry; + +impl CoinEntry for TezosEntry { + type AddressPrefix = NoPrefix; + type Address = TezosAddress; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; + type TransactionUtil = NoTransactionUtil; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + TezosAddress::from_str(address) + } + + #[inline] + fn parse_address_unchecked(&self, address: &str) -> AddressResult { + TezosAddress::from_str(address) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + TezosAddress::with_public_key(&public_key) + } + + #[inline] + fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + TezosSigner::sign(_coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + TezosCompiler::preimage_hashes(_coin, input) + } + + #[inline] + fn compile( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + TezosCompiler::compile(_coin, input, signatures, public_keys) + } + + #[inline] + fn message_signer(&self) -> Option { + None + } + + #[inline] + fn transaction_util(&self) -> Option { + None + } +} diff --git a/rust/chains/tw_tezos/src/forging.rs b/rust/chains/tw_tezos/src/forging.rs new file mode 100644 index 00000000000..4ccd72141ea --- /dev/null +++ b/rust/chains/tw_tezos/src/forging.rs @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::num::ParseIntError; + +use tw_encoding::{ + base58::{self, Alphabet}, + hex::{self, FromHexError}, + EncodingError, +}; +use tw_keypair::tw::PublicKeyType; +use tw_memory::Data; +use tw_proto::Tezos::Proto::mod_Operation::OneOfoperation_data::*; +use tw_proto::Tezos::Proto::mod_Operation::OperationKind; + +use crate::{ + address::{TEZOS_ADDRESS_CHECKSUM_SIZE, TEZOS_ADDRESS_PREFIX_SIZE}, + binary_coding::{base58_to_hex, encode_check, encode_prefix}, + michelson::{ + fa12_parameter_to_michelson, fa2_parameter_to_michelson, MichelsonValue, PrimType, + }, +}; +use tw_proto::Tezos::Proto::mod_OperationParameters::OneOfparameters::{ + fa12_parameters, fa2_parameters, +}; + +const TEZOS_CONTRACT_PREFIX: &str = "KT1"; + +#[derive(Debug)] +pub enum ForgeError { + InvalidEntrypoint, + InvalidPrefix, + FromEncodingError, + InvalidAddressSize, + InvalidKeyType, + InvalidOperationKind, + FromParseIntError, + FromHexError, + InvalidBranch, + InvalidPublicKey, +} + +impl From for ForgeError { + fn from(_: EncodingError) -> Self { + ForgeError::FromEncodingError + } +} + +impl From for ForgeError { + fn from(_: ParseIntError) -> Self { + ForgeError::FromParseIntError + } +} + +impl From for ForgeError { + fn from(_: FromHexError) -> Self { + ForgeError::FromHexError + } +} + +// Forge the given boolean into a hex encoded string. +pub fn forge_bool(input: bool) -> Data { + vec![if input { 0xff } else { 0x00 }] +} + +pub fn forge_int32(value: i32, len: i32) -> Data { + let mut out = vec![0; len as usize]; + let mut val = value; + for i in (0..len).rev() { + out[i as usize] = (val & 0xFF) as u8; + val >>= 8; + } + out +} + +pub fn forge_string(value: &str, len: usize) -> Data { + let bytes = value.as_bytes(); + let mut result = forge_int32(bytes.len() as i32, len as i32); + result.extend_from_slice(bytes); + result +} + +pub fn forge_entrypoint(value: &str) -> Result { + let forged = match value { + "default" => vec![0x00], + "root" => vec![0x01], + "do" => vec![0x02], + "set_delegate" => vec![0x03], + "remove_delegate" => vec![0x04], + _ => { + let mut forged = vec![0xff]; + forged.extend(forge_string(value, 1)); + forged + }, + }; + Ok(forged) +} + +// Forge the given public key hash into a hex encoded string. +// Note: This function supports tz1, tz2 and tz3 addresses. +pub fn forge_public_key_hash(public_key_hash: &str) -> Result { + let mut forged = Data::new(); + // Adjust prefix based on tz1, tz2 or tz3. + match public_key_hash.chars().nth(2) { + Some('1') => forged.push(0x00), + Some('2') => forged.push(0x01), + Some('3') => forged.push(0x02), + _ => return Err(ForgeError::InvalidPrefix), + } + encode_prefix(public_key_hash, &mut forged)?; + Ok(forged) +} + +pub fn forge_address(address: &str) -> Result { + if address.len() < TEZOS_ADDRESS_PREFIX_SIZE { + return Err(ForgeError::InvalidAddressSize); + } + let prefix = &address[0..TEZOS_ADDRESS_PREFIX_SIZE]; + + if prefix == "tz1" || prefix == "tz2" || prefix == "tz3" { + let mut forged = vec![0x00]; + forged.extend(forge_public_key_hash(address)?); + Ok(forged) + } else if prefix == TEZOS_CONTRACT_PREFIX { + let mut forged = vec![0x01]; + encode_prefix(address, &mut forged)?; + forged.push(0x00); + Ok(forged) + } else { + Err(ForgeError::InvalidPrefix) + } +} + +// https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/codec.ts#L19 +pub fn forge_prefix( + prefix: [u8; TEZOS_ADDRESS_PREFIX_SIZE], + val: &str, +) -> Result { + let decoded = base58::decode(val, Alphabet::Bitcoin)?; + if !prefix.iter().zip(decoded.iter()).all(|(a, b)| a == b) { + return Err(ForgeError::InvalidPrefix); + } + + let mut forged = Data::new(); + forged.extend_from_slice(&decoded[TEZOS_ADDRESS_PREFIX_SIZE..]); + Ok(forged) +} + +// Forge the given public key into a hex encoded string. +pub fn forge_public_key(public_key: &[u8], key_type: PublicKeyType) -> Result { + let (prefix, tag) = match key_type { + PublicKeyType::Ed25519 => ([13, 15, 37, 217], "00"), + PublicKeyType::Secp256k1 => ([3, 254, 226, 86], "01"), + _ => return Err(ForgeError::InvalidKeyType), + }; + + let mut data = prefix.to_vec(); + data.extend_from_slice(public_key); + let pk = encode_check(&data, Alphabet::Bitcoin); + let decoded = format!( + "{}{}", + tag, + base58_to_hex(&pk, TEZOS_ADDRESS_CHECKSUM_SIZE)? + ); + Ok(hex::decode(&decoded)?) +} + +// Forge the given zarith hash into a hex encoded string. +pub fn forge_zarith(input: u64) -> Data { + let mut forged = Data::new(); + let mut val = input; + while val >= 0x80 { + forged.push(((val & 0xff) | 0x80) as u8); + val >>= 7; + } + forged.push(val as u8); + forged +} + +/// Forge the given operation. +pub fn forge_operation(operation: &tw_proto::Tezos::Proto::Operation) -> Result { + let mut forged = Data::new(); + let forged_source = forge_public_key_hash(&operation.source)?; // https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/schema/operation.ts#L40 + let forged_fee = forge_zarith(operation.fee as u64); + let forged_counter = forge_zarith(operation.counter as u64); + let forged_gas_limit = forge_zarith(operation.gas_limit as u64); + let forged_storage_limit = forge_zarith(operation.storage_limit as u64); + + match &operation.operation_data { + reveal_operation_data(operation_data) => { + let key_type = match operation_data.public_key.len() { + 32 => PublicKeyType::Ed25519, + 33 => PublicKeyType::Secp256k1, + _ => return Err(ForgeError::InvalidKeyType), + }; + let forged_public_key = forge_public_key(&operation_data.public_key, key_type)?; + + forged.push(OperationKind::REVEAL as u8); + forged.extend_from_slice(&forged_source); + forged.extend_from_slice(&forged_fee); + forged.extend_from_slice(&forged_counter); + forged.extend_from_slice(&forged_gas_limit); + forged.extend_from_slice(&forged_storage_limit); + forged.extend_from_slice(&forged_public_key); + }, + + delegation_operation_data(operation_data) => { + forged.push(OperationKind::DELEGATION as u8); + forged.extend_from_slice(&forged_source); + forged.extend_from_slice(&forged_fee); + forged.extend_from_slice(&forged_counter); + forged.extend_from_slice(&forged_gas_limit); + forged.extend_from_slice(&forged_storage_limit); + + if !operation_data.delegate.is_empty() { + let forged_pkh = forge_public_key_hash(&operation_data.delegate)?; + forged.extend_from_slice(&forge_bool(true)); + forged.extend_from_slice(&forged_pkh); + } else { + forged.extend_from_slice(&forge_bool(false)); + } + }, + + transaction_operation_data(operation_data) => { + let tx_data = operation_data; + let forged_amount = forge_zarith(tx_data.amount as u64); + let forged_destination = forge_address(&tx_data.destination)?; + + forged.push(OperationKind::TRANSACTION as u8); + forged.extend_from_slice(&forged_source); + forged.extend_from_slice(&forged_fee); + forged.extend_from_slice(&forged_counter); + forged.extend_from_slice(&forged_gas_limit); + forged.extend_from_slice(&forged_storage_limit); + forged.extend_from_slice(&forged_amount); + forged.extend_from_slice(&forged_destination); + + if tx_data.parameters.is_none() && tx_data.encoded_parameter.is_empty() { + forged.extend_from_slice(&forge_bool(false)); + } else if let Some(parameters) = &tx_data.parameters { + forged.extend_from_slice(&forge_bool(true)); + match ¶meters.parameters { + fa12_parameters(parameters) => { + forged.extend_from_slice(&forge_entrypoint(¶meters.entrypoint)?); + forged.extend_from_slice(&forge_array(&forge_michelson( + &fa12_parameter_to_michelson(parameters.clone()), + )?)); + }, + fa2_parameters(parameters) => { + forged.extend_from_slice(&forge_entrypoint(¶meters.entrypoint)?); + forged.extend_from_slice(&forge_array(&forge_michelson( + &fa2_parameter_to_michelson(parameters.clone()), + )?)); + }, + _ => {}, + } + } else { + forged.extend_from_slice(&tx_data.encoded_parameter); + } + }, + + _ => return Err(ForgeError::InvalidOperationKind), + } + + Ok(forged) +} + +pub fn forge_prim(value: &MichelsonValue) -> Result { + let mut forged = Data::new(); + if let MichelsonValue::Prim { prim, args, anots } = value { + if prim == "Pair" { + // https://tezos.gitlab.io/developer/encodings.html?highlight=pair#pairs + forged.reserve(2); + const NB_ARGS: u8 = 2; + // https://github.com/ecadlabs/taquito/blob/fd84d627171d24ce7ba81dd7b18763a95f16a99c/packages/taquito-local-forging/src/michelson/codec.ts#L195 + // https://github.com/baking-bad/netezos/blob/0bfd6db4e85ab1c99fb55503e476fe67cebd2dc5/Netezos/Forging/Local/LocalForge.Forgers.cs#L199 + let preamble = std::cmp::min(2 * NB_ARGS + anots.len() as u8 + 0x03, 9); + forged.push(preamble); + forged.push(PrimType::Pair as u8); + let sub_forged = Data::new(); + for arg in args { + forged.extend_from_slice(&forge_michelson(arg)?); + } + forged.extend_from_slice(&sub_forged); + } + } + Ok(forged) +} + +pub fn forge_michelson(value: &MichelsonValue) -> Result { + let forged = match value { + MichelsonValue::Prim { .. } => forge_prim(value)?, + MichelsonValue::String(s) => { + let mut forged = Data::from(vec![1]); + forged.extend_from_slice(&forge_string(s, 4)); + forged + }, + MichelsonValue::Int(i) => { + let mut forged = Data::from(vec![0]); + forged.extend_from_slice(&forge_michel_int(&i.parse()?)); + forged + }, + MichelsonValue::Array(arr) => { + let mut forged = Data::from(vec![2]); + let mut sub_forged = Data::new(); + for item in arr { + sub_forged.extend_from_slice(&forge_michelson(item)?); + } + forged.extend_from_slice(&forge_array(&sub_forged)); + forged + }, + }; + Ok(forged) +} + +pub fn forge_array(data: &[u8]) -> Data { + let mut forged = forge_int32(data.len() as i32, 4); + forged.extend_from_slice(data); + forged +} + +pub fn forge_michel_int(value: &i128) -> Data { + let mut forged = Data::new(); + let abs = value.unsigned_abs(); + forged.push(if value.is_negative() { + ((abs & 0x3f) as u8).wrapping_sub(0x40) + } else { + (abs & 0x3f) as u8 + }); + let mut abs = abs >> 6; + while abs > 0 { + let last = forged.len() - 1; + forged[last] |= 0x80; + forged.push((abs & 0x7F) as u8); + abs >>= 7; + } + forged +} diff --git a/rust/chains/tw_tezos/src/lib.rs b/rust/chains/tw_tezos/src/lib.rs new file mode 100644 index 00000000000..92c0f9bf16f --- /dev/null +++ b/rust/chains/tw_tezos/src/lib.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod address; +pub mod binary_coding; +pub mod compiler; +pub mod entry; +pub mod forging; +pub mod michelson; +pub mod operation_list; +pub mod signer; diff --git a/rust/chains/tw_tezos/src/michelson.rs b/rust/chains/tw_tezos/src/michelson.rs new file mode 100644 index 00000000000..8a0c43be46b --- /dev/null +++ b/rust/chains/tw_tezos/src/michelson.rs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_proto::Tezos::Proto::{FA12Parameters, FA2Parameters}; + +pub enum PrimType { + Pair = 7, +} + +#[derive(Debug)] +pub enum MichelsonValue { + String(String), + Int(String), + Prim { + prim: String, + args: Vec, + anots: Vec, + }, + Array(Vec), +} + +pub fn fa12_parameter_to_michelson(data: FA12Parameters) -> MichelsonValue { + let address = MichelsonValue::String(data.from.to_string()); + let to = MichelsonValue::String(data.to.to_string()); + let amount = MichelsonValue::Int(data.value.to_string()); + + let prim_transfer_infos = MichelsonValue::Prim { + prim: "Pair".to_string(), + args: vec![to, amount], + anots: vec![], + }; + + MichelsonValue::Prim { + prim: "Pair".to_string(), + args: vec![address, prim_transfer_infos], + anots: vec![], + } +} + +pub fn fa2_parameter_to_michelson(data: FA2Parameters) -> MichelsonValue { + let tx_obj = data + .txs_object + .first() + .expect("FA2Parameters must have at least one txs_object"); + let from = MichelsonValue::String(tx_obj.from.to_string()); + let tx_transfer_infos = &tx_obj.txs[0]; + + let token_id = MichelsonValue::Int(tx_transfer_infos.token_id.to_string()); + let amount = MichelsonValue::Int(tx_transfer_infos.amount.to_string()); + + let prim_transfer_infos = MichelsonValue::Prim { + prim: "Pair".to_string(), + args: vec![token_id, amount], + anots: vec![], + }; + + let to = MichelsonValue::String(tx_transfer_infos.to.to_string()); + let txs = MichelsonValue::Array(vec![MichelsonValue::Prim { + prim: "Pair".to_string(), + args: vec![to, prim_transfer_infos], + anots: vec![], + }]); + + let prim_txs = MichelsonValue::Prim { + prim: "Pair".to_string(), + args: vec![from, txs], + anots: vec![], + }; + + MichelsonValue::Array(vec![prim_txs]) +} diff --git a/rust/chains/tw_tezos/src/operation_list.rs b/rust/chains/tw_tezos/src/operation_list.rs new file mode 100644 index 00000000000..ac7ccde53cd --- /dev/null +++ b/rust/chains/tw_tezos/src/operation_list.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_keypair::tw::{PrivateKey, PublicKeyType}; +use tw_memory::Data; +use tw_proto::Tezos::Proto::mod_Operation::OneOfoperation_data; +use tw_proto::Tezos::Proto::Operation; + +use crate::binary_coding::decode_check; +use crate::forging::{forge_operation, ForgeError}; + +pub struct OperationList<'a> { + branch: String, + operation_list: Vec>, +} + +impl<'a> OperationList<'a> { + pub fn new(branch: String) -> Self { + Self { + branch, + operation_list: Vec::new(), + } + } + + pub fn add_operation(&mut self, operation: Operation<'a>) { + self.operation_list.push(operation); + } + + // Forge the given branch to a hex encoded string. + pub fn forge_branch(&self) -> Result { + let prefix = [1, 52]; + let decoded = decode_check(&self.branch, tw_encoding::base58::Alphabet::Bitcoin)?; + + if decoded.len() != 34 || !prefix.iter().zip(decoded.iter()).all(|(a, b)| a == b) { + return Err(ForgeError::InvalidBranch); + } + + let mut forged = Data::new(); + forged.extend_from_slice(&decoded[prefix.len()..]); + Ok(forged) + } + + pub fn forge(&self, private_key: Option<&PrivateKey>) -> Result { + let mut forged = self.forge_branch()?; + + for mut operation in self.operation_list.iter().cloned() { + // If it's REVEAL operation, inject the public key if not specified + if let OneOfoperation_data::reveal_operation_data(reveal_data) = + &mut operation.operation_data + { + if reveal_data.public_key.is_empty() { + if let Some(key) = private_key { + reveal_data.public_key = key + .get_public_key_by_type(PublicKeyType::Ed25519) + .map_err(|_| ForgeError::InvalidPublicKey)? + .to_bytes() + .to_vec() + .into(); + } + } + } + + forged.extend_from_slice(&forge_operation(&operation)?); + } + + Ok(forged) + } +} diff --git a/rust/chains/tw_tezos/src/signer.rs b/rust/chains/tw_tezos/src/signer.rs new file mode 100644 index 00000000000..c16ef97502e --- /dev/null +++ b/rust/chains/tw_tezos/src/signer.rs @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::signing_output_error; +use tw_hash::blake2::blake2_b; +use tw_keypair::tw::{Curve, PrivateKey}; +use tw_proto::Tezos::Proto; + +use crate::{forging::ForgeError, operation_list::OperationList}; + +pub struct TezosSigner; + +impl TezosSigner { + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + _coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let key = PrivateKey::new(input.private_key.to_vec())?; + let encoded = if input.encoded_operations.is_empty() { + let mut operation_list = OperationList::new( + input + .operation_list + .clone() + .ok_or(SigningErrorType::Error_invalid_params)? + .branch + .into(), + ); + for operation in input + .operation_list + .clone() + .ok_or(SigningErrorType::Error_invalid_params)? + .operations + { + operation_list.add_operation(operation); + } + Self::sign_operation_list(&key, &operation_list) + } else { + Self::sign_data(&key, &input.encoded_operations) + }?; + + let output = Proto::SigningOutput { + encoded: encoded.into(), + ..Proto::SigningOutput::default() + }; + Ok(output) + } + + pub fn sign_operation_list( + private_key: &PrivateKey, + operation_list: &OperationList, + ) -> SigningResult> { + let forged = operation_list + .forge(Some(private_key)) + .map_err(|_| SigningErrorType::Error_internal)?; + Self::sign_data(private_key, &forged) + } + + pub fn sign_data(private_key: &PrivateKey, data: &[u8]) -> SigningResult> { + let mut watermarked_data = Vec::new(); + watermarked_data.push(0x03); + watermarked_data.extend_from_slice(data); + + let hash = blake2_b(&watermarked_data, 32).map_err(|_| SigningErrorType::Error_internal)?; + let signature = private_key.sign(&hash, Curve::Ed25519)?; + + let mut signed_data = Vec::new(); + signed_data.extend_from_slice(data); + signed_data.extend_from_slice(&signature); + Ok(signed_data) + } + + pub fn build_unsigned_tx(operation_list: &OperationList) -> Result, ForgeError> { + operation_list.forge(None) + } + + pub fn build_signed_tx( + operation_list: &OperationList, + signature: Vec, + ) -> Result, ForgeError> { + let mut signed_data = Vec::new(); + + let tx_data = operation_list.forge(None)?; + + signed_data.extend_from_slice(&tx_data); + signed_data.extend_from_slice(&signature); + + Ok(signed_data) + } +} diff --git a/rust/chains/tw_tezos/tests/address.rs b/rust/chains/tw_tezos/tests/address.rs new file mode 100644 index 00000000000..e90e8dcd55e --- /dev/null +++ b/rust/chains/tw_tezos/tests/address.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::str::FromStr; +use tw_encoding::hex; +use tw_keypair::ed25519; +use tw_tezos::address::TezosAddress; + +#[test] +fn test_account_zero() { + let addr = TezosAddress::from_str("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don").unwrap(); + let forged = addr.forge().unwrap(); + assert_eq!( + hex::encode(forged, false), + "0000cfa4aae60f5d9389752d41e320da224d43287fe2" + ); +} + +#[test] +fn test_forge_tz2() { + let addr = TezosAddress::from_str("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo").unwrap(); + let forged = addr.forge().unwrap(); + assert_eq!( + hex::encode(forged, false), + "0001be99dd914e38388ec80432818b517759e3524f16" + ); +} + +#[test] +fn test_forge_tz3() { + let addr = TezosAddress::from_str("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9").unwrap(); + let forged = addr.forge().unwrap(); + assert_eq!( + hex::encode(forged, false), + "0002358cbffa97149631cfb999fa47f0035fb1ea8636" + ); +} + +#[test] +fn test_forge_kt1() { + let addr = TezosAddress::from_str("KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o").unwrap(); + let forged = addr.forge().unwrap(); + assert_eq!( + hex::encode(forged, false), + "01fe810959c3d6127a41cbd471e7cb4e91a61b780b00" + ); +} + +#[test] +fn test_invalid_addresses() { + let invalid_addresses = [ + "NmH7tmeJUmHcncBDvpr7aJNEBk7rp5zYsB1qt", // Invalid prefix, valid checksum + "tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3AAAA", // Valid prefix, invalid checksum + "1tzeZwq8b5cvE2bPKokatLkVMzkxz24zAAAAA", // Invalid prefix, invalid checksum + ]; + + for address in invalid_addresses { + assert!(!TezosAddress::from_str(address).is_ok()); + } +} + +#[test] +fn test_valid_addresses() { + let valid_addresses = [ + "tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt", + "tz2PdGc7U5tiyqPgTSgqCDct94qd6ovQwP6u", + "tz3VEZ4k6a4Wx42iyev6i2aVAptTRLEAivNN", + "KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o", + ]; + + for address in valid_addresses { + assert!(TezosAddress::from_str(address).is_ok()); + } +} + +#[test] +fn test_string() { + let address_string = "tz1d1qQL3mYVuiH4JPFvuikEpFwaDm85oabM"; + let address = TezosAddress::from_str(address_string).unwrap(); + assert_eq!(address.to_string(), address_string); +} + +#[test] +fn test_public_key_init() { + let bytes = + hex::decode("01fe157cc8011727936c592f856c9071d39cf4acdadfa6d76435e4619c9dc56f63").unwrap(); + let public_key = ed25519::sha512::PublicKey::try_from(&bytes[1..]).unwrap(); + let address = TezosAddress::with_ed25519_public_key(&public_key).unwrap(); + let expected = "tz1cG2jx3W4bZFeVGBjsTxUAG8tdpTXtE8PT"; + assert_eq!(address.to_string(), expected); +} diff --git a/rust/chains/tw_tezos/tests/compiler.rs b/rust/chains/tw_tezos/tests/compiler.rs new file mode 100644 index 00000000000..2944eee52f1 --- /dev/null +++ b/rust/chains/tw_tezos/tests/compiler.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_encoding::hex; +use tw_keypair::tw::PrivateKey; +use tw_proto::Common::Proto as CommonProto; +use tw_proto::Tezos::Proto::{self, mod_Operation::OperationKind}; +use tw_tezos::compiler::TezosCompiler; +use tw_tezos::signer::TezosSigner; + +#[test] +fn test_compile_with_signatures() { + let private_key = PrivateKey::new( + hex::decode("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f").unwrap(), + ) + .unwrap(); + let reveal_key = + hex::decode("311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95").unwrap(); + + let mut input = Proto::SigningInput::default(); + let mut operations = Proto::OperationList::default(); + operations.branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp".into(); + + let mut reveal = Proto::Operation::default(); + reveal.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(); + reveal.fee = 1272; + reveal.counter = 30738; + reveal.gas_limit = 10100; + reveal.storage_limit = 257; + reveal.kind = OperationKind::REVEAL; + reveal.operation_data = Proto::mod_Operation::OneOfoperation_data::reveal_operation_data( + Proto::RevealOperationData { + public_key: reveal_key.into(), + }, + ); + operations.operations.push(reveal); + + let mut transaction = Proto::Operation::default(); + transaction.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(); + transaction.fee = 1272; + transaction.counter = 30739; + transaction.gas_limit = 10100; + transaction.storage_limit = 257; + transaction.kind = OperationKind::TRANSACTION; + transaction.operation_data = + Proto::mod_Operation::OneOfoperation_data::transaction_operation_data( + Proto::TransactionOperationData { + amount: 1, + destination: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + encoded_parameter: vec![].into(), + parameters: None, + }, + ); + operations.operations.push(transaction); + + input.operation_list = Some(operations); + + // Step 2: Obtain preimage hash + let pre_image_hash_data = + TezosCompiler::preimage_hashes(&TestCoinContext::default(), input.clone()); + assert_eq!(pre_image_hash_data.error, CommonProto::SigningError::OK); + assert_eq!( + hex::encode(&pre_image_hash_data.data_hash, false), + "12e4f8b17ad3b316a5a56960db76c7d6505dbf2fff66106be75c8d6753daac0e" + ); + + let signature = hex::decode("0217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05").unwrap(); + + // Step 3: Compile transaction info + let expected_tx = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff956c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b95721000217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05"; + + let output = TezosCompiler::compile( + &TestCoinContext::default(), + input.clone(), + vec![signature.clone()], + vec![], + ); + assert_eq!(hex::encode(&output.encoded, false), expected_tx); + + // Double check with direct signing + input.private_key = private_key.bytes().into(); + let signed_output = TezosSigner::sign(&TestCoinContext::default(), input.clone()); + assert_eq!(hex::encode(&signed_output.encoded, false), expected_tx); + + // Negative: inconsistent signatures & publicKeys + let output = TezosCompiler::compile( + &TestCoinContext::default(), + input, + vec![signature.clone(), signature], + vec![], + ); + assert_eq!(output.encoded.len(), 0); + assert_eq!( + output.error, + CommonProto::SigningError::Error_signatures_count + ); +} diff --git a/rust/chains/tw_tezos/tests/forging.rs b/rust/chains/tw_tezos/tests/forging.rs new file mode 100644 index 00000000000..5888461685e --- /dev/null +++ b/rust/chains/tw_tezos/tests/forging.rs @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_encoding::hex; +use tw_keypair::{ecdsa, ed25519, tw::PublicKeyType}; +use tw_misc::traits::ToBytesVec; +use tw_proto::Tezos::Proto::mod_Operation::OneOfoperation_data::*; +use tw_proto::Tezos::Proto::mod_Operation::OperationKind; +use tw_proto::Tezos::Proto::mod_OperationParameters::OneOfparameters::{ + fa12_parameters, fa2_parameters, +}; +use tw_proto::Tezos::Proto::{ + DelegationOperationData, FA12Parameters, FA2Parameters, Operation, OperationParameters, + RevealOperationData, TransactionOperationData, TxObject, Txs, +}; +use tw_tezos::binary_coding::parse_public_key; +use tw_tezos::forging::*; +use tw_tezos::michelson::*; + +#[test] +fn test_forge_bool_true() { + let expected = "ff"; + let output = forge_bool(true); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_bool_false() { + let expected = "00"; + let output = forge_bool(false); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_zarith_zero() { + let expected = "00"; + let output = forge_zarith(0); + assert_eq!(hex::encode(&output, false), expected); +} + +#[test] +fn test_forge_zarith_ten() { + let expected = "0a"; + let output = forge_zarith(10); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_zarith_twenty() { + let expected = "14"; + let output = forge_zarith(20); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_zarith_one_hundred_fifty() { + let expected = "9601"; + let output = forge_zarith(150); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_zarith_large() { + let expected = "bbd08001"; + let output = forge_zarith(2107451); + assert_eq!(hex::encode(&output, false), expected); +} + +#[test] +fn test_forge_tz1() { + let expected = "00cfa4aae60f5d9389752d41e320da224d43287fe2"; + let output = forge_public_key_hash("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don").unwrap(); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_tz2() { + let expected = "01be99dd914e38388ec80432818b517759e3524f16"; + let output = forge_public_key_hash("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo").unwrap(); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_tz3() { + let expected = "02358cbffa97149631cfb999fa47f0035fb1ea8636"; + let output = forge_public_key_hash("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9").unwrap(); + assert_eq!(hex::encode(output, false), expected); +} + +#[test] +fn test_forge_ed25519_public_key() { + let expected = "00311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"; + let private_key = ed25519::sha512::PrivateKey::try_from( + "c6377a4cc490dc913fc3f0d9cf67d293a32df4547c46cb7e9e33c3b7b97c64d8", + ) + .unwrap(); + let public_key = private_key.public(); + println!( + "public_key: {:?}", + hex::encode(public_key.as_slice(), false) + ); + let output = forge_public_key(public_key.as_slice(), PublicKeyType::Ed25519).unwrap(); + assert_eq!(hex::encode(&output, false), expected); +} + +#[test] +fn test_forge_int32() { + let expected = "01"; + assert_eq!(hex::encode(forge_int32(1, 1), false), expected); +} + +#[test] +fn test_forge_string() { + let expected = "087472616e73666572"; + assert_eq!(hex::encode(forge_string("transfer", 1), false), expected); +} + +#[test] +fn test_forge_entrypoint() { + let expected = "ff087472616e73666572"; + assert_eq!( + hex::encode(forge_entrypoint("transfer").unwrap(), false), + expected + ); +} + +#[test] +fn test_forge_michelson_fa12() { + let data = FA12Parameters { + entrypoint: "transfer".into(), + from: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + to: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + value: "123".into(), + }; + let v = fa12_parameter_to_michelson(data); + assert_eq!(hex::encode(forge_michelson(&v).unwrap(), false), "07070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb01"); +} + +#[test] +fn test_forge_secp256k1_public_key() { + let expected = "0102b4ac9056d20c52ac11b0d7e83715dd3eac851cfc9cb64b8546d9ea0d4bb3bdfe"; + let private_key = ecdsa::secp256k1::PrivateKey::try_from( + "3a8e0a528f62f4ca2c77744c8a571def2845079b50105a9f7ef6b1b823def67a", + ) + .unwrap(); + let public_key = private_key.public(); + let output = forge_public_key(&public_key.to_vec(), PublicKeyType::Secp256k1).unwrap(); + assert_eq!(hex::encode(&output, false), expected); +} + +#[test] +fn test_forge_transaction() { + let transaction_data = TransactionOperationData { + amount: 1, + destination: "tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt".into(), + encoded_parameter: vec![].into(), + parameters: Option::::None, + }; + + let operation = Operation { + source: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + fee: 1272, + counter: 30738, + gas_limit: 10100, + storage_limit: 257, + kind: OperationKind::TRANSACTION, + operation_data: transaction_operation_data(transaction_data).into(), + }; + + let expected = "6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; + let serialized = forge_operation(&operation).unwrap(); + assert_eq!(hex::encode(&serialized, false), expected); +} + +#[test] +fn test_forge_transaction_fa12() { + let parameters = OperationParameters { + parameters: fa12_parameters(FA12Parameters { + from: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + to: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + entrypoint: "transfer".into(), + value: "123".into(), + }) + .into(), + }; + + let transaction_data = TransactionOperationData { + amount: 0, + destination: "KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5".into(), + encoded_parameter: vec![].into(), + parameters: Some(parameters), + }; + + let operation = Operation { + source: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + fee: 100000, + counter: 2993172, + gas_limit: 100000, + storage_limit: 0, + kind: OperationKind::TRANSACTION, + operation_data: transaction_operation_data(transaction_data).into(), + }; + + let expected = "6c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb01"; + let serialized = forge_operation(&operation).unwrap(); + assert_eq!(hex::encode(&serialized, false), expected); +} + +#[test] +fn test_forge_transaction_fa2() { + let mut parameters = OperationParameters { + parameters: fa2_parameters(FA2Parameters { + entrypoint: "transfer".into(), + txs_object: vec![], + }) + .into(), + }; + + let mut transaction_data = TransactionOperationData { + amount: 0, + destination: "KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj".into(), + encoded_parameter: vec![].into(), + parameters: Some(parameters.clone()), + }; + + let mut tx_object = TxObject { + from: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + txs: vec![], + }; + + let tx = Txs { + amount: "10".into(), + token_id: "0".into(), + to: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + }; + tx_object.txs.push(tx); + + parameters.parameters = fa2_parameters(FA2Parameters { + entrypoint: "transfer".into(), + txs_object: vec![tx_object], + }) + .into(); + transaction_data.parameters = Some(parameters); + + let operation = Operation { + source: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + fee: 100000, + counter: 2993173, + gas_limit: 100000, + storage_limit: 0, + kind: OperationKind::TRANSACTION, + operation_data: transaction_operation_data(transaction_data).into(), + }; + + let expected = "6c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a"; + let serialized = forge_operation(&operation).unwrap(); + assert_eq!(hex::encode(&serialized, false), expected); +} + +#[test] +fn test_forge_reveal() { + let public_key = + parse_public_key("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A").unwrap(); + + let reveal_data = RevealOperationData { + public_key: public_key.to_bytes().into(), + }; + + let operation = Operation { + source: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + fee: 1272, + counter: 30738, + gas_limit: 10100, + storage_limit: 257, + kind: OperationKind::REVEAL, + operation_data: reveal_operation_data(reveal_data).into(), + }; + + let expected = "6b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; + let serialized = forge_operation(&operation).unwrap(); + assert_eq!(hex::encode(&serialized, false), expected); +} + +#[test] +fn test_forge_delegate() { + let delegate_data = DelegationOperationData { + delegate: "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".into(), + }; + + let operation = Operation { + source: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + fee: 1272, + counter: 30738, + gas_limit: 10100, + storage_limit: 257, + kind: OperationKind::DELEGATION, + operation_data: delegation_operation_data(delegate_data).into(), + }; + + let expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e8102ff003e47f837f0467b4acde406ed5842f35e2414b1a8"; + let serialized = forge_operation(&operation).unwrap(); + assert_eq!(hex::encode(&serialized, false), expected); +} + +#[test] +fn test_forge_undelegate() { + let delegate_data = DelegationOperationData { + delegate: "".into(), + }; + + let operation = Operation { + source: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + fee: 1272, + counter: 30738, + gas_limit: 10100, + storage_limit: 257, + kind: OperationKind::DELEGATION, + operation_data: delegation_operation_data(delegate_data).into(), + }; + + let expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200"; + let serialized = forge_operation(&operation).unwrap(); + assert_eq!(hex::encode(&serialized, false), expected); +} diff --git a/rust/chains/tw_tezos/tests/operation_list.rs b/rust/chains/tw_tezos/tests/operation_list.rs new file mode 100644 index 00000000000..dd22e85cc1d --- /dev/null +++ b/rust/chains/tw_tezos/tests/operation_list.rs @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_tezos::{ + binary_coding::{parse_private_key, parse_public_key}, + operation_list::OperationList, +}; + +use tw_encoding::hex::DecodeHex; +use tw_proto::Tezos::Proto::{ + mod_Operation::{OneOfoperation_data, OperationKind}, + DelegationOperationData, Operation, RevealOperationData, TransactionOperationData, +}; + +#[test] +fn test_forge_branch() { + let input = + OperationList::new("BMNY6Jkas7BzKb7wDLCFoQ4YxfYoieU7Xmo1ED3Y9Lo3ZvVGdgW".to_string()); + let expected = "da8eb4f57f98a647588b47d29483d1edfdbec1428c11609cee0da6e0f27cfc38" + .decode_hex() + .unwrap(); + assert_eq!(input.forge_branch().unwrap(), expected); +} + +#[test] +fn test_forge_operation_list_transaction_only() { + let branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; + let mut op_list = OperationList::new(branch.to_string()); + let key = parse_private_key("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc").unwrap(); + + let mut transaction_operation = Operation::default(); + transaction_operation.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".to_string().into(); + transaction_operation.fee = 1272; + transaction_operation.counter = 30738; + transaction_operation.gas_limit = 10100; + transaction_operation.storage_limit = 257; + transaction_operation.kind = OperationKind::TRANSACTION; + transaction_operation.operation_data = + OneOfoperation_data::transaction_operation_data(TransactionOperationData { + amount: 1, + destination: "tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt".to_string().into(), + ..Default::default() + }); + + op_list.add_operation(transaction_operation); + + let expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100".decode_hex().unwrap(); + let forged = op_list.forge(Some(&key)).unwrap(); + assert_eq!(forged, expected); +} + +#[test] +fn test_forge_operation_list_reveal_only() { + let branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; + let mut op_list = OperationList::new(branch.to_string()); + let key = parse_private_key("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc").unwrap(); + let public_key = + parse_public_key("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A").unwrap(); + + let mut reveal_operation = Operation::default(); + reveal_operation.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".to_string().into(); + reveal_operation.fee = 1272; + reveal_operation.counter = 30738; + reveal_operation.gas_limit = 10100; + reveal_operation.storage_limit = 257; + reveal_operation.kind = OperationKind::REVEAL; + reveal_operation.operation_data = + OneOfoperation_data::reveal_operation_data(RevealOperationData { + public_key: public_key.to_bytes().to_vec().into(), + ..Default::default() + }); + + op_list.add_operation(reveal_operation); + + let expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e".decode_hex().unwrap(); + let forged = op_list.forge(Some(&key)).unwrap(); + assert_eq!(forged, expected); +} + +#[test] +fn test_forge_operation_list_delegation_clear_delegate() { + let branch = "BLGJfQDFEYZBRLj5GSHskj8NPaRYhk7Kx5WAfdcDucD3q98WdeW"; + let mut op_list = OperationList::new(branch.to_string()); + let key = parse_private_key("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc").unwrap(); + + let mut delegation_operation = Operation::default(); + delegation_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + delegation_operation.fee = 1257; + delegation_operation.counter = 67; + delegation_operation.gas_limit = 10000; + delegation_operation.storage_limit = 0; + delegation_operation.kind = OperationKind::DELEGATION; + delegation_operation.operation_data = + OneOfoperation_data::delegation_operation_data(DelegationOperationData { + delegate: "tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M".to_string().into(), + ..Default::default() + }); + + op_list.add_operation(delegation_operation); + + let expected = "48b63d801fa824013a195f7885ba522503c59e0580f7663e15c52f03ccc935e66e003e47f837f0467b4acde406ed5842f35e2414b1a8e90943904e00ff00e42504da69a7c8d5baeaaeebe157a02db6b22ed8".decode_hex().unwrap(); + assert_eq!(op_list.forge(Some(&key)).unwrap(), expected); +} + +#[test] +fn test_forge_operation_list_delegation_add_delegate() { + let branch = "BLa4GrVQTxUgQWbHv6cF7RXWSGzHGPbgecpQ795R3cLzw4cGfpD"; + let mut op_list = OperationList::new(branch.to_string()); + let key = parse_private_key("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc").unwrap(); + + let mut delegation_operation = Operation::default(); + delegation_operation.source = "KT1D5jmrBD7bDa3jCpgzo32FMYmRDdK2ihka".to_string().into(); + delegation_operation.fee = 1257; + delegation_operation.counter = 68; + delegation_operation.gas_limit = 10000; + delegation_operation.storage_limit = 0; + delegation_operation.kind = OperationKind::DELEGATION; + delegation_operation.operation_data = + OneOfoperation_data::delegation_operation_data(DelegationOperationData { + delegate: "tz1dYUCcrorfCoaQCtZaxi1ynGrP3prTZcxS".to_string().into(), + ..Default::default() + }); + + op_list.add_operation(delegation_operation); + + let expected = "7105102c032807994dd9b5edf219261896a559876ca16cbf9d31dbe3612b89f26e00315b1206ec00b1b1e64cc3b8b93059f58fa2fc39e90944904e00ff00c4650fd609f88c67356e5fe01e37cd3ff654b18c".decode_hex().unwrap(); + let forged = op_list.forge(Some(&key)).unwrap(); + assert_eq!(forged, expected); +} + +#[test] +fn test_forge_operation_list_transaction_and_reveal() { + let branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; + let mut op_list = OperationList::new(branch.to_string()); + let key = parse_private_key("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc").unwrap(); + let public_key = + parse_public_key("edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp").unwrap(); + + let mut reveal_operation = Operation::default(); + reveal_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + reveal_operation.fee = 1272; + reveal_operation.counter = 30738; + reveal_operation.gas_limit = 10100; + reveal_operation.storage_limit = 257; + reveal_operation.kind = OperationKind::REVEAL; + reveal_operation.operation_data = + OneOfoperation_data::reveal_operation_data(RevealOperationData { + public_key: public_key.to_bytes().to_vec().into(), + ..Default::default() + }); + + let mut transaction_operation = Operation::default(); + transaction_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + transaction_operation.fee = 1272; + transaction_operation.counter = 30739; + transaction_operation.gas_limit = 10100; + transaction_operation.storage_limit = 257; + transaction_operation.kind = OperationKind::TRANSACTION; + transaction_operation.operation_data = + OneOfoperation_data::transaction_operation_data(TransactionOperationData { + amount: 1, + destination: "tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M".to_string().into(), + ..Default::default() + }); + + op_list.add_operation(reveal_operation); + op_list.add_operation(transaction_operation); + + let expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b003e47f837f0467b4acde406ed5842f35e2414b1a8f80992f001f44e810200603247bbf52501498293686da89ad8b2aca85f83b90903d4521dd2aba66054eb6c003e47f837f0467b4acde406ed5842f35e2414b1a8f80993f001f44e8102010000e42504da69a7c8d5baeaaeebe157a02db6b22ed800".decode_hex().unwrap(); + let forged = op_list.forge(Some(&key)).unwrap(); + assert_eq!(forged, expected); +} + +#[test] +fn test_forge_operation_list_reveal_without_public_key() { + let branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; + let mut op_list = OperationList::new(branch.to_string()); + let key = parse_private_key("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc").unwrap(); + + let mut reveal_operation = Operation::default(); + reveal_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + reveal_operation.fee = 1272; + reveal_operation.counter = 30738; + reveal_operation.gas_limit = 10100; + reveal_operation.storage_limit = 257; + reveal_operation.kind = OperationKind::REVEAL; + reveal_operation.operation_data = + OneOfoperation_data::reveal_operation_data(RevealOperationData::default()); + + op_list.add_operation(reveal_operation); + + let expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b003e47f837f0467b4acde406ed5842f35e2414b1a8f80992f001f44e810200603247bbf52501498293686da89ad8b2aca85f83b90903d4521dd2aba66054eb".decode_hex().unwrap(); + let forged = op_list.forge(Some(&key)).unwrap(); + assert_eq!(forged, expected); +} diff --git a/rust/chains/tw_tezos/tests/signer.rs b/rust/chains/tw_tezos/tests/signer.rs new file mode 100644 index 00000000000..cac6888d52c --- /dev/null +++ b/rust/chains/tw_tezos/tests/signer.rs @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_tezos::{ + binary_coding::{decode_check, parse_public_key}, + signer::TezosSigner, +}; + +use tw_encoding::hex; +use tw_keypair::tw::PrivateKey; +use tw_proto::Tezos::Proto::{self, mod_Operation::OperationKind}; +use tw_tezos::operation_list::OperationList; + +#[test] +fn test_sign_string() { + let bytes_to_sign = hex::decode("ffaa").unwrap(); + let expected_signature = hex::decode("eaab7f4066217b072b79609a9f76cdfadd93f8dde41763887e131c02324f18c8e41b1009e334baf87f9d2e917bf4c0e73165622e5522409a0c5817234a48cc02").unwrap(); + let mut expected = Vec::new(); + expected.extend_from_slice(&bytes_to_sign); + expected.extend_from_slice(&expected_signature); + + let key = PrivateKey::new( + hex::decode("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f").unwrap(), + ) + .unwrap(); + let signed_bytes = TezosSigner::sign_data(&key, &bytes_to_sign).unwrap(); + + assert_eq!(signed_bytes, expected); +} + +#[test] +fn test_sign_operation_list() { + let branch = "BLDnkhhVgwdBAtmDNQc5HtEMsrxq8L3t7NQbjUbbdTdw5Ug1Mpe"; + let mut op_list = OperationList::new(branch.to_string()); + + let mut transaction_operation = Proto::Operation::default(); + transaction_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + transaction_operation.fee = 1283; + transaction_operation.counter = 1878; + transaction_operation.gas_limit = 10307; + transaction_operation.storage_limit = 0; + transaction_operation.kind = OperationKind::TRANSACTION; + transaction_operation.operation_data = + Proto::mod_Operation::OneOfoperation_data::transaction_operation_data( + Proto::TransactionOperationData { + amount: 11100000, + destination: "tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M".to_string().into(), + encoded_parameter: vec![].into(), + parameters: None, + }, + ); + + op_list.add_operation(transaction_operation); + + let mut reveal_operation = Proto::Operation::default(); + reveal_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + reveal_operation.fee = 1268; + reveal_operation.counter = 1876; + reveal_operation.gas_limit = 10100; + reveal_operation.storage_limit = 0; + reveal_operation.kind = OperationKind::REVEAL; + reveal_operation.operation_data = + Proto::mod_Operation::OneOfoperation_data::reveal_operation_data( + Proto::RevealOperationData { + public_key: parse_public_key( + "edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp", + ) + .unwrap() + .to_bytes() + .into(), + }, + ); + + op_list.add_operation(reveal_operation); + + let mut delegate_operation = Proto::Operation::default(); + delegate_operation.source = "tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4".to_string().into(); + delegate_operation.fee = 1257; + delegate_operation.counter = 1879; + delegate_operation.gas_limit = 10100; + delegate_operation.storage_limit = 0; + delegate_operation.kind = OperationKind::DELEGATION; + delegate_operation.operation_data = + Proto::mod_Operation::OneOfoperation_data::delegation_operation_data( + Proto::DelegationOperationData { + delegate: "tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M".to_string().into(), + }, + ); + + op_list.add_operation(delegate_operation); + + let decoded_private_key = decode_check( + "edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc", + tw_encoding::base58::Alphabet::Bitcoin, + ) + .unwrap(); + let key = PrivateKey::new(decoded_private_key[4..].to_vec()).unwrap(); + + let expected_forged_bytes = hex::encode(op_list.forge(Some(&key)).unwrap(), false); + let expected_signature = "871693145f2dc72861ff6816e7ac3ce93c57611ac09a4c657a5a35270fa57153334c14cd8cae94ee228b6ef52f0e3f10948721e666318bc54b6c455404b11e03"; + let expected_signed_bytes = format!("{}{}", expected_forged_bytes, expected_signature); + + let signed_bytes = TezosSigner::sign_operation_list(&key, &op_list).unwrap(); + + assert_eq!(hex::encode(&signed_bytes, false), expected_signed_bytes); +} diff --git a/rust/tw_coin_entry/src/error/address_error.rs b/rust/tw_coin_entry/src/error/address_error.rs index 520eb41075a..77cf56485c0 100644 --- a/rust/tw_coin_entry/src/error/address_error.rs +++ b/rust/tw_coin_entry/src/error/address_error.rs @@ -25,6 +25,7 @@ pub enum AddressError { InvalidChecksum, InvalidWitnessProgram, Internal, + FromHashError, } impl From for AddressError { @@ -32,3 +33,9 @@ impl From for AddressError { AddressError::FromHexError } } + +impl From for AddressError { + fn from(_: tw_hash::Error) -> Self { + AddressError::FromHashError + } +} diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index d3617a4c475..3e503c15b66 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -36,6 +36,7 @@ tw_ronin = { path = "../chains/tw_ronin" } tw_solana = { path = "../chains/tw_solana" } tw_substrate = { path = "../frameworks/tw_substrate" } tw_sui = { path = "../chains/tw_sui" } +tw_tezos = { path = "../chains/tw_tezos" } tw_thorchain = { path = "../chains/tw_thorchain" } tw_ton = { path = "../chains/tw_ton" } tw_utxo = { path = "../frameworks/tw_utxo" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 146743ec386..9a7a31f4aae 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -30,6 +30,7 @@ pub enum BlockchainType { Ronin, Solana, Sui, + Tezos, TheOpenNetwork, Thorchain, Zcash, diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 419be6b9eb2..718e3018699 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -30,6 +30,7 @@ use tw_ronin::entry::RoninEntry; use tw_solana::entry::SolanaEntry; use tw_substrate::entry::SubstrateEntry; use tw_sui::entry::SuiEntry; +use tw_tezos::entry::TezosEntry; use tw_thorchain::entry::ThorchainEntry; use tw_ton::entry::TheOpenNetworkEntry; use tw_zcash::entry::ZcashEntry; @@ -58,6 +59,7 @@ const RIPPLE: RippleEntry = RippleEntry; const RONIN: RoninEntry = RoninEntry; const SOLANA: SolanaEntry = SolanaEntry; const SUI: SuiEntry = SuiEntry; +const TEZOS: TezosEntry = TezosEntry; const THE_OPEN_NETWORK: TheOpenNetworkEntry = TheOpenNetworkEntry; const THORCHAIN: ThorchainEntry = ThorchainEntry; const ZCASH: ZcashEntry = ZcashEntry; @@ -87,6 +89,7 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&RONIN), BlockchainType::Solana => Ok(&SOLANA), BlockchainType::Sui => Ok(&SUI), + BlockchainType::Tezos => Ok(&TEZOS), BlockchainType::TheOpenNetwork => Ok(&THE_OPEN_NETWORK), BlockchainType::Thorchain => Ok(&THORCHAIN), BlockchainType::Zcash => Ok(&ZCASH), diff --git a/rust/tw_tests/Cargo.toml b/rust/tw_tests/Cargo.toml index 3bc066b06bd..bd78a8583fe 100644 --- a/rust/tw_tests/Cargo.toml +++ b/rust/tw_tests/Cargo.toml @@ -20,6 +20,7 @@ wallet-core-rs = { path = "../wallet_core_rs" } # Chain specific: tw_cosmos_sdk = { path = "../tw_cosmos_sdk", features = ["test-utils"] } tw_solana = { path = "../chains/tw_solana" } +tw_tezos = { path = "../chains/tw_tezos" } tw_ton = { path = "../chains/tw_ton" } tw_ton_sdk = { path = "../frameworks/tw_ton_sdk" } tw_utxo = { path = "../frameworks/tw_utxo" } diff --git a/rust/tw_tests/tests/chains/mod.rs b/rust/tw_tests/tests/chains/mod.rs index 885e1b26e0f..22eb066ae9a 100644 --- a/rust/tw_tests/tests/chains/mod.rs +++ b/rust/tw_tests/tests/chains/mod.rs @@ -26,6 +26,7 @@ mod ripple; mod solana; mod sui; mod tbinance; +mod tezos; mod thorchain; mod ton; mod zcash; diff --git a/rust/tw_tests/tests/chains/tezos/mod.rs b/rust/tw_tests/tests/chains/tezos/mod.rs new file mode 100644 index 00000000000..1cd61b2c928 --- /dev/null +++ b/rust/tw_tests/tests/chains/tezos/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod tezos_compile; +mod tezos_sign; diff --git a/rust/tw_tests/tests/chains/tezos/tezos_compile.rs b/rust/tw_tests/tests/chains/tezos/tezos_compile.rs new file mode 100644 index 00000000000..7ebff9fa06b --- /dev/null +++ b/rust/tw_tests/tests/chains/tezos/tezos_compile.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex; +use tw_keypair::tw::PrivateKey; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Tezos::Proto; +use tw_proto::Tezos::Proto::mod_Operation::OperationKind; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_tezos::compiler::TezosCompiler; +use tw_tezos::signer::TezosSigner; + +#[test] +fn test_tezos_compile_with_signatures() { + let private_key = PrivateKey::new( + hex::decode("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f").unwrap(), + ) + .unwrap(); + let reveal_key = + hex::decode("311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95").unwrap(); + + let mut input = Proto::SigningInput::default(); + let mut operations = Proto::OperationList::default(); + operations.branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp".into(); + + let mut reveal = Proto::Operation::default(); + reveal.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(); + reveal.fee = 1272; + reveal.counter = 30738; + reveal.gas_limit = 10100; + reveal.storage_limit = 257; + reveal.kind = OperationKind::REVEAL; + reveal.operation_data = Proto::mod_Operation::OneOfoperation_data::reveal_operation_data( + Proto::RevealOperationData { + public_key: reveal_key.into(), + }, + ); + operations.operations.push(reveal); + + let mut transaction = Proto::Operation::default(); + transaction.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(); + transaction.fee = 1272; + transaction.counter = 30739; + transaction.gas_limit = 10100; + transaction.storage_limit = 257; + transaction.kind = OperationKind::TRANSACTION; + transaction.operation_data = + Proto::mod_Operation::OneOfoperation_data::transaction_operation_data( + Proto::TransactionOperationData { + amount: 1, + destination: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + encoded_parameter: vec![].into(), + parameters: None, + }, + ); + operations.operations.push(transaction); + + input.operation_list = Some(operations); + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Tezos, &input); + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + hex::encode(&preimage_output.data_hash, false), + "12e4f8b17ad3b316a5a56960db76c7d6505dbf2fff66106be75c8d6753daac0e" + ); + + let signature = hex::decode("0217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05").unwrap(); + + // Step 3: Compile transaction info + let expected_tx = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff956c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b95721000217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05"; + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile(CoinType::Tezos, &input, vec![signature.clone()], vec![]); + assert_eq!(output.error, SigningError::OK); + assert_eq!(hex::encode(&output.encoded, false), expected_tx); + + // Double check with direct signing + input.private_key = private_key.bytes().into(); + let signed_output = TezosSigner::sign(&TestCoinContext::default(), input.clone()); + assert_eq!(hex::encode(&signed_output.encoded, false), expected_tx); + + // Negative: inconsistent signatures & publicKeys + let output = TezosCompiler::compile( + &TestCoinContext::default(), + input, + vec![signature.clone(), signature], + vec![], + ); + assert_eq!(output.encoded.len(), 0); + assert_eq!(output.error, SigningError::Error_signatures_count); +} diff --git a/rust/tw_tests/tests/chains/tezos/tezos_sign.rs b/rust/tw_tests/tests/chains/tezos/tezos_sign.rs new file mode 100644 index 00000000000..3f881a2cd68 --- /dev/null +++ b/rust/tw_tests/tests/chains/tezos/tezos_sign.rs @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex; +use tw_proto::Tezos::Proto::{self, mod_Operation::OperationKind}; + +#[test] +fn test_sign_fa12() { + // https://ghostnet.tzkt.io/ooTBu7DLbeC7DmVfXEsp896A6WTwimedbsM9QRqUVtqA8Vxt6D3/2993172 + let key = + hex::decode("363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6").unwrap(); + + let input = Proto::SigningInput { + private_key: key.into(), + operation_list: Some(Proto::OperationList { + branch: "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp".into(), + operations: vec![Proto::Operation { + source: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + fee: 100000, + counter: 2993172, + gas_limit: 100000, + storage_limit: 0, + kind: OperationKind::TRANSACTION, + operation_data: + Proto::mod_Operation::OneOfoperation_data::transaction_operation_data( + Proto::TransactionOperationData { + amount: 0, + destination: "KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5".into(), + parameters: Some(Proto::OperationParameters { + parameters: Proto::mod_OperationParameters::OneOfparameters::fa12_parameters(Proto::FA12Parameters { + entrypoint: "transfer".into(), + from: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + to: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + value: "123".into(), + }).into(), + }), + encoded_parameter: vec![].into(), + }, + ), + }], + }), + ..Default::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Tezos, input); + assert_eq!(hex::encode(&output.encoded, false), "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb012914d768155fba2df319a81136e8e3e573b9cadb1676834490c90212615d271da029b6b0531e290e9063bcdb40bea43627af048b18e036f02be2b6b22fc8b307"); +} + +#[test] +fn test_sign_fa2() { + // https://ghostnet.tzkt.io/onxLBoPaf23M3A8kHTwncSFG2GVXPfnGXUhkC8BhKj8QDdCEbng + let key = + hex::decode("363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6").unwrap(); + + let mut transaction = Proto::Operation::default(); + transaction.source = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(); + transaction.fee = 100000; + transaction.counter = 2993173; + transaction.gas_limit = 100000; + transaction.storage_limit = 0; + transaction.kind = OperationKind::TRANSACTION; + transaction.operation_data = + Proto::mod_Operation::OneOfoperation_data::transaction_operation_data( + Proto::TransactionOperationData { + amount: 0, + destination: "KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj".into(), + encoded_parameter: vec![].into(), + parameters: Some(Proto::OperationParameters { + parameters: Proto::mod_OperationParameters::OneOfparameters::fa2_parameters( + Proto::FA2Parameters { + entrypoint: "transfer".into(), + txs_object: vec![Proto::TxObject { + from: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + txs: vec![Proto::Txs { + to: "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP".into(), + token_id: "0".into(), + amount: "10".into(), + }], + }], + }, + ) + .into(), + }), + }, + ); + + let mut input = Proto::SigningInput::default(); + input.private_key = key.into(); + input.operation_list = Some(Proto::OperationList { + branch: "BKvEAX9HXfJZWYfTQbR1C7B3ADoKY6a1aKVRF7qQqvc9hS8Rr3m".into(), + operations: vec![transaction], + }); + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Tezos, input); + assert_eq!(hex::encode(&output.encoded, false), "1b1f9345dc9f77bd24b09034d1d2f9a28f02ac837f49db54b8d68341f53dc4b76c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a552d24710d6c59383286700c6c2917b25a6c1fa8b587e593c289dd47704278796792f1e522c1623845ec991e292b0935445e6994850bd03f035a006c5ed93806"); +} + +#[test] +fn test_blind_sign() { + // Successfully broadcasted: https://ghostnet.tzkt.io/oobGgTkDNz9eqGVXiU4wShPZydkroCrmbKjoDcfSqhnM7GmcdEu/15229334 + let key = + hex::decode("3caf5afaed067890cd850efd1555df351aa482badb4a541c29261f1acf261bf5").unwrap(); + let bytes = hex::decode("64aa7792af40de41371a72b3342daa7bf3d2b5a84511e9074341fdd52148dd9d6c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542850f96c3a1079d780080ade2040155959998da7e79231e2be8ed8ff373ac1b1574b000ffff04737761700000009e070703060707020000000807070508030b000007070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a4438457907070080dac409070700bdf892a1a291e196aa0503066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0497c3a107f10f180001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f76650000002d070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900006c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0498c3a107f70f090001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f766500000036070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900bdf892a1a291e196aa056c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542e71599c3a107fabb01400001b1f0d7affc39861f7f5c75f917f683d2e9f55e3100ffff04737761700000009a070700000707000007070001070700bdf892a1a291e196aa05070700a3f683c2a6d80a07070100000018323032332d30322d32345431333a34303a32322e3332385a070705090100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484805090100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a443845796c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049ac3a107f50f1b000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050507070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a52717572617400006c00ad756cb46ba6f59efa8bd10ff544ba9d20d0954285109bc3a107a0820100000155959998da7e79231e2be8ed8ff373ac1b1574b000ffff0473776170000000a1070703060707020000000807070508030b000807070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a44384579070700a3f683c2a6d80a070700a4f096bfbe9df6f0e00603066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049cc3a107ed0f00000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050807070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a5271757261740000").unwrap(); + + let mut input = Proto::SigningInput::default(); + input.private_key = key.into(); + input.encoded_operations = bytes.into(); + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Tezos, input); + + assert_eq!(hex::encode(&output.encoded, false), "64aa7792af40de41371a72b3342daa7bf3d2b5a84511e9074341fdd52148dd9d6c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542850f96c3a1079d780080ade2040155959998da7e79231e2be8ed8ff373ac1b1574b000ffff04737761700000009e070703060707020000000807070508030b000007070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a4438457907070080dac409070700bdf892a1a291e196aa0503066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0497c3a107f10f180001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f76650000002d070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900006c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd0498c3a107f70f090001543aa1803f0bbe2099809ab067dfa8a4cbc1c26a00ffff07617070726f766500000036070701000000244b5431516f64676b5974754e79664a726a72673854515a586d64544643616d373268533900bdf892a1a291e196aa056c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542e71599c3a107fabb01400001b1f0d7affc39861f7f5c75f917f683d2e9f55e3100ffff04737761700000009a070700000707000007070001070700bdf892a1a291e196aa05070700a3f683c2a6d80a07070100000018323032332d30322d32345431333a34303a32322e3332385a070705090100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484805090100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a443845796c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049ac3a107f50f1b000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050507070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a52717572617400006c00ad756cb46ba6f59efa8bd10ff544ba9d20d0954285109bc3a107a0820100000155959998da7e79231e2be8ed8ff373ac1b1574b000ffff0473776170000000a1070703060707020000000807070508030b000807070100000018323032332d30322d32345431333a34303a32322e3332385a07070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a7376617868484807070100000024747a315377326d4641557a626b6d37646b47434472626542734a54547456374a44384579070700a3f683c2a6d80a070700a4f096bfbe9df6f0e00603066c00ad756cb46ba6f59efa8bd10ff544ba9d20d09542cd049cc3a107ed0f00000193d22b59c496c94504729be1c671ec1d1d7a9cf000ffff107570646174655f6f70657261746f72730000005f020000005a050807070100000024747a31625443473754415535523736356f4458694c4d63385a4537546a73766178684848070701000000244b543147504a44546638475a73704363616e6147324b684d764775334e4a5271757261740000e10077fc3068aaaf1c7779e1dc2c396b3b40d73ddda04648bf4b16ac2e747c89b461771488e80da3aa30fc18c90de99fd358bfb76683f3c3ec250b1ee09b6d07"); +} + +#[test] +fn test_sign() { + let key = + hex::decode("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f").unwrap(); + let reveal_key = + hex::decode("311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95").unwrap(); + + let mut input = Proto::SigningInput::default(); + input.private_key = key.into(); + input.operation_list = Some(Proto::OperationList { + branch: "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp".into(), + operations: vec![], + }); + + let mut reveal = Proto::Operation::default(); + reveal.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(); + reveal.fee = 1272; + reveal.counter = 30738; + reveal.gas_limit = 10100; + reveal.storage_limit = 257; + reveal.kind = OperationKind::REVEAL; + reveal.operation_data = Proto::mod_Operation::OneOfoperation_data::reveal_operation_data( + Proto::RevealOperationData { + public_key: reveal_key.into(), + }, + ); + + let mut transaction = Proto::Operation::default(); + transaction.source = "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(); + transaction.fee = 1272; + transaction.counter = 30739; + transaction.gas_limit = 10100; + transaction.storage_limit = 257; + transaction.kind = OperationKind::TRANSACTION; + transaction.operation_data = + Proto::mod_Operation::OneOfoperation_data::transaction_operation_data( + Proto::TransactionOperationData { + amount: 1, + destination: "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW".into(), + encoded_parameter: vec![].into(), + parameters: None, + }, + ); + + input.operation_list = Some(Proto::OperationList { + branch: "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp".into(), + operations: vec![reveal, transaction], + }); + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Tezos, input); + + assert_eq!(hex::encode(&output.encoded, false), "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff956c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b95721000217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05"); +} diff --git a/rust/tw_tests/tests/coin_address_derivation_test.rs b/rust/tw_tests/tests/coin_address_derivation_test.rs index 026b260128c..ad014e6c81e 100644 --- a/rust/tw_tests/tests/coin_address_derivation_test.rs +++ b/rust/tw_tests/tests/coin_address_derivation_test.rs @@ -165,6 +165,7 @@ fn test_coin_address_derivation() { CoinType::XRP => "r9cwJ8hM13jodBBGtioB44FUZ5HwWGwqfX", CoinType::Groestlcoin => "grs1qten42eesehw0ktddcp0fws7d3ycsqez35034a2", CoinType::Decred => "DsbEmWV6ZZBsUJY2vVi5u7H62GUfBFPBfoF", + CoinType::Tezos => "tz1dR8WXe5oCvjd3Lhy6cTqSs4ZxRbkRWi3a", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/src/Tezos/Address.cpp b/src/Tezos/Address.cpp deleted file mode 100644 index f079b45ce96..00000000000 --- a/src/Tezos/Address.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Address.h" -#include "BinaryCoding.h" -#include "Forging.h" - -#include "../Base58.h" -#include "../BinaryCoding.h" -#include "../Hash.h" -#include "../HexCoding.h" - -#include - -namespace TW::Tezos { - -/// Address prefixes. -const std::array tz1Prefix{6, 161, 159}; -const std::array tz2Prefix{6, 161, 161}; -const std::array tz3Prefix{6, 161, 164}; -const std::array kt1Prefix{2, 90, 121}; - -bool Address::isValid(const std::string& string) { - const auto decoded = Base58::decodeCheck(string); - if (decoded.size() != Address::size) { - return false; - } - - // verify prefix - if (std::equal(tz1Prefix.begin(), tz1Prefix.end(), decoded.begin()) || - std::equal(tz2Prefix.begin(), tz2Prefix.end(), decoded.begin()) || - std::equal(tz3Prefix.begin(), tz3Prefix.end(), decoded.begin())) { - return true; - } - - // contract prefix - if (std::equal(kt1Prefix.begin(), kt1Prefix.end(), decoded.begin())) { - return true; - } - - return false; -} - -Address::Address(const PublicKey& publicKey) { - auto encoded = Data(publicKey.bytes.begin(), publicKey.bytes.end()); - auto hash = Hash::blake2b(encoded, 20); - Data addressData; - if (publicKey.type == TWPublicKeyTypeSECP256k1) { - addressData = Data({6, 161, 161}); - } else if (publicKey.type == TWPublicKeyTypeED25519){ - addressData = Data({6, 161, 159}); - } else { - throw std::invalid_argument("unsupported public key type"); - } - append(addressData, hash); - if (addressData.size() != Address::size) - throw std::invalid_argument("Invalid address key data"); - std::copy(addressData.data(), addressData.data() + Address::size, bytes.begin()); -} - -std::string Address::deriveOriginatedAddress(const std::string& operationHash, int operationIndex) { - // Decode and remove 2 byte prefix. - auto decoded = Base58::decodeCheck(operationHash); - decoded.erase(decoded.begin(), decoded.begin() + 2); - TW::encode32BE(operationIndex, decoded); - - auto hash = Hash::blake2b(decoded, 20); - - auto prefix = Data({2, 90, 121}); - prefix.insert(prefix.end(), hash.begin(), hash.end()); - - return Base58::encodeCheck(prefix); -} - -Data Address::forgePKH() const { - std::string s = string(); - return forgePublicKeyHash(s); -} - -Data Address::forge() const { - // normal address - // https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/codec.ts#L183 - if (std::equal(tz1Prefix.begin(), tz1Prefix.end(), bytes.begin()) || - std::equal(tz2Prefix.begin(), tz2Prefix.end(), bytes.begin()) || - std::equal(tz3Prefix.begin(), tz3Prefix.end(), bytes.begin())) { - std::string s = string(); - Data forgedPKH = forgePublicKeyHash(s); - Data forged = Data(); - forged.insert(forged.end(), 0x00); - forged.insert(forged.end(), forgedPKH.begin(), forgedPKH.end()); - return forged; - } - - // contract address - // https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/codec.ts#L183 - if (std::equal(kt1Prefix.begin(), kt1Prefix.end(), bytes.begin())) { - std::string s = string(); - Data forgedPrefix = forgePrefix(kt1Prefix, s); - Data forged = Data(); - forged.insert(forged.end(), 0x01); - forged.insert(forged.end(), forgedPrefix.begin(), forgedPrefix.end()); - forged.insert(forged.end(), 0x00); - return forged; - } - - throw std::invalid_argument("invalid address"); -} - -} // namespace TW::Tezos diff --git a/src/Tezos/Address.h b/src/Tezos/Address.h deleted file mode 100644 index b2f83cffcbd..00000000000 --- a/src/Tezos/Address.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "../Base58Address.h" -#include "Data.h" -#include "../PublicKey.h" - -#include - -namespace TW::Tezos { - -class Address : public TW::Base58Address<23> { - public: - /// Determines whether a string makes a valid address. - static bool isValid(const std::string& string); - - /// Initializes a Tezos address with a string representation. - explicit Address(const std::string& string) : TW::Base58Address<23>(string) {} - - /// Initializes an address with a collection of bytes. - explicit Address(const std::vector& data) : TW::Base58Address<23>(data) {} - - /// Initializes a Tezos address with a public key. - explicit Address(const PublicKey& publicKey); - - /// Derives an originated address from the given inputs. - static std::string deriveOriginatedAddress(const std::string& operationHash, int operationIndex); - - /// Forge an address to hex bytes. - Data forge() const; - - // without type prefix - Data forgePKH() const; -}; - -} // namespace TW::Tezos diff --git a/src/Tezos/BinaryCoding.cpp b/src/Tezos/BinaryCoding.cpp deleted file mode 100644 index 6be3edd6442..00000000000 --- a/src/Tezos/BinaryCoding.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "../Base58.h" -#include "Data.h" -#include "../HexCoding.h" -#include "../PublicKey.h" -#include "../PrivateKey.h" - -#include -#include - -namespace TW::Tezos { - -std::string base58ToHex(const std::string& string, size_t prefixLength) { - const auto decoded = Base58::decodeCheck(string); - if (decoded.size() < prefixLength) { - return ""; - } - Data v(decoded.data() + prefixLength, decoded.data() + decoded.size()); - return TW::hex(v); -} - -PublicKey parsePublicKey(const std::string& publicKey) { - const auto decoded = Base58::decodeCheck(publicKey); - - std::array prefix; - enum TWPublicKeyType type; - std::array ed25519Prefix = {13, 15, 37, 217}; - std::array secp256k1Prefix = {3, 254, 226, 86}; - - if (std::equal(std::begin(ed25519Prefix), std::end(ed25519Prefix), std::begin(decoded))) { - prefix = ed25519Prefix; - type = TWPublicKeyTypeED25519; - } else if (std::equal(std::begin(secp256k1Prefix), std::end(secp256k1Prefix), std::begin(decoded))) { - prefix = secp256k1Prefix; - type = TWPublicKeyTypeSECP256k1; - } else { - throw std::invalid_argument("Unsupported Public Key Type"); - } - auto pk = Data(); - if (type == TWPublicKeyTypeED25519 && decoded.size() != 32 + prefix.size()) { - throw std::invalid_argument("Invalid Public Key"); - } - if (type == TWPublicKeyTypeSECP256k1 && decoded.size() != 33 + prefix.size()) { - throw std::invalid_argument("Invalid Public Key"); - } - append(pk, Data(decoded.begin() + prefix.size(), decoded.end())); - - return PublicKey(pk, type); -} - -PrivateKey parsePrivateKey(const std::string& privateKey) { - const auto decoded = Base58::decodeCheck(privateKey); - auto pk = Data(); - auto prefix_size = 4ul; - - if (decoded.size() != 32 + prefix_size) { - throw std::invalid_argument("Invalid Public Key"); - } - append(pk, Data(decoded.begin() + prefix_size, decoded.end())); - return PrivateKey(pk, TWCurveSECP256k1); -} - -} // namespace TW::Tezos diff --git a/src/Tezos/BinaryCoding.h b/src/Tezos/BinaryCoding.h deleted file mode 100644 index 24849592375..00000000000 --- a/src/Tezos/BinaryCoding.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Data.h" -#include "../PublicKey.h" -#include "../PrivateKey.h" - -#include - -namespace TW::Tezos { - -PublicKey parsePublicKey(const std::string& publicKey); -PrivateKey parsePrivateKey(const std::string& privateKey); -std::string base58ToHex(const std::string& data, size_t prefixLength); - -} // namespace TW::Tezos diff --git a/src/Tezos/Entry.cpp b/src/Tezos/Entry.cpp index 0b197d93e5a..357c4e994df 100644 --- a/src/Tezos/Entry.cpp +++ b/src/Tezos/Entry.cpp @@ -1,71 +1,20 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. +// // SPDX-License-Identifier: Apache-2.0 +// // +// // Copyright © 2017 Trust Wallet. #include "Entry.h" - -#include "Address.h" -#include "proto/TransactionCompiler.pb.h" -#include "Signer.h" +#include "HexCoding.h" +#include "proto/Tezos.pb.h" namespace TW::Tezos { -// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. - -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); -} - -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); -} - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); -} - -std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { - return Signer::signJSON(json, key); -} - -TW::Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - auto operationList = TW::Tezos::OperationList(input.operation_list().branch()); - for (TW::Tezos::Proto::Operation operation : input.operation_list().operations()) { - operationList.addOperation(operation); - } - - auto preImage = Signer().buildUnsignedTx(operationList); - - // get preImage hash - Data watermarkedData = Data(); - watermarkedData.push_back(0x03); - append(watermarkedData, preImage); - auto preImageHash = Hash::blake2b(watermarkedData, 32); - - output.set_data_hash(preImageHash.data(), preImageHash.size()); - output.set_data(preImage.data(), preImage.size()); - }); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, [[maybe_unused]] const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerTemplate( - txInputData, [&](const auto& input, auto& output) { - if (signatures.size() != 1) { - output.set_error(Common::Proto::Error_signatures_count); - output.set_error_message(Common::Proto::SigningError_Name(Common::Proto::Error_signatures_count)); - return; - } - - auto operationList = TW::Tezos::OperationList(input.operation_list().branch()); - for (TW::Tezos::Proto::Operation operation : input.operation_list().operations()) { - operationList.addOperation(operation); - } - auto tx = Signer().buildSignedTx(operationList, signatures[0]); - - output.set_encoded(tx.data(), tx.size()); - }); +std::string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return signJSONHelper( + coin, + json, + key, + [](const Proto::SigningOutput& output) { return hex(output.encoded()); } + ); } } // namespace TW::Tezos diff --git a/src/Tezos/Entry.h b/src/Tezos/Entry.h index 3354a2ee744..cd18b5a9bac 100644 --- a/src/Tezos/Entry.h +++ b/src/Tezos/Entry.h @@ -4,22 +4,14 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Tezos { -/// Entry point for implementation of Tezos coin. -/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { +class Entry final : public Rust::RustCoinEntryWithSignJSON { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; - bool supportsJSONSigning() const override { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; + bool supportsJSONSigning() const final { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const final; }; } // namespace TW::Tezos diff --git a/src/Tezos/Forging.cpp b/src/Tezos/Forging.cpp deleted file mode 100644 index 9c7b22b41ec..00000000000 --- a/src/Tezos/Forging.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Forging.h" -#include "Address.h" -#include "BinaryCoding.h" -#include "../HexCoding.h" -#include "../proto/Tezos.pb.h" -#include - -namespace TW::Tezos { - -namespace { - -constexpr const char* gTezosContractAddressPrefix{"KT1"}; - -void encodePrefix(const std::string& address, Data& forged) { - const auto decoded = Base58::decodeCheck(address); - constexpr auto prefixSize{3}; - forged.insert(forged.end(), decoded.begin() + prefixSize, decoded.end()); -} - -} // namespace - -// Forge the given boolean into a hex encoded string. -Data forgeBool(bool input) { - unsigned char result = input ? 0xff : 0x00; - return Data{result}; -} - -Data forgeInt32(int value, int len) { - Data out(len); - for (int i = len - 1; i >= 0; i--, value >>= 8) { - out[i] = (value & 0xFF); - } - return out; -} - -Data forgeString(const std::string& value, std::size_t len) { - auto bytes = data(value); - auto result = forgeInt32(static_cast(bytes.size()), static_cast(len)); - append(result, bytes); - return result; -} - -Data forgeEntrypoint(const std::string& value) { - if (value == "default") - return Data{0x00}; - else if (value == "root") - return Data{0x01}; - else if (value == "do") - return Data{0x02}; - else if (value == "set_delegate") - return Data{0x03}; - else if (value == "remove_delegate") - return Data{0x04}; - else { - Data forged{0xff}; - append(forged, forgeString(value, 1)); - return forged; - } -} - -// Forge the given public key hash into a hex encoded string. -// Note: This function supports tz1, tz2 and tz3 addresses. -Data forgePublicKeyHash(const std::string& publicKeyHash) { - Data forged = Data(); - // Adjust prefix based on tz1, tz2 or tz3. - switch ((char)publicKeyHash[2]) { - case '1': - forged.push_back(0x00); - break; - case '2': - forged.push_back(0x01); - break; - case '3': - forged.push_back(0x02); - break; - default: - throw std::invalid_argument("Invalid Prefix"); - } - encodePrefix(publicKeyHash, forged); - return forged; -} - -Data forgeAddress(const std::string& address) { - if (address.size() < 3) { - throw std::invalid_argument("Invalid address size"); - } - auto prefix = address.substr(0, 3); - - if (prefix == "tz1" || prefix == "tz2" || prefix == "tz3") { - Data forged{0x00}; - append(forged, forgePublicKeyHash(address)); - return forged; - } - - if (prefix == gTezosContractAddressPrefix) { - Data forged{0x01}; - encodePrefix(address, forged); - forged.emplace_back(0x00); - return forged; - } - throw std::invalid_argument("Invalid Prefix"); -} - -// https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/codec.ts#L19 -Data forgePrefix(std::array prefix, const std::string& val) { - const auto decoded = Base58::decodeCheck(val); - if (!std::equal(prefix.begin(), prefix.end(), decoded.begin())) { - throw std::invalid_argument("prefix not match"); - } - - const auto prefixSize = 3; - Data forged = Data(); - forged.insert(forged.end(), decoded.begin() + prefixSize, decoded.end()); - return forged; -} - -// Forge the given public key into a hex encoded string. -Data forgePublicKey(PublicKey publicKey) { - std::string tag; - std::array prefix; - if (publicKey.type == TWPublicKeyTypeED25519) { - prefix = {13, 15, 37, 217}; - tag = "00"; - } else if (publicKey.type == TWPublicKeyTypeSECP256k1) { - prefix = {3, 254, 226, 86}; - tag = "01"; - } - auto data = Data(prefix.begin(), prefix.end()); - auto bytes = Data(publicKey.bytes.begin(), publicKey.bytes.end()); - append(data, bytes); - - auto pk = Base58::encodeCheck(data); - auto decoded = tag + base58ToHex(pk, 4); - return parse_hex(decoded); -} - -// Forge the given zarith hash into a hex encoded string. -Data forgeZarith(uint64_t input) { - Data forged = Data(); - while (input >= 0x80) { - forged.push_back(static_cast((input & 0xff) | 0x80)); - input >>= 7; - } - forged.push_back(static_cast(input)); - return forged; -} - -// Forge the given operation. -Data forgeOperation(const Proto::Operation& operation) { - using namespace Proto; - auto forged = Data(); - auto source = Address(operation.source()); - auto forgedSource = source.forgePKH(); //https://github.com/ecadlabs/taquito/blob/master/packages/taquito-local-forging/src/schema/operation.ts#L40 - auto forgedFee = forgeZarith(operation.fee()); - auto forgedCounter = forgeZarith(operation.counter()); - auto forgedGasLimit = forgeZarith(operation.gas_limit()); - auto forgedStorageLimit = forgeZarith(operation.storage_limit()); - - if (operation.kind() == Operation_OperationKind_REVEAL) { - enum TWPublicKeyType type; - if (operation.reveal_operation_data().public_key().size() == 32) { - type = TWPublicKeyTypeED25519; - } else if (operation.reveal_operation_data().public_key().size() == 33) { - type = TWPublicKeyTypeSECP256k1; - } else { - throw std::invalid_argument("unsupported public key type"); - } - auto publicKey = PublicKey(data(operation.reveal_operation_data().public_key()), type); - auto forgedPublicKey = forgePublicKey(publicKey); - - forged.push_back(Operation_OperationKind_REVEAL); - append(forged, forgedSource); - append(forged, forgedFee); - append(forged, forgedCounter); - append(forged, forgedGasLimit); - append(forged, forgedStorageLimit); - append(forged, forgedPublicKey); - return forged; - } - - if (operation.kind() == Operation_OperationKind_DELEGATION) { - auto delegate = operation.delegation_operation_data().delegate(); - - forged.push_back(Operation_OperationKind_DELEGATION); - append(forged, forgedSource); - append(forged, forgedFee); - append(forged, forgedCounter); - append(forged, forgedGasLimit); - append(forged, forgedStorageLimit); - if (!delegate.empty()) { - auto forgedPublicKeyHash = forgePublicKeyHash(delegate); - - append(forged, forgeBool(true)); - append(forged, forgedPublicKeyHash); - } else { - append(forged, forgeBool(false)); - } - return forged; - } - - if (operation.kind() == Operation_OperationKind_TRANSACTION) { - auto forgedAmount = forgeZarith(operation.transaction_operation_data().amount()); - auto forgedDestination = forgeAddress(operation.transaction_operation_data().destination()); - - forged.emplace_back(Operation_OperationKind_TRANSACTION); - append(forged, forgedSource); - append(forged, forgedFee); - append(forged, forgedCounter); - append(forged, forgedGasLimit); - append(forged, forgedStorageLimit); - append(forged, forgedAmount); - append(forged, forgedDestination); - if (!operation.transaction_operation_data().has_parameters() && operation.transaction_operation_data().encoded_parameter().empty()) { - append(forged, forgeBool(false)); - } else if (operation.transaction_operation_data().has_parameters()) { - append(forged, forgeBool(true)); - auto& parameters = operation.transaction_operation_data().parameters(); - switch (parameters.parameters_case()) { - case OperationParameters::kFa12Parameters: - append(forged, forgeEntrypoint(parameters.fa12_parameters().entrypoint())); - append(forged, forgeArray(forgeMichelson(FA12ParameterToMichelson(parameters.fa12_parameters())))); - break; - case OperationParameters::kFa2Parameters: - append(forged, forgeEntrypoint(parameters.fa2_parameters().entrypoint())); - append(forged, forgeArray(forgeMichelson(FA2ParameterToMichelson(parameters.fa2_parameters())))); - break; - case OperationParameters::PARAMETERS_NOT_SET: - break; - } - } else { - append(forged, TW::data(operation.transaction_operation_data().encoded_parameter())); - } - return forged; - } - - throw std::invalid_argument("Invalid operation kind"); -} - -Data forgePrim(const PrimValue& value) { - Data forged; - if (value.prim == "Pair") { - // https://tezos.gitlab.io/developer/encodings.html?highlight=pair#pairs - forged.reserve(2); - constexpr uint8_t nbArgs = 2; - // https://github.com/ecadlabs/taquito/blob/fd84d627171d24ce7ba81dd7b18763a95f16a99c/packages/taquito-local-forging/src/michelson/codec.ts#L195 - // https://github.com/baking-bad/netezos/blob/0bfd6db4e85ab1c99fb55503e476fe67cebd2dc5/Netezos/Forging/Local/LocalForge.Forgers.cs#L199 - const uint8_t preamble = static_cast(std::min(2 * nbArgs + static_cast(value.anots.size()) + 0x03, 9)); - forged.emplace_back(preamble); - forged.emplace_back(PrimType::Pair); - Data subForged; - for (auto&& cur : value.args) { - append(subForged, forgeMichelson(cur.value)); - } - append(forged, subForged); - } - return forged; -} - -Data forgeMichelson(const MichelsonValue::MichelsonVariant& value) { - auto visit_functor = [](const MichelsonValue::MichelsonVariant& value) -> Data { - if (std::holds_alternative(value)) { - return forgePrim(std::get(value)); - } else if (std::holds_alternative(value)) { - Data forged{1}; - append(forged, forgeString(std::get(value).string)); - return forged; - } else if (std::holds_alternative(value)) { - Data forged{0}; - auto res = int256_t(std::get(value)._int); - append(forged, forgeMichelInt(res)); - return forged; - } else if (std::holds_alternative(value)) { - return {}; - } else if (std::holds_alternative(value)) { - // array - Data forged{2}; - Data subForged; - auto array = std::get(value); - for (auto&& cur : array) { - std::visit([&subForged](auto&& arg) { append(subForged, forgeMichelson(arg)); }, cur); - } - append(forged, forgeArray(subForged)); - return forged; - } else { - throw std::invalid_argument("Invalid variant"); - } - }; - - return std::visit(visit_functor, value); -} - -Data forgeArray(const Data& data) { - auto forged = forgeInt32(static_cast(data.size())); - append(forged, data); - return forged; -} - -Data forgeMichelInt(const TW::int256_t& value) { - Data forged; - auto abs = boost::multiprecision::abs(value); - forged.emplace_back(static_cast(value.sign() < 0 ? (abs & 0x3f - 0x40) : (abs & 0x3f))); - abs >>= 6; - while (abs > 0) { - forged[forged.size() - 1] |= 0x80; - forged.emplace_back(static_cast(abs & 0x7F)); - abs >>= 7; - } - return forged; -} - -} // namespace TW::Tezos diff --git a/src/Tezos/Forging.h b/src/Tezos/Forging.h deleted file mode 100644 index 878c9e35880..00000000000 --- a/src/Tezos/Forging.h +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Michelson.h" -#include "uint256.h" -#include "../PublicKey.h" -#include "../proto/Tezos.pb.h" - -#include -#include -#include - -using namespace TW; - -namespace TW::Tezos { - -Data forgeBool(bool input); -Data forgeOperation(const Proto::Operation& operation); -Data forgeAddress(const std::string& address); -Data forgeArray(const Data& data); -Data forgePublicKeyHash(const std::string& publicKeyHash); -Data forgePrefix(std::array prefix, const std::string& val); -Data forgePublicKey(PublicKey publicKey); -Data forgeZarith(uint64_t input); -Data forgeInt32(int value, int len = 4); -Data forgeString(const std::string& value, std::size_t len = 4); -Data forgeEntrypoint(const std::string& value); -Data forgeMichelson(const MichelsonValue::MichelsonVariant& value); -Data forgeMichelInt(const TW::int256_t& value); -Data forgePrim(const PrimValue& value); - -} // namespace TW::Tezos diff --git a/src/Tezos/Michelson.cpp b/src/Tezos/Michelson.cpp deleted file mode 100644 index 18e0bfb79d0..00000000000 --- a/src/Tezos/Michelson.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Michelson.h" - -namespace TW::Tezos { - -MichelsonValue::MichelsonVariant FA12ParameterToMichelson(const Proto::FA12Parameters& data) { - MichelsonValue::MichelsonVariant address = StringValue{.string = data.from()}; - MichelsonValue::MichelsonVariant to = StringValue{.string = data.to()}; - MichelsonValue::MichelsonVariant amount = IntValue{._int = data.value()}; - auto primTransferInfos = PrimValue{.prim = "Pair", .args{{to}, {amount}}, .anots{}}; - return PrimValue{.prim = "Pair", .args{{address}, {primTransferInfos}}, .anots{}}; -} - -MichelsonValue::MichelsonVariant FA2ParameterToMichelson(const Proto::FA2Parameters& data) { - auto& txObj = *data.txs_object().begin(); - MichelsonValue::MichelsonVariant from = StringValue{.string = txObj.from()}; - auto& txTransferInfos = txObj.txs(0); - MichelsonValue::MichelsonVariant tokenId = IntValue{._int = txTransferInfos.token_id()}; - MichelsonValue::MichelsonVariant amount = IntValue{._int = txTransferInfos.amount()}; - auto primTransferInfos = PrimValue{.prim = "Pair", .args{{tokenId}, {amount}}, .anots{}}; - MichelsonValue::MichelsonVariant to = StringValue{.string = txTransferInfos.to()}; - MichelsonValue::MichelsonVariant txs = MichelsonValue::MichelsonArray{PrimValue{.prim = "Pair", .args{{to}, {primTransferInfos}}, .anots{}}}; - auto primTxs = PrimValue{.prim = "Pair", .args{{from}, {txs}}, .anots{}}; - return MichelsonValue::MichelsonArray{primTxs}; -} - -} // namespace TW::Tezos diff --git a/src/Tezos/Michelson.h b/src/Tezos/Michelson.h deleted file mode 100644 index 556383f2e12..00000000000 --- a/src/Tezos/Michelson.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include -#include -#include -#include - -#include "../proto/Tezos.pb.h" - -#pragma once - -namespace TW::Tezos { - -enum PrimType : std::uint8_t { - Pair = 7, -}; - -struct MichelsonValue; - -struct PrimValue { - std::string prim; - std::vector args; - std::vector anots; -}; - -struct BytesValue { - std::string bytes; -}; - -struct StringValue { - std::string string; -}; - -struct IntValue { - std::string _int; -}; - -struct MichelsonValue { - using MichelsonArray = std::vector>; - using MichelsonVariant = std::variant< - PrimValue, - BytesValue, - StringValue, - IntValue, - MichelsonArray>; - MichelsonVariant value; -}; - -MichelsonValue::MichelsonVariant FA12ParameterToMichelson(const Proto::FA12Parameters& data); -MichelsonValue::MichelsonVariant FA2ParameterToMichelson(const Proto::FA2Parameters& data); - -} // namespace TW::Tezos diff --git a/src/Tezos/OperationList.cpp b/src/Tezos/OperationList.cpp deleted file mode 100644 index d293999080a..00000000000 --- a/src/Tezos/OperationList.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "OperationList.h" -#include "Forging.h" -#include "../Base58.h" - -namespace TW::Tezos { - -Tezos::OperationList::OperationList(const std::string& str) { - branch = str; -} - -void Tezos::OperationList::addOperation(const Operation& operation) { - operation_list.push_back(operation); -} - -// Forge the given branch to a hex encoded string. -Data Tezos::OperationList::forgeBranch() const { - std::array prefix = {1, 52}; - const auto decoded = Base58::decodeCheck(branch); - if (decoded.size() != 34 || !std::equal(prefix.begin(), prefix.end(), decoded.begin())) { - throw std::invalid_argument("Invalid branch for forge"); - } - auto forged = Data(); - forged.insert(forged.end(), decoded.begin() + prefix.size(), decoded.end()); - return forged; -} - -Data Tezos::OperationList::forge(const PrivateKey& privateKey) const { - auto forged = forgeBranch(); - - for (auto operation : operation_list) { - // If it's REVEAL operation, inject the public key if not specified - if (operation.kind() == Operation::REVEAL && operation.has_reveal_operation_data()) { - auto* revealOperationData = operation.mutable_reveal_operation_data(); - if (revealOperationData->public_key().empty()) { - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - } - } - - append(forged, forgeOperation(operation)); - } - - return forged; -} - -Data TW::Tezos::OperationList::forge() const { - auto forged = forgeBranch(); - - for (auto operation : operation_list) { - append(forged, forgeOperation(operation)); - } - - return forged; -} - -} // namespace TW::Tezos diff --git a/src/Tezos/OperationList.h b/src/Tezos/OperationList.h deleted file mode 100644 index 3c673f50d79..00000000000 --- a/src/Tezos/OperationList.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Data.h" -#include "proto/Tezos.pb.h" -#include "../PrivateKey.h" -#include - -namespace TW::Tezos { - -using TW::Tezos::Proto::Operation; - -class OperationList { - public: - std::string branch; - std::vector operation_list; - OperationList(const std::string& string); - void addOperation(const Operation& transaction); - /// Returns a data representation of the operations. - Data forge(const PrivateKey& privateKey) const; - Data forge() const; - Data forgeBranch() const; -}; - -} // namespace TW::Tezos diff --git a/src/Tezos/Signer.cpp b/src/Tezos/Signer.cpp deleted file mode 100644 index 42ec445cdd0..00000000000 --- a/src/Tezos/Signer.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Signer.h" -#include "OperationList.h" -#include "../HexCoding.h" - -#include -#include - -#include - -using namespace TW; - -namespace TW::Tezos { - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - auto signer = Signer(); - PrivateKey key = PrivateKey(Data(input.private_key().begin(), input.private_key().end()), TWCurveED25519); - Data encoded; - if (input.encoded_operations().empty()) { - auto operationList = Tezos::OperationList(input.operation_list().branch()); - for (Proto::Operation operation : input.operation_list().operations()) { - operationList.addOperation(operation); - } - encoded = signer.signOperationList(key, operationList); - } else { - encoded = signer.signData(key, TW::data(input.encoded_operations())); - } - - auto output = Proto::SigningOutput(); - output.set_encoded(encoded.data(), encoded.size()); - return output; -} - -std::string Signer::signJSON(const std::string& json, const Data& key) { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - auto output = Signer::sign(input); - return hex(output.encoded()); -} - -Data Signer::signOperationList(const PrivateKey& privateKey, const OperationList& operationList) { - auto forged = operationList.forge(privateKey); - return signData(privateKey, forged); -} - -Data Signer::signData(const PrivateKey& privateKey, const Data& data) { - Data watermarkedData = Data(); - watermarkedData.push_back(0x03); - append(watermarkedData, data); - - Data hash = Hash::blake2b(watermarkedData, 32); - Data signature = privateKey.sign(hash); - - Data signedData = Data(); - append(signedData, data); - append(signedData, signature); - return signedData; -} - -Data Signer::buildUnsignedTx(const OperationList& operationList) { - Data txData = operationList.forge(); - return txData; -} - -Data Signer::buildSignedTx(const OperationList& operationList, Data signature) { - Data signedData = Data(); - - Data txData = operationList.forge(); - - append(signedData, txData); - append(signedData, signature); - - return signedData; -} - -} // namespace TW::Tezos diff --git a/src/Tezos/Signer.h b/src/Tezos/Signer.h deleted file mode 100644 index 1ec492b7625..00000000000 --- a/src/Tezos/Signer.h +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "OperationList.h" -#include "Data.h" -#include "../PrivateKey.h" -#include "../proto/Tezos.pb.h" - -#include - -namespace TW::Tezos { - -/// Helper class that performs Tezos transaction signing. -class Signer { - public: - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; - /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key); - - public: - /// Signs the given transaction. - Data signOperationList(const PrivateKey& privateKey, const OperationList& operationList); - Data buildUnsignedTx(const OperationList& operationList); - Data buildSignedTx(const OperationList& operationList, Data signature); - Data signData(const PrivateKey& privateKey, const Data& data); -}; - -} // namespace TW::Tezos diff --git a/tests/chains/Tezos/AddressTests.cpp b/tests/chains/Tezos/AddressTests.cpp deleted file mode 100644 index 0fca1e431cd..00000000000 --- a/tests/chains/Tezos/AddressTests.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "HDWallet.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Tezos/Address.h" -#include "Tezos/Forging.h" - -#include - -#include -#include -#include - -using namespace TW; - -namespace TW::Tezos::tests { - -TEST(TezosAddress, forge_tz1) { - auto input = Address("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don"); - auto expected = "0000cfa4aae60f5d9389752d41e320da224d43287fe2"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, forge_tz2) { - auto input = Address("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo"); - auto expected = "0001be99dd914e38388ec80432818b517759e3524f16"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, forge_tz3) { - auto input = Address("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9"); - auto expected = "0002358cbffa97149631cfb999fa47f0035fb1ea8636"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, forge_kt1) { - auto input = Address("KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o"); - auto expected = "01fe810959c3d6127a41cbd471e7cb4e91a61b780b00"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, isInvalid) { - std::array invalidAddresses{ - "NmH7tmeJUmHcncBDvpr7aJNEBk7rp5zYsB1qt", // Invalid prefix, valid checksum - "tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3AAAA", // Valid prefix, invalid checksum - "1tzeZwq8b5cvE2bPKokatLkVMzkxz24zAAAAA" // Invalid prefix, invalid checksum - }; - - for (auto& address : invalidAddresses) { - ASSERT_FALSE(Address::isValid(address)); - } -} - -TEST(TezosAddress, isValid) { - std::array validAddresses { - "tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt", - "tz2PdGc7U5tiyqPgTSgqCDct94qd6ovQwP6u", - "tz3VEZ4k6a4Wx42iyev6i2aVAptTRLEAivNN", - "KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o", - }; - - for (auto& address : validAddresses) { - ASSERT_TRUE(Address::isValid(address)); - } -} - -TEST(TezosAddress, string) { - auto addressString = "tz1d1qQL3mYVuiH4JPFvuikEpFwaDm85oabM"; - auto address = Address(addressString); - ASSERT_EQ(address.string(), addressString); -} - -TEST(TezosAddress, deriveOriginatedAddress) { - auto operationHash = "oo7VeTEPjEusPKnsHtKcGYbYa7i4RWpcEhUVo3Suugbbs6K62Ro"; - auto operationIndex = 0; - auto expected = "KT1WrtjtAYQSrUVvSNJPTZTebiUWoopQL5hw"; - - ASSERT_EQ(Address::deriveOriginatedAddress(operationHash, operationIndex), expected); -} - -TEST(TezosAddress, PublicKeyInit) { - Data bytes = parse_hex("01fe157cc8011727936c592f856c9071d39cf4acdadfa6d76435e4619c9dc56f63"); - const auto publicKey = PublicKey(bytes, TWPublicKeyTypeED25519); - auto address = Address(publicKey); - - auto expected = "tz1cG2jx3W4bZFeVGBjsTxUAG8tdpTXtE8PT"; - ASSERT_EQ(address.string(), expected); -} - -} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/ForgingTests.cpp b/tests/chains/Tezos/ForgingTests.cpp deleted file mode 100644 index 95ffe14dd78..00000000000 --- a/tests/chains/Tezos/ForgingTests.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "HDWallet.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "Tezos/Address.h" -#include "Tezos/BinaryCoding.h" -#include "Tezos/Forging.h" -#include "proto/Tezos.pb.h" -#include -#include -#include -#include - -namespace TW::Tezos::tests { - -TEST(Forging, ForgeBoolTrue) { - auto expected = "ff"; - - auto output = forgeBool(true); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeBoolFalse) { - auto expected = "00"; - - auto output = forgeBool(false); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithZero) { - auto expected = "00"; - - auto output = forgeZarith(0); - - ASSERT_EQ(hex(output), hex(parse_hex(expected))); -} - -TEST(Forging, ForgeZarithTen) { - auto expected = "0a"; - - auto output = forgeZarith(10); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithTwenty) { - auto expected = "14"; - - auto output = forgeZarith(20); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithOneHundredFifty) { - auto expected = "9601"; - - auto output = forgeZarith(150); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithLarge) { - auto expected = "bbd08001"; - - auto output = forgeZarith(2107451); - - ASSERT_EQ(hex(output), expected); -} - -TEST(Forging, forge_tz1) { - auto expected = "00cfa4aae60f5d9389752d41e320da224d43287fe2"; - - auto output = forgePublicKeyHash("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don"); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, forge_tz2) { - auto expected = "01be99dd914e38388ec80432818b517759e3524f16"; - - auto output = forgePublicKeyHash("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo"); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, forge_tz3) { - auto expected = "02358cbffa97149631cfb999fa47f0035fb1ea8636"; - - auto output = forgePublicKeyHash("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9"); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeED25519PublicKey) { - auto expected = "00311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"; - - auto privateKey = PrivateKey(parse_hex("c6377a4cc490dc913fc3f0d9cf67d293a32df4547c46cb7e9e33c3b7b97c64d8")); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - auto output = forgePublicKey(publicKey); - - ASSERT_EQ(hex(output), expected); -} - -TEST(Forging, ForgeInt32) { - auto expected = "01"; - ASSERT_EQ(hex(forgeInt32(1, 1)), expected); -} - -TEST(Forging, ForgeString) { - auto expected = "087472616e73666572"; - ASSERT_EQ(hex(forgeString("transfer", 1)), expected); -} - -TEST(Forging, ForgeEntrypoint) { - auto expected = "ff087472616e73666572"; - ASSERT_EQ(hex(forgeEntrypoint("transfer")), expected); -} - -TEST(Forging, ForgeMichelsonFA12) { - Tezos::Proto::FA12Parameters data; - data.set_entrypoint("transfer"); - data.set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - data.set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - data.set_value("123"); - auto v = FA12ParameterToMichelson(data); - ASSERT_EQ(hex(forgeMichelson(v)), "07070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb01"); -} -TEST(Forging, ForgeSECP256k1PublicKey) { - auto expected = "0102b4ac9056d20c52ac11b0d7e83715dd3eac851cfc9cb64b8546d9ea0d4bb3bdfe"; - - auto privateKey = PrivateKey(parse_hex("3a8e0a528f62f4ca2c77744c8a571def2845079b50105a9f7ef6b1b823def67a")); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - auto output = forgePublicKey(publicKey); - - ASSERT_EQ(hex(output), expected); -} - -TEST(TezosTransaction, forgeTransaction) { - auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); - transactionOperationData->set_amount(1); - transactionOperationData->set_destination("tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt"); - - auto transactionOperation = TW::Tezos::Proto::Operation(); - transactionOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - transactionOperation.set_fee(1272); - transactionOperation.set_counter(30738); - transactionOperation.set_gas_limit(10100); - transactionOperation.set_storage_limit(257); - transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - auto expected = "6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; - auto serialized = forgeOperation(transactionOperation); - - ASSERT_EQ(hex(serialized), expected); -} - -TEST(TezosTransaction, forgeTransactionFA12) { - auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); - transactionOperationData->set_amount(0); - transactionOperationData->set_destination("KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5"); - transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_entrypoint("transfer"); - transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_value("123"); - - auto transactionOperation = TW::Tezos::Proto::Operation(); - transactionOperation.set_source("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - transactionOperation.set_fee(100000); - transactionOperation.set_counter(2993172); - transactionOperation.set_gas_limit(100000); - transactionOperation.set_storage_limit(0); - transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - auto expected = "6c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb01"; - auto serialized = forgeOperation(transactionOperation); - - ASSERT_EQ(hex(serialized), expected); -} - -TEST(TezosTransaction, forgeTransactionFA2) { - auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); - transactionOperationData->set_amount(0); - transactionOperationData->set_destination("KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj"); - auto& fa2 = *transactionOperationData->mutable_parameters()->mutable_fa2_parameters(); - fa2.set_entrypoint("transfer"); - auto& txObject = *fa2.add_txs_object(); - txObject.set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - auto& tx = *txObject.add_txs(); - tx.set_amount("10"); - tx.set_token_id("0"); - tx.set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - - auto transactionOperation = TW::Tezos::Proto::Operation(); - transactionOperation.set_source("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); - transactionOperation.set_fee(100000); - transactionOperation.set_counter(2993173); - transactionOperation.set_gas_limit(100000); - transactionOperation.set_storage_limit(0); - transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - auto serialized = forgeOperation(transactionOperation); - auto expected = "6c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a"; - ASSERT_EQ(hex(serialized), expected); -} - -TEST(TezosTransaction, forgeReveal) { - PublicKey publicKey = parsePublicKey("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A"); - - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); - revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - - auto revealOperation = TW::Tezos::Proto::Operation(); - revealOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - revealOperation.set_fee(1272); - revealOperation.set_counter(30738); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(257); - revealOperation.set_kind(TW::Tezos::Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - auto expected = "6b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; - auto serialized = forgeOperation(revealOperation); - - ASSERT_EQ(hex(serialized), expected); -} - -TEST(TezosTransaction, forgeDelegate) { - auto delegateOperationData = new TW::Tezos::Proto::DelegationOperationData(); - delegateOperationData->set_delegate("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - - auto delegateOperation = TW::Tezos::Proto::Operation(); - delegateOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - delegateOperation.set_fee(1272); - delegateOperation.set_counter(30738); - delegateOperation.set_gas_limit(10100); - delegateOperation.set_storage_limit(257); - delegateOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); - delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); - - auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e8102ff003e47f837f0467b4acde406ed5842f35e2414b1a8"; - auto serialized = forgeOperation(delegateOperation); - - ASSERT_EQ(hex(serialized), expected); -} - -TEST(TezosTransaction, forgeUndelegate) { - auto delegateOperationData = new TW::Tezos::Proto::DelegationOperationData(); - delegateOperationData->set_delegate(""); - - auto delegateOperation = TW::Tezos::Proto::Operation(); - delegateOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - delegateOperation.set_fee(1272); - delegateOperation.set_counter(30738); - delegateOperation.set_gas_limit(10100); - delegateOperation.set_storage_limit(257); - delegateOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); - delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); - - auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200"; - auto serialized = forgeOperation(delegateOperation); - - ASSERT_EQ(hex(serialized), expected); -} - -} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/OperationListTests.cpp b/tests/chains/Tezos/OperationListTests.cpp deleted file mode 100644 index 4218e01c6e1..00000000000 --- a/tests/chains/Tezos/OperationListTests.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Tezos/Address.h" -#include "Tezos/BinaryCoding.h" -#include "Tezos/OperationList.h" -#include "proto/Tezos.pb.h" -#include "HexCoding.h" - -#include - -namespace TW::Tezos::tests { - -TEST(TezosOperationList, ForgeBranch) { - auto input = TW::Tezos::OperationList("BMNY6Jkas7BzKb7wDLCFoQ4YxfYoieU7Xmo1ED3Y9Lo3ZvVGdgW"); - auto expected = "da8eb4f57f98a647588b47d29483d1edfdbec1428c11609cee0da6e0f27cfc38"; - - ASSERT_EQ(input.forgeBranch(), parse_hex(expected)); -} - -TEST(TezosOperationList, ForgeOperationList_TransactionOnly) { - auto branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; - auto op_list = TW::Tezos::OperationList(branch); - auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - - auto transactionOperationData = new Proto::TransactionOperationData(); - transactionOperationData->set_amount(1); - transactionOperationData->set_destination("tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt"); - - auto transactionOperation = Proto::Operation(); - transactionOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - transactionOperation.set_fee(1272); - transactionOperation.set_counter(30738); - transactionOperation.set_gas_limit(10100); - transactionOperation.set_storage_limit(257); - transactionOperation.set_kind(Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - op_list.addOperation(transactionOperation); - - auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; - auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged), expected); -} - -TEST(TezosOperationList, ForgeOperationList_RevealOnly) { - auto branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; - auto op_list = TW::Tezos::OperationList(branch); - auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - PublicKey publicKey = parsePublicKey("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A"); - - auto revealOperationData = new Proto::RevealOperationData(); - revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - - auto revealOperation = Proto::Operation(); - revealOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - revealOperation.set_fee(1272); - revealOperation.set_counter(30738); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(257); - revealOperation.set_kind(TW::Tezos::Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - op_list.addOperation(revealOperation); - auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; - auto forged = op_list.forge(key); - - ASSERT_EQ(hex(forged), expected); -} - -TEST(TezosOperationList, ForgeOperationList_Delegation_ClearDelegate) { - auto branch = "BLGJfQDFEYZBRLj5GSHskj8NPaRYhk7Kx5WAfdcDucD3q98WdeW"; - auto op_list = OperationList(branch); - auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto delegationOperationData = new Proto::DelegationOperationData(); - delegationOperationData->set_delegate("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - - auto delegationOperation = Proto::Operation(); - delegationOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - delegationOperation.set_fee(1257); - delegationOperation.set_counter(67); - delegationOperation.set_gas_limit(10000); - delegationOperation.set_storage_limit(0); - delegationOperation.set_kind(Proto::Operation::DELEGATION); - delegationOperation.set_allocated_delegation_operation_data(delegationOperationData); - - op_list.addOperation(delegationOperation); - - auto expected = "48b63d801fa824013a195f7885ba522503c59e0580f7663e15c52f03ccc935e66e003e47f837f0467b4acde406ed5842f35e2414b1a8e90943904e00ff00e42504da69a7c8d5baeaaeebe157a02db6b22ed8"; - ASSERT_EQ(hex(op_list.forge(key)), expected); -} - -TEST(TezosOperationList, ForgeOperationList_Delegation_AddDelegate) { - auto branch = "BLa4GrVQTxUgQWbHv6cF7RXWSGzHGPbgecpQ795R3cLzw4cGfpD"; - auto op_list = OperationList(branch); - auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto delegationOperationData = new Proto::DelegationOperationData(); - delegationOperationData->set_delegate("tz1dYUCcrorfCoaQCtZaxi1ynGrP3prTZcxS"); - - auto delegationOperation = Proto::Operation(); - delegationOperation.set_source("KT1D5jmrBD7bDa3jCpgzo32FMYmRDdK2ihka"); - delegationOperation.set_fee(1257); - delegationOperation.set_counter(68); - delegationOperation.set_gas_limit(10000); - delegationOperation.set_storage_limit(0); - delegationOperation.set_kind(Proto::Operation::DELEGATION); - delegationOperation.set_allocated_delegation_operation_data(delegationOperationData); - - op_list.addOperation(delegationOperation); - auto expected = "7105102c032807994dd9b5edf219261896a559876ca16cbf9d31dbe3612b89f26e00315b1206ec00b1b1e64cc3b8b93059f58fa2fc39e90944904e00ff00c4650fd609f88c67356e5fe01e37cd3ff654b18c"; - auto forged = op_list.forge(key); - ASSERT_EQ(hex(forged), expected); -} - -TEST(TezosOperationList, ForgeOperationList_TransactionAndReveal) { - auto branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; - auto op_list = OperationList(branch); - auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto publicKey = parsePublicKey("edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp"); - - auto revealOperationData = new Proto::RevealOperationData(); - revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - - auto revealOperation = Proto::Operation(); - revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - revealOperation.set_fee(1272); - revealOperation.set_counter(30738); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(257); - revealOperation.set_kind(Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - auto transactionOperationData = new Proto::TransactionOperationData(); - transactionOperationData->set_amount(1); - transactionOperationData->set_destination("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - - auto transactionOperation = Proto::Operation(); - transactionOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - transactionOperation.set_fee(1272); - transactionOperation.set_counter(30739); - transactionOperation.set_gas_limit(10100); - transactionOperation.set_storage_limit(257); - transactionOperation.set_kind(Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - op_list.addOperation(revealOperation); - op_list.addOperation(transactionOperation); - - auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b003e47f837f0467b4acde406ed5842f35e2414b1a8f80992f001f44e810200603247bbf52501498293686da89ad8b2aca85f83b90903d4521dd2aba66054eb6c003e47f837f0467b4acde406ed5842f35e2414b1a8f80993f001f44e8102010000e42504da69a7c8d5baeaaeebe157a02db6b22ed800"; - auto forged = op_list.forge(key); - - ASSERT_EQ(hex(forged), expected); -} - -TEST(TezosOperationList, ForgeOperationList_RevealWithoutPublicKey) { - auto branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; - auto op_list = OperationList(branch); - auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - - auto revealOperationData = new Proto::RevealOperationData(); - - auto revealOperation = Proto::Operation(); - revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - revealOperation.set_fee(1272); - revealOperation.set_counter(30738); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(257); - revealOperation.set_kind(Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - op_list.addOperation(revealOperation); - - auto expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b003e47f837f0467b4acde406ed5842f35e2414b1a8f80992f001f44e810200603247bbf52501498293686da89ad8b2aca85f83b90903d4521dd2aba66054eb"; - auto forged = op_list.forge(key); - - ASSERT_EQ(hex(forged), expected); -} - -} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/PublicKeyTests.cpp b/tests/chains/Tezos/PublicKeyTests.cpp deleted file mode 100644 index c91383fcb86..00000000000 --- a/tests/chains/Tezos/PublicKeyTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Tezos/BinaryCoding.h" -#include "Tezos/Forging.h" -#include "PublicKey.h" -#include "Data.h" -#include "HexCoding.h" - -#include - -namespace TW::Tezos::tests { - -TEST(TezosPublicKey, forge) { - auto input = parsePublicKey("edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"); - auto expected = "00451bde832454ba73e6e0de313fcf5d1565ec51080edc73bb19287b8e0ab2122b"; - auto serialized = forgePublicKey(input); - ASSERT_EQ(hex(serialized), expected); -} - -TEST(TezosPublicKey, parse) { - auto input = "edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"; - auto bytes = Data({1, 69, 27, 222, 131, 36, 84, 186, 115, 230, 224, 222, 49, 63, 207, 93, 21, 101, 236, 81, 8, 14, 220, 115, 187, 25, 40, 123, 142, 10, 178, 18, 43}); - auto output = parsePublicKey(input); - auto expected = PublicKey(bytes, TWPublicKeyTypeED25519); - ASSERT_EQ(output, expected); -} - -} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/SignerTests.cpp b/tests/chains/Tezos/SignerTests.cpp deleted file mode 100644 index 56f046c2f8a..00000000000 --- a/tests/chains/Tezos/SignerTests.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Base58.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Tezos/BinaryCoding.h" -#include "Tezos/OperationList.h" -#include "Tezos/Signer.h" - -#include - -using namespace TW; - -namespace TW::Tezos::tests { - -TEST(TezosSigner, SignString) { - Data bytesToSign = parse_hex("ffaa"); - Data expectedSignature = parse_hex("eaab7f4066217b072b79609a9f76cdfadd93f8dde41763887e131c02324f18c8e41b1009e334baf87f9d2e917bf4c0e73165622e5522409a0c5817234a48cc02"); - Data expected = Data(); - append(expected, bytesToSign); - append(expected, expectedSignature); - - auto key = PrivateKey(parse_hex("0x2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f"), TWCurveED25519); - auto signedBytes = Signer().signData(key, bytesToSign); - - ASSERT_EQ(signedBytes, expected); -} - -TEST(TezosSigner, SignOperationList) { - auto branch = "BLDnkhhVgwdBAtmDNQc5HtEMsrxq8L3t7NQbjUbbdTdw5Ug1Mpe"; - auto op_list = Tezos::OperationList(branch); - - auto transactionOperationData = new Proto::TransactionOperationData(); - transactionOperationData->set_amount(11100000); - transactionOperationData->set_destination("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - - auto transactionOperation = TW::Tezos::Proto::Operation(); - transactionOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - transactionOperation.set_fee(1283); - transactionOperation.set_counter(1878); - transactionOperation.set_gas_limit(10307); - transactionOperation.set_storage_limit(0); - transactionOperation.set_kind(Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - op_list.addOperation(transactionOperation); - - PublicKey publicKey = parsePublicKey("edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp"); - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); - revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - - auto revealOperation = Proto::Operation(); - revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - revealOperation.set_fee(1268); - revealOperation.set_counter(1876); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(0); - revealOperation.set_kind(Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - op_list.addOperation(revealOperation); - - auto delegateOperationData = new Tezos::Proto::DelegationOperationData(); - delegateOperationData->set_delegate("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - - auto delegateOperation = Proto::Operation(); - delegateOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - delegateOperation.set_fee(1257); - delegateOperation.set_counter(1879); - delegateOperation.set_gas_limit(10100); - delegateOperation.set_storage_limit(0); - delegateOperation.set_kind(Proto::Operation::DELEGATION); - delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); - - op_list.addOperation(delegateOperation); - - auto decodedPrivateKey = Base58::decodeCheck("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto key = PrivateKey(Data(decodedPrivateKey.begin() + 4, decodedPrivateKey.end()), TWCurveED25519); - - std::string expectedForgedBytesToSign = hex(op_list.forge(key)); - std::string expectedSignature = "871693145f2dc72861ff6816e7ac3ce93c57611ac09a4c657a5a35270fa57153334c14cd8cae94ee228b6ef52f0e3f10948721e666318bc54b6c455404b11e03"; - std::string expectedSignedBytes = expectedForgedBytesToSign + expectedSignature; - - auto signedBytes = Signer().signOperationList(key, op_list); - auto signedBytesHex = hex(signedBytes); - - ASSERT_EQ(hex(signedBytes), expectedSignedBytes); -} - -} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/TWAnySignerTests.cpp b/tests/chains/Tezos/TWAnySignerTests.cpp index 30943e749c8..05569959df7 100644 --- a/tests/chains/Tezos/TWAnySignerTests.cpp +++ b/tests/chains/Tezos/TWAnySignerTests.cpp @@ -3,7 +3,6 @@ // Copyright © 2017 Trust Wallet. #include "HexCoding.h" -#include "Tezos/BinaryCoding.h" #include "proto/Tezos.pb.h" #include "TestUtilities.h" #include