Skip to content

Commit

Permalink
WIP starting funding taproot tx impl
Browse files Browse the repository at this point in the history
  • Loading branch information
h4sh3d committed Mar 18, 2022
1 parent 29c41e4 commit 0731a73
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 9 deletions.
21 changes: 12 additions & 9 deletions contrib/run-rpc-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

cd ..

ID=$(docker run --rm -d -p 18443:18443 coblox/bitcoin-core\
-regtest\
-server\
-fallbackfee=0.00001\
-rpcbind=0.0.0.0\
-rpcallowip=0.0.0.0/0\
-rpcuser=test\
-rpcpassword=cEl2o3tHHgzYeuu3CiiZ2FjdgSiw9wNeMFzoNbFmx9k=)
ID=$(docker run --rm -d -p 18443:18443\
--env NETWORK="regtest -rpcuser=test -rpcpassword=cEl2o3tHHgzYeuu3CiiZ2FjdgSiw9wNeMFzoNbFmx9k="\
--env FALLBACKFEE="0.00001"\
--env RPC_PORT="18443"\
ghcr.io/farcaster-project/containers/bitcoin-core:22.0)

export CI=false RPC_USER=test RPC_PASS=cEl2o3tHHgzYeuu3CiiZ2FjdgSiw9wNeMFzoNbFmx9k=
cargo test --test transactions --features rpc -- --test-threads=1

case "$1" in
"--taproot")
cargo test --test taproot --features taproot,rpc -- --test-threads=1;;
*)
cargo test --test transactions --features rpc -- --test-threads=1;;
esac

docker kill $ID

Expand Down
96 changes: 96 additions & 0 deletions src/bitcoin/taproot/funding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use bitcoin::blockdata::transaction::{OutPoint, Transaction};
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{Address, XOnlyPublicKey};

use crate::blockchain::Network;
use crate::transaction::{Error, Fundable, Linkable};

use crate::bitcoin::taproot::Taproot;
use crate::bitcoin::transaction::MetadataOutput;
use crate::bitcoin::Bitcoin;
use bitcoin::util::taproot::TaprootSpendInfo;

#[derive(Debug, Clone)]
pub struct Funding {
untweaked_public_key: Option<XOnlyPublicKey>,
network: Option<bitcoin::Network>,
seen_tx: Option<Transaction>,
}

impl Linkable<MetadataOutput> for Funding {
fn get_consumable_output(&self) -> Result<MetadataOutput, Error> {
let secp = Secp256k1::new();

let address = Address::p2tr(
&secp,
self.untweaked_public_key.ok_or(Error::MissingPublicKey)?,
None,
self.network.ok_or(Error::MissingNetwork)?,
);
let script_pubkey = address.script_pubkey();

match &self.seen_tx {
Some(t) => t
.output
.iter()
.enumerate()
.find(|(_, tx_out)| tx_out.script_pubkey == script_pubkey)
.map(|(ix, tx_out)| MetadataOutput {
out_point: OutPoint::new(t.txid(), ix as u32),
tx_out: tx_out.clone(),
script_pubkey: Some(script_pubkey),
})
.ok_or(Error::MissingUTXO),
// The transaction has not been see yet, cannot infer the UTXO
None => Err(Error::MissingOnchainTransaction),
}
}
}

impl Fundable<Bitcoin<Taproot>, MetadataOutput> for Funding {
fn initialize(pubkey: XOnlyPublicKey, network: Network) -> Result<Self, Error> {
let network = match network {
Network::Mainnet => bitcoin::Network::Bitcoin,
Network::Testnet => bitcoin::Network::Testnet,
Network::Local => bitcoin::Network::Regtest,
};
Ok(Funding {
untweaked_public_key: Some(pubkey),
network: Some(network),
seen_tx: None,
})
}

fn get_address(&self) -> Result<Address, Error> {
let secp = Secp256k1::new();
let taproot_info = TaprootSpendInfo::new_key_spend(
&secp,
self.untweaked_public_key.ok_or(Error::MissingPublicKey)?,
None,
);

Ok(Address::p2tr(
&secp,
taproot_info.internal_key(),
taproot_info.merkle_root(),
self.network.ok_or(Error::MissingNetwork)?,
))
}

fn update(&mut self, tx: Transaction) -> Result<(), Error> {
self.seen_tx = Some(tx);
Ok(())
}

fn raw(tx: Transaction) -> Result<Self, Error> {
Ok(Self {
untweaked_public_key: None,
network: None,
seen_tx: Some(tx),
})
}

fn was_seen(&self) -> bool {
self.seen_tx.is_some()
}
}
7 changes: 7 additions & 0 deletions src/bitcoin/taproot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::str::FromStr;

use crate::bitcoin::taproot::funding::Funding;

use crate::bitcoin::{Bitcoin, BitcoinTaproot, Btc, Strategy};
use crate::consensus::{self, CanonicalBytes};
use crate::crypto::{Keys, SharedKeyId, SharedSecretKeys, Signatures};
Expand All @@ -13,6 +15,11 @@ use crate::crypto::{Keys, SharedKeyId, SharedSecretKeys, Signatures};
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::secp256k1::{constants::SECRET_KEY_SIZE, schnorr::Signature, KeyPair, XOnlyPublicKey};

pub mod funding;

/// Funding the swap creating a Taproot (SegWit v1) output.
pub type FundingTx = Funding;

/// Inner type for the Taproot strategy with on-chain scripts.
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
pub struct Taproot;
Expand Down
26 changes: 26 additions & 0 deletions tests/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@ macro_rules! new_address {
pair.0,
)
}};
(taproot) => {{
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::rand::thread_rng;
use bitcoin::secp256k1::{KeyPair, Secp256k1};
use bitcoin::util::address::Address;
use bitcoin::util::taproot::TaprootSpendInfo;

// Generate random key pair
let s = Secp256k1::new();
let keypair = KeyPair::new(&s, &mut thread_rng());
let xpubkey = keypair.public_key().into();

let taproot_info = TaprootSpendInfo::new_key_spend(&s, xpubkey, None);

// Generate pay-to-taproot address
(
Address::p2tr(
&s,
taproot_info.internal_key(),
taproot_info.merkle_root(),
Network::Regtest,
),
xpubkey,
keypair,
)
}};
}

macro_rules! send {
Expand Down
63 changes: 63 additions & 0 deletions tests/taproot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#![cfg(all(feature = "rpc", feature = "taproot"))]

use farcaster_core::bitcoin::taproot::*;
use farcaster_core::blockchain::*;
use farcaster_core::transaction::*;

use bitcoin::Amount;
use bitcoincore_rpc::json::AddressType;
use bitcoincore_rpc::RpcApi;

#[macro_use]
mod rpc;

#[test]
fn taproot_funding_tx() {
let (_, xpubkey_a1, keypair_a1) = new_address!(taproot);
//let (_, pubkey_a2, secret_a2) = new_address!();

let (_, xpubkey_b1, keypair_b1) = new_address!(taproot);
//let (_, pubkey_b2, secret_b2) = new_address!();

let mut funding = FundingTx::initialize(xpubkey_a1, Network::Local).unwrap();

//
// Create a wallet, mined funds, send to funding address with multiple UTXOs
//
if let Err(_) = rpc::CLIENT.create_wallet("test_wallet", Some(false), None, None, None) {
let wallets = rpc::CLIENT.list_wallets().unwrap();
if wallets.len() == 0 {
rpc::CLIENT.load_wallet("test_wallet").unwrap();
}
if wallets.len() > 1 {
panic!("More than one wallet loaded!");
}
}

let wallet_address = rpc::CLIENT
.get_new_address(None, Some(AddressType::Bech32))
.unwrap();
rpc::CLIENT.generate_to_address(4, &wallet_address).unwrap();
mine!(100);
let target_swap_amount = bitcoin::Amount::from_btc(8.0).unwrap();

let address = funding.get_address().unwrap();
println!("{:?}", address);
let txid = rpc::CLIENT
.send_to_address(
&address,
target_swap_amount,
None,
None,
None,
None,
None,
None,
)
.unwrap();

let funding_tx_seen: bitcoin::Transaction = rpc::CLIENT.get_by_id(&txid).unwrap();
// Minimum of fee of 122 sat
let target_amount = Amount::from_sat(target_swap_amount.as_sat() - 122);
funding.update(funding_tx_seen).unwrap();
}

0 comments on commit 0731a73

Please sign in to comment.