Skip to content

Commit

Permalink
Merge branch 'master' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan authored Oct 14, 2024
2 parents 0b16771 + cd6869b commit b1f4543
Show file tree
Hide file tree
Showing 72 changed files with 1,659 additions and 370 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.trustwallet.core.app.blockchains.bitcoincash

import com.trustwallet.core.app.utils.Numeric
import com.trustwallet.core.app.utils.toHexBytesInByteString
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.java.AnySigner
import wallet.core.jni.BitcoinSigHashType
import wallet.core.jni.CoinType
import wallet.core.jni.proto.Bitcoin
import wallet.core.jni.proto.BitcoinV2
import wallet.core.jni.proto.Common.SigningError

class TestBitcoinCashSigning {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun testSignV2P2PKH() {
val privateKey = "7fdafb9db5bc501f2096e7d13d331dc7a75d9594af3d251313ba8b6200f4e384".toHexBytesInByteString()

val utxo1 = BitcoinV2.Input.newBuilder().apply {
outPoint = BitcoinV2.OutPoint.newBuilder().apply {
hash = "e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05".toHexBytesInByteString()
vout = 2
}.build()
value = 5151
sighashType = BitcoinSigHashType.ALL.value() + BitcoinSigHashType.FORK.value()
receiverAddress = "bitcoincash:qzhlrcrcne07x94h99thved2pgzdtv8ccujjy73xya"
}

val out1 = BitcoinV2.Output.newBuilder().apply {
value = 600
// Legacy address
toAddress = "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx"
}
val changeOutputExplicit = BitcoinV2.Output.newBuilder().apply {
value = 4325
// Legacy address
toAddress = "qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06"
}

val builder = BitcoinV2.TransactionBuilder.newBuilder()
.setVersion(BitcoinV2.TransactionVersion.V1)
.addInputs(utxo1)
.addOutputs(out1)
.addOutputs(changeOutputExplicit)
.setInputSelector(BitcoinV2.InputSelector.UseAll)
.setFixedDustThreshold(546)
.build()

val input = BitcoinV2.SigningInput.newBuilder()
.addPrivateKeys(privateKey)
.setBuilder(builder)
.setChainInfo(BitcoinV2.ChainInfo.newBuilder().apply {
p2PkhPrefix = 0
p2ShPrefix = 5
hrp = "bitcoincash"
})
.build()

val legacyInput = Bitcoin.SigningInput.newBuilder()
.setSigningV2(input)
// `CoinType` must be set to be handled correctly.
.setCoinType(CoinType.BITCOINCASH.value())
.build()

val outputLegacy = AnySigner.sign(legacyInput, CoinType.BITCOINCASH, Bitcoin.SigningOutput.parser())
assertEquals(outputLegacy.error, SigningError.OK)
assert(outputLegacy.hasSigningResultV2())

val output = outputLegacy.signingResultV2
assertEquals(output.error, SigningError.OK)
assertEquals(
Numeric.toHexString(output.encoded.toByteArray()),
"0x0100000001e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05020000006b483045022100b70d158b43cbcded60e6977e93f9a84966bc0cec6f2dfd1463d1223a90563f0d02207548d081069de570a494d0967ba388ff02641d91cadb060587ead95a98d4e3534121038eab72ec78e639d02758e7860cdec018b49498c307791f785aa3019622f4ea5bffffffff0258020000000000001976a914769bdff96a02f9135a1d19b749db6a78fe07dc9088ace5100000000000001976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac00000000"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ impl CoinEntry for {BLOCKCHAIN}Entry {
}

#[inline]
fn parse_address_unchecked(
&self,
_coin: &dyn CoinContext,
address: &str,
) -> AddressResult<Self::Address> {
fn parse_address_unchecked(&self, address: &str) -> AddressResult<Self::Address> {
{BLOCKCHAIN}Address::from_str(address)
}

Expand Down
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWBlockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum TWBlockchain {
TWBlockchainInternetComputer = 52,
TWBlockchainNativeEvmos = 53, // Cosmos
TWBlockchainNativeInjective = 54, // Cosmos
TWBlockchainBitcoinCash = 55,
};

TW_EXTERN_C_END
4 changes: 2 additions & 2 deletions registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -1453,7 +1453,7 @@
"coinId": 145,
"symbol": "BCH",
"decimals": 8,
"blockchain": "Bitcoin",
"blockchain": "BitcoinCash",
"derivation": [
{
"path": "m/44'/145'/0'/0/0",
Expand Down Expand Up @@ -3667,7 +3667,7 @@
"coinId": 899,
"symbol": "XEC",
"decimals": 2,
"blockchain": "Bitcoin",
"blockchain": "BitcoinCash",
"derivation": [
{
"path": "m/44'/899'/0'/0/0",
Expand Down
16 changes: 16 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ members = [
"chains/tw_aptos",
"chains/tw_binance",
"chains/tw_bitcoin",
"chains/tw_bitcoincash",
"chains/tw_cosmos",
"chains/tw_ethereum",
"chains/tw_greenfield",
Expand Down
6 changes: 1 addition & 5 deletions rust/chains/tw_aptos/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ impl CoinEntry for AptosEntry {
}

#[inline]
fn parse_address_unchecked(
&self,
_coin: &dyn CoinContext,
address: &str,
) -> AddressResult<Self::Address> {
fn parse_address_unchecked(&self, address: &str) -> AddressResult<Self::Address> {
Address::from_str(address)
}

Expand Down
6 changes: 1 addition & 5 deletions rust/chains/tw_binance/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ impl CoinEntry for BinanceEntry {
}

#[inline]
fn parse_address_unchecked(
&self,
_coin: &dyn CoinContext,
address: &str,
) -> AddressResult<Self::Address> {
fn parse_address_unchecked(&self, address: &str) -> AddressResult<Self::Address> {
BinanceAddress::from_str(address)
}

Expand Down
28 changes: 28 additions & 0 deletions rust/chains/tw_bitcoin/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use tw_coin_entry::error::prelude::SigningResult;
use tw_utxo::address::standard_bitcoin::StandardBitcoinAddress;
use tw_utxo::context::{AddressPrefixes, UtxoContext};
use tw_utxo::script::Script;

#[derive(Default)]
pub struct StandardBitcoinContext;

impl UtxoContext for StandardBitcoinContext {
type Address = StandardBitcoinAddress;

fn addr_to_script_pubkey(
addr: &Self::Address,
prefixes: AddressPrefixes,
) -> SigningResult<Script> {
match addr {
StandardBitcoinAddress::Legacy(legacy) => {
legacy.to_script_pubkey(prefixes.p2pkh_prefix, prefixes.p2sh_prefix)
},
StandardBitcoinAddress::Segwit(segwit) => segwit.to_script_pubkey(),
StandardBitcoinAddress::Taproot(taproot) => taproot.to_script_pubkey(),
}
}
}
17 changes: 7 additions & 10 deletions rust/chains/tw_bitcoin/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::context::StandardBitcoinContext;
use crate::modules::compiler::BitcoinCompiler;
use crate::modules::planner::BitcoinPlanner;
use crate::modules::signer::BitcoinSigner;
Expand Down Expand Up @@ -26,7 +27,7 @@ impl CoinEntry for BitcoinEntry {

// Optional modules:
type JsonSigner = NoJsonSigner;
type PlanBuilder = BitcoinPlanner;
type PlanBuilder = BitcoinPlanner<StandardBitcoinContext>;
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;
type TransactionDecoder = NoTransactionDecoder;
Expand All @@ -43,11 +44,7 @@ impl CoinEntry for BitcoinEntry {
}

#[inline]
fn parse_address_unchecked(
&self,
_coin: &dyn CoinContext,
address: &str,
) -> AddressResult<Self::Address> {
fn parse_address_unchecked(&self, address: &str) -> AddressResult<Self::Address> {
StandardBitcoinAddress::from_str(address)
}

Expand All @@ -64,7 +61,7 @@ impl CoinEntry for BitcoinEntry {

#[inline]
fn sign(&self, coin: &dyn CoinContext, proto: Self::SigningInput<'_>) -> Self::SigningOutput {
BitcoinSigner::sign(coin, &proto)
BitcoinSigner::<StandardBitcoinContext>::sign(coin, &proto)
}

#[inline]
Expand All @@ -73,7 +70,7 @@ impl CoinEntry for BitcoinEntry {
coin: &dyn CoinContext,
proto: Proto::SigningInput<'_>,
) -> Self::PreSigningOutput {
BitcoinCompiler::preimage_hashes(coin, proto)
BitcoinCompiler::<StandardBitcoinContext>::preimage_hashes(coin, proto)
}

#[inline]
Expand All @@ -84,12 +81,12 @@ impl CoinEntry for BitcoinEntry {
signatures: Vec<SignatureBytes>,
public_keys: Vec<PublicKeyBytes>,
) -> Self::SigningOutput {
BitcoinCompiler::compile(coin, proto, signatures, public_keys)
BitcoinCompiler::<StandardBitcoinContext>::compile(coin, proto, signatures, public_keys)
}

#[inline]
fn plan_builder(&self) -> Option<Self::PlanBuilder> {
Some(BitcoinPlanner)
Some(BitcoinPlanner::<StandardBitcoinContext>::default())
}

#[inline]
Expand Down
1 change: 1 addition & 0 deletions rust/chains/tw_bitcoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
//
// Copyright © 2017 Trust Wallet.

pub mod context;
pub mod entry;
pub mod modules;
18 changes: 12 additions & 6 deletions rust/chains/tw_bitcoin/src/modules/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::modules::protobuf_builder::ProtobufBuilder;
use crate::modules::psbt_request::PsbtRequest;
use crate::modules::signing_request::SigningRequestBuilder;
use std::borrow::Cow;
use std::marker::PhantomData;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes};
use tw_coin_entry::error::prelude::*;
Expand All @@ -15,6 +16,7 @@ use tw_proto::BitcoinV2::Proto::mod_PreSigningOutput::{
SigningMethod as ProtoSigningMethod, TaprootTweak as ProtoTaprootTweak,
};
use tw_proto::BitcoinV2::Proto::mod_SigningInput::OneOftransaction as TransactionType;
use tw_utxo::context::UtxoContext;
use tw_utxo::modules::sighash_computer::{SighashComputer, TaprootTweak, TxPreimage};
use tw_utxo::modules::sighash_verifier::SighashVerifier;
use tw_utxo::modules::tx_compiler::TxCompiler;
Expand All @@ -23,9 +25,11 @@ use tw_utxo::modules::utxo_selector::SelectResult;
use tw_utxo::signing_mode::SigningMethod;
use tw_utxo::transaction::transaction_interface::TransactionInterface;

pub struct BitcoinCompiler;
pub struct BitcoinCompiler<Context: UtxoContext> {
_phantom: PhantomData<Context>,
}

impl BitcoinCompiler {
impl<Context: UtxoContext> BitcoinCompiler<Context> {
/// Please note that [`Proto::SigningInput::public_key`] must be set.
/// If the public key should be derived from a private key, please do it before this method is called.
#[inline]
Expand All @@ -43,10 +47,12 @@ impl BitcoinCompiler {
) -> SigningResult<Proto::PreSigningOutput<'static>> {
let unsigned_tx = match input.transaction {
TransactionType::builder(ref tx_builder) => {
let request = SigningRequestBuilder::build(coin, &input, tx_builder)?;
let request = SigningRequestBuilder::<Context>::build(coin, &input, tx_builder)?;
TxPlanner::plan(request)?.unsigned_tx
},
TransactionType::psbt(ref psbt) => PsbtRequest::build(&input, psbt)?.unsigned_tx,
TransactionType::psbt(ref psbt) => {
PsbtRequest::<Context>::build(&input, psbt)?.unsigned_tx
},
TransactionType::None => {
return SigningError::err(SigningErrorType::Error_invalid_params)
.context("Either `TransactionBuilder` or `Psbt` should be set")
Expand Down Expand Up @@ -104,7 +110,7 @@ impl BitcoinCompiler {
tx_builder_input: &Proto::TransactionBuilder,
signatures: Vec<SignatureBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
let request = SigningRequestBuilder::build(coin, input, tx_builder_input)?;
let request = SigningRequestBuilder::<Context>::build(coin, input, tx_builder_input)?;
let SelectResult { unsigned_tx, plan } = TxPlanner::plan(request)?;

SighashVerifier::verify_signatures(&unsigned_tx, &signatures)?;
Expand All @@ -130,7 +136,7 @@ impl BitcoinCompiler {
psbt: &Proto::Psbt,
signatures: Vec<SignatureBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
let PsbtRequest { unsigned_tx, .. } = PsbtRequest::build(input, psbt)?;
let PsbtRequest { unsigned_tx, .. } = PsbtRequest::<Context>::build(input, psbt)?;
let fee = unsigned_tx.fee()?;

SighashVerifier::verify_signatures(&unsigned_tx, &signatures)?;
Expand Down
15 changes: 10 additions & 5 deletions rust/chains/tw_bitcoin/src/modules/planner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ use crate::modules::signing_request::SigningRequestBuilder;
use crate::modules::tx_builder::utxo_protobuf::parse_out_point;
use std::borrow::Cow;
use std::collections::HashMap;
use std::marker::PhantomData;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::error::prelude::*;
use tw_coin_entry::modules::plan_builder::PlanBuilder;
use tw_coin_entry::signing_output_error;
use tw_proto::BitcoinV2::Proto;
use tw_utxo::context::UtxoContext;
use tw_utxo::modules::tx_planner::TxPlanner;
use tw_utxo::modules::utxo_selector::SelectResult;

pub mod psbt_planner;

pub struct BitcoinPlanner;
#[derive(Default)]
pub struct BitcoinPlanner<Context: UtxoContext> {
_phantom: PhantomData<Context>,
}

impl BitcoinPlanner {
impl<Context: UtxoContext> BitcoinPlanner<Context> {
pub fn plan_impl<'a>(
coin: &dyn CoinContext,
input: &Proto::SigningInput<'a>,
Expand All @@ -28,7 +33,7 @@ impl BitcoinPlanner {
match input.transaction {
TransactionType::builder(ref tx) => Self::plan_with_tx_builder(coin, input, tx),
TransactionType::psbt(ref psbt) => {
psbt_planner::PsbtPlanner::plan_psbt(coin, input, psbt)
psbt_planner::PsbtPlanner::<Context>::plan_psbt(coin, input, psbt)
},
TransactionType::None => SigningError::err(SigningErrorType::Error_invalid_params)
.context("Either `TransactionBuilder` or `Psbt` should be set"),
Expand All @@ -40,7 +45,7 @@ impl BitcoinPlanner {
input: &Proto::SigningInput<'a>,
tx_builder: &Proto::TransactionBuilder<'a>,
) -> SigningResult<Proto::TransactionPlan<'a>> {
let request = SigningRequestBuilder::build(coin, input, tx_builder)?;
let request = SigningRequestBuilder::<Context>::build(coin, input, tx_builder)?;
let SelectResult { unsigned_tx, plan } = TxPlanner::plan(request)?;

// Prepare a map of source Inputs Proto `{ OutPoint -> Input }`.
Expand Down Expand Up @@ -93,7 +98,7 @@ impl BitcoinPlanner {
}
}

impl PlanBuilder for BitcoinPlanner {
impl<Context: UtxoContext> PlanBuilder for BitcoinPlanner<Context> {
type SigningInput<'a> = Proto::SigningInput<'a>;
type Plan<'a> = Proto::TransactionPlan<'a>;

Expand Down
Loading

0 comments on commit b1f4543

Please sign in to comment.