Skip to content

Commit

Permalink
[BCH]: Fix Transaction Planning (trustwallet#4118)
Browse files Browse the repository at this point in the history
* [BCH]: Fix Transaction Planning

* [BCH]: Add BitcoinCash transaction signing V2 test in C++
  • Loading branch information
satoshiotomakan authored Nov 20, 2024
1 parent 45124dd commit e34e9ba
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 6 deletions.
1 change: 1 addition & 0 deletions rust/chains/tw_bitcoincash/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tw_coin_entry::error::prelude::*;
use tw_utxo::context::{AddressPrefixes, UtxoContext};
use tw_utxo::script::Script;

#[derive(Default)]
pub struct BitcoinCashContext;

impl UtxoContext for BitcoinCashContext {
Expand Down
10 changes: 10 additions & 0 deletions rust/chains/tw_bitcoincash/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,14 @@ impl CoinEntry for BitcoinCashEntry {
) -> Self::SigningOutput {
BitcoinCompiler::<BitcoinCashContext>::compile(coin, input, signatures, public_keys)
}

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

#[inline]
fn transaction_util(&self) -> Option<Self::TransactionUtil> {
Some(BitcoinTransactionUtil)
}
}
13 changes: 11 additions & 2 deletions rust/tw_tests/tests/chains/bitcoincash/bitcoincash_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Copyright © 2017 Trust Wallet.

use crate::chains::bitcoincash::test_cases::transfer_96ee20;
use crate::chains::common::bitcoin::{btc_info, sign, TransactionOneof};
use crate::chains::common::bitcoin::{btc_info, plan, sign, TransactionOneof};
use tw_coin_registry::coin_type::CoinType;
use tw_encoding::hex::DecodeHex;
use tw_proto::BitcoinV2::Proto;
Expand All @@ -20,6 +20,16 @@ fn test_bitcoincash_sign_input_p2pkh_from_to_address() {
..Default::default()
};

plan::BitcoinPlanHelper::new(&signing)
.coin(CoinType::BitcoinCash)
.plan(plan::Expected {
inputs: vec![5151],
outputs: vec![600, 4325],
vsize_estimate: 227,
fee_estimate: 226,
change: 0,
});

// Successfully broadcasted:
// https://blockchair.com/bitcoin-cash/transaction/96ee20002b34e468f9d3c5ee54f6a8ddaa61c118889c4f35395c2cd93ba5bbb4
sign::BitcoinSignHelper::new(&signing)
Expand All @@ -29,7 +39,6 @@ fn test_bitcoincash_sign_input_p2pkh_from_to_address() {
txid: transfer_96ee20::TX_ID,
inputs: vec![5151],
outputs: vec![600, 4325],
// `vsize` is different from the estimated value due to the signatures der serialization.
vsize: 226,
weight: 904,
fee: 226,
Expand Down
7 changes: 3 additions & 4 deletions rust/tw_tests/tests/chains/solana/solana_transaction_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@
//
// Copyright © 2017 Trust Wallet.

use tw_any_coin::test_utils::address_utils::test_address_derive;
use tw_any_coin::test_utils::sign_utils::{AnySignerHelper, PreImageHelper};
use tw_any_coin::test_utils::sign_utils::AnySignerHelper;
use tw_any_coin::test_utils::transaction_decode_utils::TransactionDecoderHelper;
use tw_coin_registry::coin_type::CoinType;
use tw_encoding::base64::STANDARD;
use tw_encoding::hex::{DecodeHex, ToHex};
use tw_encoding::hex::DecodeHex;
use tw_encoding::{base58, base64};
use tw_memory::test_utils::tw_data_helper::TWDataHelper;
use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper;
use tw_memory::test_utils::tw_string_helper::TWStringHelper;
use tw_proto::Common::Proto::SigningError;
use tw_proto::Solana::Proto::{self, mod_SigningInput::OneOftransaction_type as TransactionType};
use tw_proto::Solana::Proto::{self};
use tw_solana::SOLANA_ALPHABET;
use wallet_core_rs::ffi::solana::transaction::{
tw_solana_transaction_get_compute_unit_limit, tw_solana_transaction_get_compute_unit_price,
Expand Down
71 changes: 71 additions & 0 deletions tests/chains/BitcoinCash/TWBitcoinCashTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Bitcoin/SigHashType.h"
#include "HexCoding.h"
#include "proto/Bitcoin.pb.h"
#include "proto/BitcoinV2.pb.h"
#include "TestUtilities.h"

#include <TrustWalletCore/TWBitcoinSigHashType.h>
Expand Down Expand Up @@ -161,6 +162,76 @@ TEST(BitcoinCash, SignTransaction) {
"e510000000000000" "1976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac"
"00000000");
}

TEST(BitcoinCash, SignTransactionV2) {
auto privateKey = parse_hex("7fdafb9db5bc501f2096e7d13d331dc7a75d9594af3d251313ba8b6200f4e384");
auto txId = parse_hex("050d00e2e18ef13969606f1ceee290d3f49bd940684ce39898159352952b8ce2");
std::reverse(txId.begin(), txId.end());

BitcoinV2::Proto::SigningInput signing;
signing.add_private_keys(privateKey.data(), privateKey.size());
signing.mutable_chain_info()->set_p2pkh_prefix(0);
signing.mutable_chain_info()->set_p2sh_prefix(5);
signing.mutable_chain_info()->set_hrp("bitcoincash");

auto& builder = *signing.mutable_builder();
builder.set_version(BitcoinV2::Proto::TransactionVersion::V1);
builder.set_input_selector(BitcoinV2::Proto::InputSelector::UseAll);
builder.set_fixed_dust_threshold(546);

auto& in = *builder.add_inputs();
auto& inOutPoint = *in.mutable_out_point();
inOutPoint.set_hash(txId.data(), txId.size());
inOutPoint.set_vout(2);
in.set_value(5151);
// Cash address without prefix.
in.set_receiver_address("qzhlrcrcne07x94h99thved2pgzdtv8ccujjy73xya");
in.set_sighash_type(TWBitcoinSigHashTypeAll | TWBitcoinSigHashTypeFork);

auto& out0 = *builder.add_outputs();
out0.set_value(600);
// Legacy address.
out0.set_to_address("1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx");

auto& explicitChangeOutput = *builder.add_outputs();
explicitChangeOutput.set_value(4325);
// Cash address with an explicit prefix.
explicitChangeOutput.set_to_address("bitcoincash:qz0q3xmg38sr94rw8wg45vujah7kzma3cskxymnw06");

Proto::SigningInput legacy;
*legacy.mutable_signing_v2() = signing;
legacy.set_coin_type(TWCoinTypeBitcoinCash);

Proto::TransactionPlan plan;
ANY_PLAN(legacy, plan, TWCoinTypeBitcoin);

ASSERT_EQ(plan.error(), Common::Proto::SigningError::OK);
const auto planV2 = plan.planning_result_v2();
EXPECT_EQ(planV2.error(), Common::Proto::SigningError::OK) << planV2.error_message();

EXPECT_EQ(planV2.inputs_size(), 1);
EXPECT_EQ(planV2.outputs_size(), 2);
EXPECT_EQ(planV2.vsize_estimate(), 227);
EXPECT_EQ(planV2.fee_estimate(), 226);
EXPECT_EQ(planV2.change(), 0);

Proto::SigningOutput output;
ANY_SIGN(legacy, TWCoinTypeBitcoin);

EXPECT_EQ(output.error(), Common::Proto::OK);
ASSERT_TRUE(output.has_signing_result_v2());
const auto outputV2 = output.signing_result_v2();
EXPECT_EQ(outputV2.error(), Common::Proto::SigningError::OK) << outputV2.error_message();
ASSERT_EQ(hex(outputV2.encoded()),
"01000000"
"01"
"e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05" "02000000" "6b483045022100b70d158b43cbcded60e6977e93f9a84966bc0cec6f2dfd1463d1223a90563f0d02207548d081069de570a494d0967ba388ff02641d91cadb060587ead95a98d4e3534121038eab72ec78e639d02758e7860cdec018b49498c307791f785aa3019622f4ea5b" "ffffffff"
"02"
"5802000000000000" "1976a914769bdff96a02f9135a1d19b749db6a78fe07dc9088ac"
"e510000000000000" "1976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac"
"00000000");
}

// clang-format on

} // namespace TW::Bitcoin::tests

0 comments on commit e34e9ba

Please sign in to comment.