Skip to content

Commit

Permalink
Support calculating the TX hash of a transaction (#4008)
Browse files Browse the repository at this point in the history
* Support calculating the TX hash of a transaction

* Implement the calc_tx_hash for TON/Aptos/Sui

* Adjust the code according to the comments

* Refactor the calcTxHash for Solana

* Format rust code using `cargo fmt`

* Change UtxoTransactionUtil to BitcoinTransactionUtil

* Add some comments

* Adjust the code according to the comments

* Add more test cases

* Fix issue found by `cargo clippy`
  • Loading branch information
10gic authored Sep 9, 2024
1 parent 136506f commit d88a263
Show file tree
Hide file tree
Showing 58 changed files with 658 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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;
Expand All @@ -35,6 +36,7 @@ impl CoinEntry for {BLOCKCHAIN}Entry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
24 changes: 24 additions & 0 deletions include/TrustWalletCore/TWTransactionUtil.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#pragma once

#include "TWBase.h"
#include "TWCoinType.h"
#include "TWData.h"

TW_EXTERN_C_BEGIN

TW_EXPORT_STRUCT
struct TWTransactionUtil;

/// Calculate the TX hash of a transaction.
///
/// \param coin coin type.
/// \param encodedTx encoded transaction data.
/// \return The TX hash of a transaction, If the input is invalid or the chain is unsupported, null is returned.
TW_EXPORT_STATIC_METHOD
TWString* _Nullable TWTransactionUtilCalcTxHash(enum TWCoinType coinType, TWString* _Nonnull encodedTx);

TW_EXTERN_C_END
7 changes: 7 additions & 0 deletions rust/chains/tw_aptos/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use crate::address::Address;
use crate::compiler::Compiler;
use crate::modules::transaction_util::AptosTransactionUtil;
use crate::signer::Signer;
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
Expand Down Expand Up @@ -35,6 +36,7 @@ impl CoinEntry for AptosEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = AptosTransactionUtil;

#[inline]
fn parse_address(
Expand Down Expand Up @@ -102,4 +104,9 @@ impl CoinEntry for AptosEntry {
fn message_signer(&self) -> Option<Self::MessageSigner> {
None
}

#[inline]
fn transaction_util(&self) -> Option<Self::TransactionUtil> {
Some(AptosTransactionUtil)
}
}
1 change: 1 addition & 0 deletions rust/chains/tw_aptos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod nft;

pub mod compiler;
pub mod liquid_staking;
pub mod modules;
pub mod signer;
pub mod transaction;
pub mod transaction_builder;
Expand Down
5 changes: 5 additions & 0 deletions rust/chains/tw_aptos/src/modules/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

pub mod transaction_util;
35 changes: 35 additions & 0 deletions rust/chains/tw_aptos/src/modules/transaction_util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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::modules::transaction_util::TransactionUtil;
use tw_encoding::hex;
use tw_hash::sha3::sha3_256;

pub struct AptosTransactionUtil;

impl TransactionUtil for AptosTransactionUtil {
fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult<String> {
Self::calc_tx_hash_impl(coin, encoded_tx)
}
}

impl AptosTransactionUtil {
fn calc_tx_hash_impl(_coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult<String> {
let txn_bytes = hex::decode(encoded_tx).map_err(|_| SigningErrorType::Error_input_parse)?;

// See: https://github.com/aptos-labs/aptos-ts-sdk/blob/f54cac824a41e41dea09c7a6916858a8604dc901/src/api/transaction.ts#L118
let prefix = sha3_256("APTOS::Transaction".as_bytes());

let mut hash_message = Vec::new();
hash_message.extend_from_slice(&prefix);
// 0 is the index of the enum `Transaction`, see: https://github.com/aptos-labs/aptos-core/blob/6a130c1cca274a5cfdb4a65b441cd5fe61b6c15b/types/src/transaction/mod.rs#L1939
hash_message.push(0);
hash_message.extend_from_slice(&txn_bytes);

let tx_hash = sha3_256(&hash_message);
Ok(hex::encode(tx_hash, true))
}
}
2 changes: 2 additions & 0 deletions rust/chains/tw_binance/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ 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_keypair::tw::PublicKey;
use tw_proto::Binance::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
Expand All @@ -35,6 +36,7 @@ impl CoinEntry for BinanceEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = BinanceWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
7 changes: 7 additions & 0 deletions rust/chains/tw_cosmos/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use tw_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_cosmos_sdk::context::StandardCosmosContext;
use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler;
use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner;
use tw_cosmos_sdk::modules::transaction_util::CosmosTransactionUtil;
use tw_keypair::tw;
use tw_proto::Cosmos::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
Expand All @@ -35,6 +36,7 @@ impl CoinEntry for CosmosEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = CosmosTransactionUtil<StandardCosmosContext>;

#[inline]
fn parse_address(
Expand Down Expand Up @@ -95,4 +97,9 @@ impl CoinEntry for CosmosEntry {
public_keys,
)
}

#[inline]
fn transaction_util(&self) -> Option<Self::TransactionUtil> {
Some(CosmosTransactionUtil::<StandardCosmosContext>::default())
}
}
7 changes: 7 additions & 0 deletions rust/chains/tw_ethereum/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use tw_evm::evm_entry::EvmEntry;
use tw_evm::modules::compiler::Compiler;
use tw_evm::modules::message_signer::EthMessageSigner;
use tw_evm::modules::signer::Signer;
use tw_evm::modules::transaction_util::EvmTransactionUtil;
use tw_keypair::tw::PublicKey;
use tw_proto::Ethereum::Proto;
use tw_proto::TxCompiler::Proto as CompilerProto;
Expand All @@ -37,6 +38,7 @@ impl CoinEntry for EthereumEntry {
type MessageSigner = EthMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = EvmTransactionUtil;

#[inline]
fn parse_address(
Expand Down Expand Up @@ -100,6 +102,11 @@ impl CoinEntry for EthereumEntry {
fn message_signer(&self) -> Option<Self::MessageSigner> {
Some(EthMessageSigner)
}

#[inline]
fn transaction_util(&self) -> Option<Self::TransactionUtil> {
Some(EvmTransactionUtil)
}
}

impl EvmEntry for EthereumEntry {
Expand Down
2 changes: 2 additions & 0 deletions rust/chains/tw_greenfield/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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;
Expand All @@ -35,6 +36,7 @@ impl CoinEntry for GreenfieldEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
3 changes: 2 additions & 1 deletion rust/chains/tw_internet_computer/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use std::str::FromStr;

use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder;
use tw_coin_entry::modules::transaction_util::NoTransactionUtil;
use tw_coin_entry::{
coin_context::CoinContext,
coin_entry::CoinEntry,
Expand All @@ -16,7 +17,6 @@ use tw_coin_entry::{
prefix::NoPrefix,
signing_output_error,
};

use tw_proto::{
Common::Proto::SigningError as CommonError, InternetComputer::Proto,
TxCompiler::Proto as CompilerProto,
Expand All @@ -39,6 +39,7 @@ impl CoinEntry for InternetComputerEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
2 changes: 2 additions & 0 deletions rust/chains/tw_native_evmos/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler;
Expand All @@ -35,6 +36,7 @@ impl CoinEntry for NativeEvmosEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
2 changes: 2 additions & 0 deletions rust/chains/tw_native_injective/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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_cosmos_sdk::address::{Address, Bech32Prefix};
use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler;
Expand All @@ -35,6 +36,7 @@ impl CoinEntry for NativeInjectiveEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
2 changes: 2 additions & 0 deletions rust/chains/tw_ronin/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use tw_coin_entry::error::prelude::*;
use tw_coin_entry::modules::json_signer::NoJsonSigner;
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_evm::evm_entry::EvmEntry;
Expand All @@ -37,6 +38,7 @@ impl CoinEntry for RoninEntry {
type MessageSigner = EthMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = NoTransactionUtil;

#[inline]
fn parse_address(
Expand Down
7 changes: 7 additions & 0 deletions rust/chains/tw_solana/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::address::SolanaAddress;
use crate::compiler::SolanaCompiler;
use crate::modules::transaction_decoder::SolanaTransactionDecoder;
use crate::modules::transaction_util::SolanaTransactionUtil;
use crate::modules::wallet_connect::connector::SolanaWalletConnector;
use crate::signer::SolanaSigner;
use std::str::FromStr;
Expand Down Expand Up @@ -34,6 +35,7 @@ impl CoinEntry for SolanaEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = SolanaWalletConnector;
type TransactionDecoder = SolanaTransactionDecoder;
type TransactionUtil = SolanaTransactionUtil;

#[inline]
fn parse_address(
Expand Down Expand Up @@ -99,4 +101,9 @@ impl CoinEntry for SolanaEntry {
fn transaction_decoder(&self) -> Option<Self::TransactionDecoder> {
Some(SolanaTransactionDecoder)
}

#[inline]
fn transaction_util(&self) -> Option<Self::TransactionUtil> {
Some(SolanaTransactionUtil)
}
}
1 change: 1 addition & 0 deletions rust/chains/tw_solana/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod instruction_builder;
pub mod message_builder;
pub mod proto_builder;
pub mod transaction_decoder;
pub mod transaction_util;
pub mod tx_signer;
pub mod utils;
pub mod wallet_connect;
Expand Down
2 changes: 1 addition & 1 deletion rust/chains/tw_solana/src/modules/transaction_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl TransactionDecoder for SolanaTransactionDecoder {
}

impl SolanaTransactionDecoder {
fn decode_transaction_impl(
pub(crate) fn decode_transaction_impl(
_coin: &dyn CoinContext,
tx: &[u8],
) -> SigningResult<Proto::DecodingTransactionOutput<'static>> {
Expand Down
37 changes: 37 additions & 0 deletions rust/chains/tw_solana/src/modules/transaction_util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use crate::modules::transaction_decoder::SolanaTransactionDecoder;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::modules::transaction_util::TransactionUtil;
use tw_encoding::base64;
use tw_encoding::base64::STANDARD;

pub struct SolanaTransactionUtil;

impl TransactionUtil for SolanaTransactionUtil {
fn calc_tx_hash(&self, coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult<String> {
Self::calc_tx_hash_impl(coin, encoded_tx)
}
}

impl SolanaTransactionUtil {
fn calc_tx_hash_impl(coin: &dyn CoinContext, encoded_tx: &str) -> SigningResult<String> {
// Solana signed transactions can be encoded in either base64 or base58. For more information, see: https://solana.com/docs/rpc/http/sendtransaction
// Currently, this function only accepts base64 encoding.
let tx_bytes = base64::decode(encoded_tx, STANDARD)?;
let decoded_tx_output = SolanaTransactionDecoder::decode_transaction_impl(coin, &tx_bytes)?;

let first_sig = decoded_tx_output
.transaction
.as_ref()
.and_then(|tx| tx.signatures.first())
.or_tw_err(SigningErrorType::Error_input_parse)
.context("There is no transaction signatures. Looks like it hasn't been signed yet")?;

// Tx hash is the first signature
Ok(first_sig.signature.to_string())
}
}
7 changes: 7 additions & 0 deletions rust/chains/tw_sui/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use crate::address::SuiAddress;
use crate::compiler::SuiCompiler;
use crate::modules::transaction_util::SuiTransactionUtil;
use crate::signer::SuiSigner;
use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
Expand Down Expand Up @@ -35,6 +36,7 @@ impl CoinEntry for SuiEntry {
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
type TransactionUtil = SuiTransactionUtil;

#[inline]
fn parse_address(
Expand Down Expand Up @@ -93,4 +95,9 @@ impl CoinEntry for SuiEntry {
) -> Self::SigningOutput {
SuiCompiler::compile(coin, input, signatures, public_keys)
}

#[inline]
fn transaction_util(&self) -> Option<Self::TransactionUtil> {
Some(SuiTransactionUtil)
}
}
1 change: 1 addition & 0 deletions rust/chains/tw_sui/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
//
// Copyright © 2017 Trust Wallet.

pub mod transaction_util;
pub mod tx_builder;
pub mod tx_signer;
Loading

0 comments on commit d88a263

Please sign in to comment.