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

[PoC/Bitcoin/Utxo] Correctly Consider Fees for UTXO Selection #3667

Merged
merged 29 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ee37806
change utxo selection logic for tw_utxo::Compiler
lamafab Jan 17, 2024
9dbff6e
small cleanup
lamafab Jan 17, 2024
dc87d4f
update selected utxos, init input_selection test module
lamafab Jan 18, 2024
3fe59dd
expand tests for change output generation
lamafab Jan 18, 2024
9166297
handle change output correctly in fee estimation, expand tests
lamafab Jan 18, 2024
eb07d4a
calculate the effective fee rate, not the theoretical
lamafab Jan 18, 2024
05828dc
add extra sanity checks
lamafab Jan 18, 2024
18f3376
add input_selection_select_ascending test
lamafab Jan 18, 2024
0076671
test for empty inputs and outputs, extend error variant
lamafab Jan 18, 2024
6b2b642
additional comments and sanity checks
lamafab Jan 18, 2024
2e24874
simplify transaction builder for BRC20, update tests
lamafab Jan 18, 2024
efc881c
update reveal transaction signing in BRC20 builder
lamafab Jan 18, 2024
cca09f1
remove weight and fee_estimate checks in tw_utxo, plan for later
lamafab Jan 18, 2024
3872252
extra check for error
lamafab Jan 18, 2024
20b1a34
add input_selection_insufficient_inputs test
lamafab Jan 18, 2024
fa1d422
scrap the BitcoinPlanBuilder
lamafab Jan 19, 2024
5d5af7b
remove utils module
lamafab Jan 19, 2024
fa86681
cargo fmt
lamafab Jan 19, 2024
1247134
calculate the effective fee after setting the change output, if enabled
lamafab Jan 19, 2024
d404b43
add UseAll selection tests, check for 'effective' fee
lamafab Jan 19, 2024
17ff761
remove fee_estimate.rs, covered in input_selection.rs
lamafab Jan 19, 2024
f052adb
use consts for alice and bob info
lamafab Jan 19, 2024
bbea2cf
do extra checks in compile_impl
lamafab Jan 19, 2024
154aaaf
update change amount correctly in the native Transaction type before …
lamafab Jan 20, 2024
93f5144
build index of private keys AFTER selecting UTXOs
lamafab Jan 20, 2024
7530c54
cargo fmt
lamafab Jan 20, 2024
de0c92f
[BRC20]: Take Inscription amount as a string
satoshiotomakan Jan 22, 2024
66d9ef4
[BRC20]: Take Inscription amount as a string in C++
satoshiotomakan Jan 22, 2024
76ecd02
[BRC20]: Add a test for BitcoinV2 bridge through the legacy `SigningI…
satoshiotomakan Jan 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions rust/tw_bitcoin/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::modules::plan_builder::BitcoinPlanBuilder;
use crate::modules::signer::Signer;
use crate::{Error, Result};
use bitcoin::address::NetworkChecked;
Expand All @@ -11,6 +10,7 @@ use tw_coin_entry::derivation::Derivation;
use tw_coin_entry::error::{AddressError, AddressResult};
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::wallet_connector::NoWalletConnector;
use tw_coin_entry::prefix::NoPrefix;
use tw_coin_entry::signing_output_error;
Expand Down Expand Up @@ -44,7 +44,7 @@ impl CoinEntry for BitcoinEntry {

// Optional modules:
type JsonSigner = NoJsonSigner;
type PlanBuilder = BitcoinPlanBuilder;
type PlanBuilder = NoPlanBuilder;
type MessageSigner = NoMessageSigner;
type WalletConnector = NoWalletConnector;

Expand Down Expand Up @@ -128,7 +128,7 @@ impl CoinEntry for BitcoinEntry {

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

Expand All @@ -147,14 +147,15 @@ impl BitcoinEntry {
.map(crate::modules::transactions::InputBuilder::utxo_from_proto)
.collect::<Result<Vec<_>>>()?;

// Convert output builders into Utxo outputs.
// Convert output builders into Utxo outputs (does not contain the change output).
let mut utxo_outputs = proto
.outputs
.iter()
.map(crate::modules::transactions::OutputBuilder::utxo_from_proto)
.collect::<Result<Vec<_>>>()?;

// If automatic change output is enabled, a change script must be provided.
// If automatic change output creation is enabled (by default), a change
// script must be provided.
let change_script_pubkey = if proto.disable_change_output {
Cow::default()
} else {
Expand Down Expand Up @@ -186,17 +187,23 @@ impl BitcoinEntry {
disable_change_output: proto.disable_change_output,
};

// Generate the sighashes to be signed.
// Generate the sighashes to be signed. This also selects the inputs
// according to the input selecter and appends a change output, if
// enabled.
let utxo_presigning = tw_utxo::compiler::Compiler::preimage_hashes(utxo_signing);
handle_utxo_error(&utxo_presigning.error)?;

// If a change output was created by the Utxo compiler, we return it here too.
if utxo_presigning.outputs.len() == utxo_outputs.len() + 1 {
// Check whether the change output is present.
if !proto.disable_change_output {
// Change output has been added.
debug_assert_eq!(utxo_presigning.outputs.len(), utxo_outputs.len() + 1);

let change_output = utxo_presigning
.outputs
.last()
.expect("expected change output");

// Push it to the list of outputs.
utxo_outputs.push(Proto::mod_PreSigningOutput::TxOut {
value: change_output.value,
script_pubkey: change_output.script_pubkey.to_vec().into(),
Expand All @@ -205,6 +212,10 @@ impl BitcoinEntry {
})
}

// Sanity check.
debug_assert!(utxo_presigning.inputs.len() <= proto.inputs.len());
debug_assert_eq!(utxo_presigning.outputs.len(), utxo_outputs.len());

Ok(Proto::PreSigningOutput {
error: Proto::Error::OK,
error_message: Default::default(),
Expand Down Expand Up @@ -241,13 +252,14 @@ impl BitcoinEntry {
crate::modules::transactions::InputClaimBuilder::utxo_claim_from_proto(
input, signature,
)?;

utxo_input_claims.push(utxo_claim);
}

// Process all the outputs.
// Prepare all the outputs.
let mut utxo_outputs = vec![];
for output in proto.outputs {
let utxo = crate::modules::transactions::OutputBuilder::utxo_from_proto(&output)?;
for output in &proto.outputs {
let utxo = crate::modules::transactions::OutputBuilder::utxo_from_proto(output)?;

utxo_outputs.push(utxo);
}
Expand All @@ -272,9 +284,13 @@ impl BitcoinEntry {
let utxo_serialized = tw_utxo::compiler::Compiler::compile(utxo_preserializtion);
handle_utxo_error(&utxo_serialized.error)?;

// Prepare `Proto::TransactionInput` protobufs for signing output.
let mut total_input_amount = 0;

// Prepare `Proto::TransactionInput` for end result.
let mut proto_inputs = vec![];
for input in utxo_input_claims {
total_input_amount += input.value;

proto_inputs.push(Proto::TransactionInput {
txid: Cow::Owned(input.txid.to_vec()),
vout: input.vout,
Expand All @@ -288,7 +304,7 @@ impl BitcoinEntry {
});
}

// Prepare `Proto::TransactionOutput` protobufs for output.
// Prepare `Proto::TransactionOutput` for end result.
let mut proto_outputs = vec![];
for output in utxo_outputs {
proto_outputs.push(Proto::TransactionOutput {
Expand All @@ -299,14 +315,29 @@ impl BitcoinEntry {
});
}

// Prepare `Proto::Transaction` protobuf for output.
// Prepare `Proto::Transaction` for end result.
let transaction = Proto::Transaction {
version: proto.version,
lock_time: proto.lock_time,
inputs: proto_inputs,
outputs: proto_outputs,
};

let total_output_amount = transaction
.outputs
.iter()
.map(|output| output.value)
.sum::<u64>();

// Sanity check.
debug_assert_eq!(transaction.inputs.len(), proto.inputs.len());
debug_assert_eq!(transaction.outputs.len(), proto.outputs.len());
// Every output is accounted for, including the fee.
debug_assert_eq!(
total_input_amount,
total_output_amount + utxo_serialized.fee
);

// Return the full protobuf output.
Ok(Proto::SigningOutput {
error: Proto::Error::OK,
Expand Down Expand Up @@ -353,6 +384,7 @@ fn handle_utxo_error(utxo_err: &UtxoProto::Error) -> Result<()> {
UtxoProto::Error::Error_missing_sighash_method => Proto::Error::Error_utxo_missing_sighash_method,
UtxoProto::Error::Error_failed_encoding => Proto::Error::Error_utxo_failed_encoding,
UtxoProto::Error::Error_insufficient_inputs => Proto::Error::Error_utxo_insufficient_inputs,
UtxoProto::Error::Error_no_outputs_specified => Proto::Error::Error_utxo_no_outputs_specified,
UtxoProto::Error::Error_missing_change_script_pubkey => Proto::Error::Error_utxo_missing_change_script_pubkey,
};

Expand Down
1 change: 0 additions & 1 deletion rust/tw_bitcoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error(Proto::Error);

// TODO: We can improve this.
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
Expand Down
2 changes: 0 additions & 2 deletions rust/tw_bitcoin/src/modules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
pub mod legacy;
pub mod plan_builder;
pub mod signer;
pub mod transactions;
mod utils;
Loading
Loading