Skip to content

Commit

Permalink
Increase commit tx value to pay for reveal tx
Browse files Browse the repository at this point in the history
  • Loading branch information
casey committed Jan 17, 2023
1 parent db570a0 commit 8f1f614
Show file tree
Hide file tree
Showing 8 changed files with 560 additions and 178 deletions.
3 changes: 3 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ fmt:
clippy:
cargo clippy --all --all-targets

lclippy:
cargo lclippy --all --all-targets -- -D warnings

deploy branch chain domain:
ssh root@{{domain}} "mkdir -p deploy \
&& apt-get update --yes \
Expand Down
10 changes: 10 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ mod test;
#[cfg(test)]
use self::test::*;

macro_rules! tprintln {
($($arg:tt)*) => {

if cfg!(test) {
eprint!("==> ");
eprintln!($($arg)*);
}
};
}

mod arguments;
mod blocktime;
mod chain;
Expand Down
162 changes: 73 additions & 89 deletions src/subcommand/wallet/inscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use {
},
util::key::PrivateKey,
util::sighash::{Prevouts, SighashCache},
util::taproot::{LeafVersion, TapLeafHash, TaprootBuilder},
util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TaprootBuilder},
PackedLockTime, SchnorrSighashType, Witness,
},
bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp},
Expand Down Expand Up @@ -185,13 +185,25 @@ impl Inscribe {

let commit_tx_address = Address::p2tr_tweaked(taproot_spend_info.output_key(), network);

let unsigned_commit_tx = TransactionBuilder::build_transaction(
let (_, reveal_fee) = Self::build_reveal_transaction(
&control_block,
fee_rate,
OutPoint::null(),
TxOut {
script_pubkey: destination.script_pubkey(),
value: 0,
},
&reveal_script,
);

let unsigned_commit_tx = TransactionBuilder::build_transaction_with_value(
satpoint,
inscriptions,
utxos,
commit_tx_address.clone(),
change,
fee_rate,
reveal_fee + TransactionBuilder::TARGET_POSTAGE,
)?;

let (vout, output) = unsigned_commit_tx
Expand All @@ -201,43 +213,30 @@ impl Inscribe {
.find(|(_vout, output)| output.script_pubkey == commit_tx_address.script_pubkey())
.expect("should find sat commit/inscription output");

let mut reveal_tx = Transaction {
input: vec![TxIn {
previous_output: OutPoint {
txid: unsigned_commit_tx.txid(),
vout: vout.try_into().unwrap(),
},
script_sig: script::Builder::new().into_script(),
witness: Witness::new(),
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
}],
output: vec![TxOut {
let (mut reveal_tx, fee) = Self::build_reveal_transaction(
&control_block,
fee_rate,
OutPoint {
txid: unsigned_commit_tx.txid(),
vout: vout.try_into().unwrap(),
},
TxOut {
script_pubkey: destination.script_pubkey(),
value: output.value,
}],
lock_time: PackedLockTime::ZERO,
version: 1,
};

let fee = {
let mut reveal_tx = reveal_tx.clone();

reveal_tx.input[0].witness.push(
Signature::from_slice(&[0; SCHNORR_SIGNATURE_SIZE])
.unwrap()
.as_ref(),
);
reveal_tx.input[0].witness.push(&reveal_script);
reveal_tx.input[0].witness.push(&control_block.serialize());

fee_rate.fee(reveal_tx.vsize())
};
},
&reveal_script,
);

reveal_tx.output[0].value = reveal_tx.output[0]
.value
.checked_sub(fee.to_sat())
.context("commit transaction output value insufficient to pay transaction fee")?;

assert_eq!(
reveal_tx.output[0].value,
TransactionBuilder::TARGET_POSTAGE.to_sat()
);

if reveal_tx.output[0].value < reveal_tx.output[0].script_pubkey.dust_value().to_sat() {
bail!("commit transaction output would be dust");
}
Expand Down Expand Up @@ -307,6 +306,42 @@ impl Inscribe {

Ok(())
}

fn build_reveal_transaction(
control_block: &ControlBlock,
fee_rate: FeeRate,
input: OutPoint,
output: TxOut,
script: &Script,
) -> (Transaction, Amount) {
let reveal_tx = Transaction {
input: vec![TxIn {
previous_output: input,
script_sig: script::Builder::new().into_script(),
witness: Witness::new(),
sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
}],
output: vec![output],
lock_time: PackedLockTime::ZERO,
version: 1,
};

let fee = {
let mut reveal_tx = reveal_tx.clone();

reveal_tx.input[0].witness.push(
Signature::from_slice(&[0; SCHNORR_SIGNATURE_SIZE])
.unwrap()
.as_ref(),
);
reveal_tx.input[0].witness.push(script);
reveal_tx.input[0].witness.push(&control_block.serialize());

fee_rate.fee(reveal_tx.vsize())
};

(reveal_tx, fee)
}
}

#[cfg(test)]
Expand All @@ -315,7 +350,7 @@ mod tests {

#[test]
fn reveal_transaction_pays_fee() {
let utxos = vec![(outpoint(1), Amount::from_sat(5000))];
let utxos = vec![(outpoint(1), Amount::from_sat(20000))];
let inscription = inscription("text/plain", "ord");
let commit_address = change(0);
let reveal_address = recipient();
Expand All @@ -338,64 +373,13 @@ mod tests {

assert_eq!(
reveal_tx.output[0].value,
5000 - fee.to_sat() - (5000 - commit_tx.output[0].value),
);
}

#[test]
fn reveal_transaction_value_insufficient_to_pay_fee() {
let utxos = vec![(outpoint(1), Amount::from_sat(1000))];
let satpoint = Some(satpoint(1, 0));
let inscription = inscription("image/png", [1; 10_000]);
let commit_address = change(0);
let reveal_address = recipient();

assert!(Inscribe::create_inscription_transactions(
satpoint,
inscription,
BTreeMap::new(),
Network::Bitcoin,
utxos.into_iter().collect(),
vec![commit_address, change(1)],
reveal_address,
FeeRate::try_from(1.0).unwrap(),
)
.unwrap_err()
.to_string()
.contains("commit transaction output value insufficient to pay transaction fee"));
}

#[test]
fn reveal_transaction_would_create_dust() {
let utxos = vec![(outpoint(1), Amount::from_sat(500))];
let inscription = inscription("text/plain", "ord");
let satpoint = Some(satpoint(1, 0));
let commit_address = change(0);
let reveal_address = recipient();

let error = Inscribe::create_inscription_transactions(
satpoint,
inscription,
BTreeMap::new(),
Network::Bitcoin,
utxos.into_iter().collect(),
vec![commit_address, change(1)],
reveal_address,
FeeRate::try_from(1.0).unwrap(),
)
.unwrap_err()
.to_string();

assert!(
error.contains("commit transaction output would be dust"),
"{}",
error
20000 - fee.to_sat() - (20000 - commit_tx.output[0].value),
);
}

#[test]
fn inscript_tansactions_opt_in_to_rbf() {
let utxos = vec![(outpoint(1), Amount::from_sat(5000))];
let utxos = vec![(outpoint(1), Amount::from_sat(20000))];
let inscription = inscription("text/plain", "ord");
let commit_address = change(0);
let reveal_address = recipient();
Expand Down Expand Up @@ -456,8 +440,8 @@ mod tests {
#[test]
fn inscribe_with_no_satpoint_and_enough_cardinal_utxos() {
let utxos = vec![
(outpoint(1), Amount::from_sat(1000)),
(outpoint(2), Amount::from_sat(1000)),
(outpoint(1), Amount::from_sat(20_000)),
(outpoint(2), Amount::from_sat(20_000)),
];
let mut inscriptions = BTreeMap::new();
inscriptions.insert(
Expand Down Expand Up @@ -490,7 +474,7 @@ mod tests {
fn inscribe_with_custom_fee_rate() {
let utxos = vec![
(outpoint(1), Amount::from_sat(10_000)),
(outpoint(2), Amount::from_sat(10_000)),
(outpoint(2), Amount::from_sat(20_000)),
];
let mut inscriptions = BTreeMap::new();
inscriptions.insert(
Expand Down Expand Up @@ -526,7 +510,7 @@ mod tests {

assert_eq!(
reveal_tx.output[0].value,
10_000 - fee - (10_000 - commit_tx.output[0].value),
20_000 - fee - (20_000 - commit_tx.output[0].value),
);
}
}
2 changes: 1 addition & 1 deletion src/subcommand/wallet/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl Send {

let change = get_change_addresses(&options, 2)?;

let unsigned_transaction = TransactionBuilder::build_transaction(
let unsigned_transaction = TransactionBuilder::build_transaction_with_postage(
satpoint,
inscriptions,
unspent_outputs,
Expand Down
Loading

0 comments on commit 8f1f614

Please sign in to comment.