Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Zcash]: Migrate Zcash blockchain to Rust #4232

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
18 changes: 17 additions & 1 deletion 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 @@ -16,6 +16,7 @@ members = [
"chains/tw_sui",
"chains/tw_thorchain",
"chains/tw_ton",
"chains/tw_zcash",
"frameworks/tw_ton_sdk",
"frameworks/tw_utxo",
"tw_any_coin",
Expand Down
25 changes: 25 additions & 0 deletions rust/chains/tw_bitcoin/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,35 @@
//
// Copyright © 2017 Trust Wallet.

use crate::modules::protobuf_builder::standard_protobuf_builder::StandardProtobufBuilder;
use crate::modules::protobuf_builder::ProtobufBuilder;
use crate::modules::psbt_request::standard_psbt_request_builder::StandardPsbtRequestBuilder;
use crate::modules::psbt_request::PsbtRequestBuilder;
use crate::modules::signing_request::standard_signing_request::StandardSigningRequestBuilder;
use crate::modules::signing_request::SigningRequestBuilder;
use tw_coin_entry::error::prelude::SigningResult;
use tw_utxo::address::standard_bitcoin::StandardBitcoinAddress;
use tw_utxo::context::{AddressPrefixes, UtxoContext};
use tw_utxo::fee::fee_estimator::StandardFeeEstimator;
use tw_utxo::script::Script;
use tw_utxo::transaction::standard_transaction::Transaction;

/// A set of associated modules different from a chain to chain.
/// The modules are mainly used to generate a signing request from a [`BitcoinV2::Proto::SigningInput`],
/// and generate a [`Utxo::Proto::Transaction`] output when the transaction is signed.
pub trait BitcoinSigningContext: UtxoContext + Sized {
type SigningRequestBuilder: SigningRequestBuilder<Self>;
type ProtobufBuilder: ProtobufBuilder<Self>;
type PsbtRequestBuilder: PsbtRequestBuilder<Self>;
}

#[derive(Default)]
pub struct StandardBitcoinContext;

impl UtxoContext for StandardBitcoinContext {
type Address = StandardBitcoinAddress;
type Transaction = Transaction;
type FeeEstimator = StandardFeeEstimator<Self::Transaction>;

fn addr_to_script_pubkey(
addr: &Self::Address,
Expand All @@ -26,3 +45,9 @@ impl UtxoContext for StandardBitcoinContext {
}
}
}

impl BitcoinSigningContext for StandardBitcoinContext {
type SigningRequestBuilder = StandardSigningRequestBuilder;
type ProtobufBuilder = StandardProtobufBuilder;
type PsbtRequestBuilder = StandardPsbtRequestBuilder;
}
20 changes: 10 additions & 10 deletions rust/chains/tw_bitcoin/src/modules/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
//
// Copyright © 2017 Trust Wallet.

use crate::context::BitcoinSigningContext;
use crate::modules::protobuf_builder::ProtobufBuilder;
use crate::modules::psbt_request::PsbtRequest;
use crate::modules::psbt_request::{PsbtRequest, PsbtRequestBuilder};
use crate::modules::signing_request::SigningRequestBuilder;
use std::borrow::Cow;
use std::marker::PhantomData;
Expand All @@ -17,6 +18,7 @@ use tw_proto::BitcoinV2::Proto::mod_PreSigningOutput::{
};
use tw_proto::BitcoinV2::Proto::mod_SigningInput::OneOftransaction as TransactionType;
use tw_utxo::context::UtxoContext;
use tw_utxo::encode::Encodable;
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 @@ -29,7 +31,7 @@ pub struct BitcoinCompiler<Context: UtxoContext> {
_phantom: PhantomData<Context>,
}

impl<Context: UtxoContext> BitcoinCompiler<Context> {
impl<Context: BitcoinSigningContext> 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 @@ -47,11 +49,11 @@ impl<Context: UtxoContext> BitcoinCompiler<Context> {
) -> SigningResult<Proto::PreSigningOutput<'static>> {
let unsigned_tx = match input.transaction {
TransactionType::builder(ref tx_builder) => {
let request = SigningRequestBuilder::<Context>::build(coin, &input, tx_builder)?;
let request = Context::SigningRequestBuilder::build(coin, &input, tx_builder)?;
TxPlanner::plan(request)?.unsigned_tx
},
TransactionType::psbt(ref psbt) => {
PsbtRequest::<Context>::build(&input, psbt)?.unsigned_tx
Context::PsbtRequestBuilder::build(&input, psbt)?.unsigned_tx
},
TransactionType::None => {
return SigningError::err(SigningErrorType::Error_invalid_params)
Expand Down Expand Up @@ -110,15 +112,14 @@ impl<Context: UtxoContext> BitcoinCompiler<Context> {
tx_builder_input: &Proto::TransactionBuilder,
signatures: Vec<SignatureBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
let request = SigningRequestBuilder::<Context>::build(coin, input, tx_builder_input)?;
let request = Context::SigningRequestBuilder::build(coin, input, tx_builder_input)?;
let SelectResult { unsigned_tx, plan } = TxPlanner::plan(request)?;

SighashVerifier::verify_signatures(&unsigned_tx, &signatures)?;
let signed_tx = TxCompiler::compile(unsigned_tx, &signatures)?;
let tx_proto = ProtobufBuilder::tx_to_proto(&signed_tx);

Ok(Proto::SigningOutput {
transaction: Some(tx_proto),
transaction: Context::ProtobufBuilder::tx_to_proto(&signed_tx),
encoded: Cow::from(signed_tx.encode_out()),
txid: Cow::from(signed_tx.txid()),
// `vsize` could have been changed after the transaction being signed.
Expand All @@ -136,15 +137,14 @@ impl<Context: UtxoContext> BitcoinCompiler<Context> {
psbt: &Proto::Psbt,
signatures: Vec<SignatureBytes>,
) -> SigningResult<Proto::SigningOutput<'static>> {
let PsbtRequest { unsigned_tx, .. } = PsbtRequest::<Context>::build(input, psbt)?;
let PsbtRequest { unsigned_tx, .. } = Context::PsbtRequestBuilder::build(input, psbt)?;
let fee = unsigned_tx.fee()?;

SighashVerifier::verify_signatures(&unsigned_tx, &signatures)?;
let signed_tx = TxCompiler::compile(unsigned_tx, &signatures)?;
let tx_proto = ProtobufBuilder::tx_to_proto(&signed_tx);

Ok(Proto::SigningOutput {
transaction: Some(tx_proto),
transaction: Context::ProtobufBuilder::tx_to_proto(&signed_tx),
encoded: Cow::from(signed_tx.encode_out()),
txid: Cow::from(signed_tx.txid()),
// `vsize` could have been changed after the transaction being signed.
Expand Down
20 changes: 12 additions & 8 deletions rust/chains/tw_bitcoin/src/modules/planner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// Copyright © 2017 Trust Wallet.

use crate::context::BitcoinSigningContext;
use crate::modules::signing_request::SigningRequestBuilder;
use crate::modules::tx_builder::utxo_protobuf::parse_out_point;
use std::borrow::Cow;
Expand All @@ -15,6 +16,9 @@ use tw_proto::BitcoinV2::Proto;
use tw_utxo::context::UtxoContext;
use tw_utxo::modules::tx_planner::TxPlanner;
use tw_utxo::modules::utxo_selector::SelectResult;
use tw_utxo::transaction::transaction_interface::{
TransactionInterface, TxInputInterface, TxOutputInterface,
};

pub mod psbt_planner;

Expand All @@ -23,7 +27,7 @@ pub struct BitcoinPlanner<Context: UtxoContext> {
_phantom: PhantomData<Context>,
}

impl<Context: UtxoContext> BitcoinPlanner<Context> {
impl<Context: BitcoinSigningContext> BitcoinPlanner<Context> {
pub fn plan_impl<'a>(
coin: &dyn CoinContext,
input: &Proto::SigningInput<'a>,
Expand All @@ -45,7 +49,7 @@ impl<Context: UtxoContext> BitcoinPlanner<Context> {
input: &Proto::SigningInput<'a>,
tx_builder: &Proto::TransactionBuilder<'a>,
) -> SigningResult<Proto::TransactionPlan<'a>> {
let request = SigningRequestBuilder::<Context>::build(coin, input, tx_builder)?;
let request = Context::SigningRequestBuilder::build(coin, input, tx_builder)?;
let SelectResult { unsigned_tx, plan } = TxPlanner::plan(request)?;

// Prepare a map of source Inputs Proto `{ OutPoint -> Input }`.
Expand All @@ -64,23 +68,23 @@ impl<Context: UtxoContext> BitcoinPlanner<Context> {
let mut selected_inputs_proto = Vec::with_capacity(unsigned_tx.inputs().len());
for selected_utxo in unsigned_tx.inputs() {
let utxo_proto = inputs_map
.get(&selected_utxo.previous_output)
.get(selected_utxo.previous_output())
.or_tw_err(SigningErrorType::Error_internal)
.context("Planned transaction contains an unknown UTXO")?;
selected_inputs_proto.push((*utxo_proto).clone());
}

// Fill out the Output Proto.
let mut outputs_proto = Vec::with_capacity(unsigned_tx.transaction().outputs.len());
for selected_output in unsigned_tx.transaction().outputs.iter() {
let mut outputs_proto = Vec::with_capacity(unsigned_tx.transaction().outputs().len());
for selected_output in unsigned_tx.transaction().outputs().iter() {
// For now, just provide a scriptPubkey as is.
// Later it's probably worth to return the same output builders as in `SigningInput`.
let to_recipient = Proto::mod_Output::OneOfto_recipient::custom_script_pubkey(
Cow::from(selected_output.script_pubkey.to_vec()),
Cow::from(selected_output.script_pubkey().to_vec()),
);

outputs_proto.push(Proto::Output {
value: selected_output.value,
value: selected_output.value(),
to_recipient,
})
}
Expand All @@ -98,7 +102,7 @@ impl<Context: UtxoContext> BitcoinPlanner<Context> {
}
}

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

Expand Down
41 changes: 23 additions & 18 deletions rust/chains/tw_bitcoin/src/modules/planner/psbt_planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
//
// Copyright © 2017 Trust Wallet.

use crate::modules::psbt_request::PsbtRequest;
use crate::modules::signing_request::SigningRequestBuilder;
use crate::context::BitcoinSigningContext;
use crate::modules::psbt_request::{PsbtRequest, PsbtRequestBuilder};
use crate::modules::signing_request::standard_signing_request::chain_info;
use crate::modules::tx_builder::script_parser::StandardScriptParser;
use crate::modules::tx_builder::BitcoinChainInfo;
use std::marker::PhantomData;
Expand All @@ -12,23 +13,25 @@ use tw_coin_entry::error::prelude::*;
use tw_proto::BitcoinV2::Proto;
use tw_proto::BitcoinV2::Proto::mod_Input::OneOfclaiming_script as ClaimingScriptProto;
use tw_proto::BitcoinV2::Proto::mod_Output::OneOfto_recipient as ToRecipientProto;
use tw_utxo::context::UtxoContext;
use tw_utxo::transaction::standard_transaction::{TransactionInput, TransactionOutput};
use tw_utxo::transaction::transaction_interface::TransactionInterface;
use tw_proto::Utxo::Proto as UtxoProto;
use tw_utxo::transaction::transaction_interface::{
TransactionInterface, TxInputInterface, TxOutputInterface,
};
use tw_utxo::transaction::UtxoToSign;

pub struct PsbtPlanner<Context: UtxoContext> {
pub struct PsbtPlanner<Context: BitcoinSigningContext> {
_phantom: PhantomData<Context>,
}

impl<Context: UtxoContext> PsbtPlanner<Context> {
impl<Context: BitcoinSigningContext> PsbtPlanner<Context> {
pub fn plan_psbt(
coin: &dyn CoinContext,
input: &Proto::SigningInput,
psbt_input: &Proto::Psbt,
) -> SigningResult<Proto::TransactionPlan<'static>> {
let chain_info = SigningRequestBuilder::<Context>::chain_info(coin, &input.chain_info)?;
let PsbtRequest { unsigned_tx, .. } = PsbtRequest::<Context>::build(input, psbt_input)?;
let chain_info = chain_info(coin, &input.chain_info)?;
let PsbtRequest { unsigned_tx, .. } =
Context::PsbtRequestBuilder::build(input, psbt_input)?;

let total_input = unsigned_tx.total_input()?;
let fee_estimate = unsigned_tx.fee()?;
Expand Down Expand Up @@ -62,17 +65,18 @@ impl<Context: UtxoContext> PsbtPlanner<Context> {

pub fn utxo_to_proto(
unsigned_txin: &UtxoToSign,
txin: &TransactionInput,
txin: &<Context::Transaction as TransactionInterface>::Input,
chain_info: &BitcoinChainInfo,
) -> SigningResult<Proto::Input<'static>> {
let out_point = Proto::OutPoint {
hash: txin.previous_output.hash.to_vec().into(),
vout: txin.previous_output.index,
let out_point = UtxoProto::OutPoint {
hash: txin.previous_output().hash.to_vec().into(),
vout: txin.previous_output().index,
};
let sequence = Proto::mod_Input::Sequence {
sequence: txin.sequence,
sequence: txin.sequence(),
};

// TODO add `UtxoSigningContext::ScriptParser` associative type.
let from_address = StandardScriptParser
.parse(&unsigned_txin.prevout_script_pubkey)?
.try_to_address(chain_info)?
Expand All @@ -90,20 +94,21 @@ impl<Context: UtxoContext> PsbtPlanner<Context> {
}

pub fn output_to_proto(
output: &TransactionOutput,
output: &<Context::Transaction as TransactionInterface>::Output,
chain_info: &BitcoinChainInfo,
) -> SigningResult<Proto::Output<'static>> {
// TODO add `UtxoSigningContext::ScriptParser` associative type.
let to_recipient = match StandardScriptParser
.parse(&output.script_pubkey)?
.parse(output.script_pubkey())?
.try_to_address(chain_info)?
{
Some(to_addr) => ToRecipientProto::to_address(to_addr.to_string().into()),
// Cannot convert the output scriptPubkey into an address. Return it as is.
None => ToRecipientProto::custom_script_pubkey(output.script_pubkey.to_vec().into()),
None => ToRecipientProto::custom_script_pubkey(output.script_pubkey().to_vec().into()),
};

Ok(Proto::Output {
value: output.value,
value: output.value(),
to_recipient,
})
}
Expand Down
Loading