use std::str::FromStr;
use elements::bitcoin::PrivateKey;
use elements::encode::{serialize, serialize_hex};
use elements::hashes::Hash;
use elements::sighash::SighashCache;
use elements::{confidential, AssetId, LockTime, TxOutWitness};
use miniscript::elements::pset::PartiallySignedTransaction as Psbt;
use miniscript::elements::{
self, pset, secp256k1_zkp as secp256k1, Address, AddressParams, OutPoint, Script, Sequence,
Transaction, TxIn, TxOut,
};
use miniscript::psbt::{PsbtExt, PsbtInputExt};
use miniscript::{elementssig_to_rawsig, Descriptor};
use {actual_base64 as base64, elements_miniscript as miniscript};
const ELEMENTS_PARAMS: AddressParams = AddressParams::ELEMENTS;
fn main() {
let secp256k1 = secp256k1::Secp256k1::new();
let s = "elwsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#tdp6ld3e";
let bridge_descriptor = Descriptor::from_str(s).unwrap();
assert!(bridge_descriptor.sanity_check().is_ok());
println!(
"Bridge pubkey script: {}",
bridge_descriptor.script_pubkey()
);
println!(
"Bridge address: {}",
bridge_descriptor.address(&ELEMENTS_PARAMS).unwrap()
);
println!(
"Weight for witness satisfaction cost {}",
bridge_descriptor.max_weight_to_satisfy().unwrap()
);
let master_private_key_str = "cQhdvB3McbBJdx78VSSumqoHQiSXs75qwLptqwxSQBNBMDxafvaw";
let _master_private_key =
PrivateKey::from_str(master_private_key_str).expect("Can't create private key");
println!(
"Master public key: {}",
_master_private_key.public_key(&secp256k1)
);
let backup1_private_key_str = "cWA34TkfWyHa3d4Vb2jNQvsWJGAHdCTNH73Rht7kAz6vQJcassky";
let backup1_private =
PrivateKey::from_str(backup1_private_key_str).expect("Can't create private key");
println!(
"Backup1 public key: {}",
backup1_private.public_key(&secp256k1)
);
let backup2_private_key_str = "cPJFWUKk8sdL7pcDKrmNiWUyqgovimmhaaZ8WwsByDaJ45qLREkh";
let backup2_private =
PrivateKey::from_str(backup2_private_key_str).expect("Can't create private key");
println!(
"Backup2 public key: {}",
backup2_private.public_key(&secp256k1)
);
let backup3_private_key_str = "cT5cH9UVm81W5QAf5KABXb23RKNSMbMzMx85y6R2mF42L94YwKX6";
let _backup3_private =
PrivateKey::from_str(backup3_private_key_str).expect("Can't create private key");
println!(
"Backup3 public key: {}",
_backup3_private.public_key(&secp256k1)
);
let spend_tx = Transaction {
version: 2,
lock_time: LockTime::from_height(5000).unwrap(),
input: vec![],
output: vec![],
};
let mut psbt = Psbt::from_tx(spend_tx);
let receiver =
Address::from_str("ert1qpq2cfgz5lktxzr5zqv7nrzz46hsvq3492ump9pz8rzcl8wqtwqcs2yqnuv")
.unwrap();
let amount = 100000000;
let outpoint = elements::OutPoint {
txid: elements::Txid::from_str(
"7a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de",
)
.unwrap(),
vout: 0,
};
let witness_utxo = bitcoin_asset_txout(bridge_descriptor.script_pubkey(), amount);
let txin = TxIn {
previous_output: outpoint,
sequence: Sequence::from_height(26), ..TxIn::default()
};
psbt.add_input(pset::Input::from_txin(txin));
psbt.add_output(pset::Output::from_txout(bitcoin_asset_txout(
receiver.script_pubkey(),
amount / 5 - 500,
)));
psbt.add_output(pset::Output::from_txout(bitcoin_asset_txout(
bridge_descriptor.script_pubkey(),
amount * 4 / 5,
)));
psbt.add_output(pset::Output::from_txout(bitcoin_asset_txout(
Script::new(),
500,
)));
psbt.inputs_mut()[0]
.update_with_descriptor_unchecked(&bridge_descriptor)
.unwrap();
psbt.inputs_mut()[0].witness_utxo = Some(witness_utxo);
let tx = &psbt.extract_tx().unwrap();
let mut sighash_cache = SighashCache::new(tx);
let genesis_hash = elements::BlockHash::all_zeros();
let msg = psbt
.sighash_msg(0, &mut sighash_cache, None, genesis_hash)
.unwrap()
.to_secp_msg();
let hash_ty = elements::EcdsaSighashType::All;
let sk1 = backup1_private.inner;
let sk2 = backup2_private.inner;
let sig1 = secp256k1.sign_ecdsa(&msg, &sk1);
let pk1 = backup1_private.public_key(&secp256k1);
assert!(secp256k1.verify_ecdsa(&msg, &sig1, &pk1.inner).is_ok());
let sig2 = secp256k1.sign_ecdsa(&msg, &sk2);
let pk2 = backup2_private.public_key(&secp256k1);
assert!(secp256k1.verify_ecdsa(&msg, &sig2, &pk2.inner).is_ok());
psbt.inputs_mut()[0]
.partial_sigs
.insert(pk1, elementssig_to_rawsig(&(sig1, hash_ty)));
println!("{:#?}", psbt);
let serialized = serialize(&psbt);
println!("{}", base64::encode(serialized));
psbt.finalize_mut(&secp256k1, genesis_hash).unwrap();
println!("{:#?}", psbt);
let tx = psbt.extract_tx().unwrap();
println!("{}", serialize_hex(&tx));
}
#[allow(dead_code)]
fn get_vout(tx: &Transaction, spk: Script) -> (OutPoint, TxOut) {
for (i, txout) in tx.clone().output.into_iter().enumerate() {
if spk == txout.script_pubkey {
return (OutPoint::new(tx.txid(), i as u32), txout);
}
}
panic!("Only call get vout on functions which have the expected outpoint");
}
fn bitcoin_asset_txout(spk: Script, amt: u64) -> TxOut {
TxOut {
script_pubkey: spk,
value: confidential::Value::Explicit(amt),
asset: confidential::Asset::Explicit(
AssetId::from_str("088f6b381694259fd20599e71f7eb46e392f36b43cc20d131d95c8a4b8cc1aa8")
.unwrap(),
),
nonce: confidential::Nonce::Null,
witness: TxOutWitness::default(),
}
}