Created
April 22, 2021 15:56
-
-
Save chris-belcher/7988c798292883fd8421c6fa6b846ef5 to your computer and use it in GitHub Desktop.
rust bitcoin play teleport coinswap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 22/4/2021 | |
// random various unrelated functions coded to help me figure out how to use rust-bitcoin | |
// should be useful for figuring out why certain things in teleport are coded the way they are | |
extern crate bitcoincore_rpc; | |
use bitcoincore_rpc::{Client, Error, RpcApi, Auth}; | |
extern crate bitcoin_wallet; | |
use bitcoin_wallet::account::{ | |
MasterAccount, Unlocker, Account, AccountAddressType | |
}; | |
use bitcoin_wallet::mnemonic; | |
extern crate bitcoin; | |
use bitcoin::Network; | |
use bitcoin::{OutPoint, TxIn, TxOut, Transaction, Address, Script, Txid, | |
SigHashType}; | |
use bitcoin::hashes::hex::{FromHex, ToHex}; | |
use bitcoin::consensus::encode::{serialize_hex, deserialize}; | |
use bitcoin::blockdata::script::Builder; | |
use bitcoin::blockdata::opcodes; | |
use bitcoin::util::bip32; | |
use bitcoin::util::key::{PrivateKey, PublicKey}; | |
use bitcoin::util::bip143; | |
use bitcoin::util::psbt::serialize::Serialize; | |
use bitcoin::hashes::hash160::Hash as Hash160; | |
use bitcoin::hashes::Hash; | |
use serde_json; | |
use bitcoin::secp256k1::{SecretKey, Signature}; | |
extern crate secp256k1; | |
use secp256k1::Secp256k1; | |
use std::str::FromStr; | |
use std::collections::HashMap; | |
use std::fs::File; | |
fn sign_tx() { | |
const SEED_PHRASE: &str = | |
"great rice pitch bitter stay crash among position disease enable sell road"; | |
const EXTENSION: &str = "helloworld"; | |
//root key according to the bip39.org site | |
// xprv9s21ZrQH143K3x8kThmrmvTXGthvazWEVMuM18Rfz9cBijs5krbwsxSmU6PJTqQtYCb4EjinKaQzvaGQMXa45gckv1GBbahu5eh2ZWeppGA | |
//importing seed into electrum does indeed give the same address as below | |
// m/44'/1'/0' | |
//addr(0, 0) = 1HVyNRjLPQiWti9P8umBv2AfYP2cSHEXNo | |
const PASSPHRASE: &str = ""; | |
let mut master = MasterAccount::from_mnemonic( | |
&mnemonic::Mnemonic::from_str(SEED_PHRASE).unwrap(), | |
0, | |
Network::Regtest, | |
PASSPHRASE, | |
Some(EXTENSION) | |
).unwrap(); | |
let mut unlocker = Unlocker::new_for_master(&master, PASSPHRASE).unwrap(); | |
let p2pkh_account = Account::new(&mut unlocker, AccountAddressType::P2PKH, | |
0, 0, 10).unwrap(); | |
master.add_account(p2pkh_account); | |
let xpub = master.get_mut((0, 0)).unwrap().master_public(); | |
println!("xpub = {}", xpub); | |
//xpub = tpubDE5vtSEeHLG1iGPveZffKaMJsFsr5ijqb8rcZ3ex6NxwaGn58TD522jAyi3JsAWcYUBDXPGcrs6AB8s43zxkph9ou4t4HHAQvoJSv8SNaKE | |
//addresses from this xpub are simply in m/0, m/1, m/2, etc | |
//so this could be used in a descriptor | |
let p2pkh_addr_0 = master.get_mut((0, 0)).unwrap().next_key().unwrap() | |
.address.clone(); | |
println!("addr = {}", p2pkh_addr_0); | |
//address = mzxWmxuY352Lw4FadGmiDNndnPg34h5P6V | |
//sent 1 btc to it on regtest | |
let mut spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: Txid::from_hex("07d3e8721234468fd0ca51247a79d180dcbcaf81370cde089847a9ba698b9fcc").unwrap(), | |
vout: 0 | |
}, | |
sequence: 0, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("mzxWmxuY352Lw4FadGmiDNndnPg34h5P6V") | |
.unwrap().script_pubkey(), | |
value: 99950000, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
let prev_out = TxOut { | |
script_pubkey: Address::from_str("mzxWmxuY352Lw4FadGmiDNndnPg34h5P6V") | |
.unwrap().script_pubkey(), | |
value: 100000000 | |
}; | |
master.sign(&mut spending_tx, SigHashType::All, &|_| Some(prev_out.clone()), | |
&mut unlocker).expect("cannot sign"); | |
println!("fully signed txid = {}", spending_tx.txid()); | |
let txhex = serialize_hex(&spending_tx); | |
println!("txhex = {}", txhex); | |
//output | |
//fully signed txid = c1e6276e808feaea39eb914c22d1f9ec9ab08f124f03c8254c399b98811e13a1 | |
//txhex = 0200000001cc9f8b69baa9479808de0c3781afbcdc80d1797a2451cad08f46341272e8d307000000006b483045022100bdc5f767fe66218279d29523294d1741ef50e220d55677b5aad512ad30da84bb02206684bfc5996e27866dfa0eaa61a1fc99ccc8f0200062c2733d8b33e65463390101210306d4bb7fcee609d18f87fe34c3d7cc9e29d193beecccebdb4a3792f50ad3007c0000000001b01df505000000001976a914d53fe723f631c7c355986d3684223a666557591388ac00000000 | |
//passing to RPC | |
/* | |
testmempoolaccept "[\"0200000001cc9f8b69baa9479808de0c3781afbcdc80d1797a2451cad08f46341272e8d307000000006b483045022100bdc5f767fe66218279d29523294d1741ef50e220d55677b5aad512ad30da84bb02206684bfc5996e27866dfa0eaa61a1fc99ccc8f0200062c2733d8b33e65463390101210306d4bb7fcee609d18f87fe34c3d7cc9e29d193beecccebdb4a3792f50ad3007c0000000001b01df505000000001976a914d53fe723f631c7c355986d3684223a666557591388ac00000000\"]" | |
[ | |
{ | |
"txid": "c1e6276e808feaea39eb914c22d1f9ec9ab08f124f03c8254c399b98811e13a1", | |
"allowed": true | |
} | |
] | |
*/ | |
//tx is valid, and also results in the same txid calculated by the library | |
} | |
fn generate_without_rustwallet_masteraccount() { | |
const SEED_PHRASE: &str = | |
"great rice pitch bitter stay crash among position disease enable sell road"; | |
const EXTENSION: &str = "helloworld"; | |
let seed = mnemonic::Mnemonic::from_str(SEED_PHRASE).unwrap() | |
.to_seed(Some(EXTENSION)); | |
let xprv = bip32::ExtendedPrivKey::new_master(Network::Bitcoin, &seed.0) | |
.unwrap(); | |
println!("xprv_seed = {}", xprv); | |
//this xprv comes from putting the above seed phrase into bip39.org | |
let xprv_str = "xprv9s21ZrQH143K3x8kThmrmvTXGthvazWEVMuM18Rfz9cBijs5krbwsxSmU6PJTqQtYCb4EjinKaQzvaGQMXa45gckv1GBbahu5eh2ZWeppGA"; | |
println!(" xprv_str = {}", xprv_str); | |
//check that they are the same | |
} | |
fn sign_tx_from_indium_test_wallet() { | |
const SEED_PHRASE: &str = | |
"finger topple before stock embrace liar air now trouble beauty forum protect"; | |
const EXTENSION: &str = "www"; | |
const PASSPHRASE: &str = ""; | |
let mut master = MasterAccount::from_mnemonic( | |
&mnemonic::Mnemonic::from_str(SEED_PHRASE).unwrap(), | |
0, | |
Network::Regtest, | |
PASSPHRASE, | |
Some(EXTENSION) | |
).unwrap(); | |
let mut unlocker = Unlocker::new_for_master(&master, PASSPHRASE).unwrap(); | |
let p2pkh_account = Account::new(&mut unlocker, AccountAddressType::P2WPKH, | |
0, 0, 10).unwrap(); | |
master.add_account(p2pkh_account); | |
let p2pkh_addr_0 = master.get_mut((0, 0)).unwrap().next_key().unwrap() | |
.address.clone(); | |
println!("addr = {}", p2pkh_addr_0); | |
//address = bcrt1q7llc9avh7tklhcq6wucj5jss2jn69dcte4n4pq | |
/* | |
let mut spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: Txid::from_hex("06941b3a6b65860877a5459819269c35715868b545771e0b14db496854d831b7").unwrap(), | |
vout: 1 | |
}, | |
sequence: 0, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("mzxWmxuY352Lw4FadGmiDNndnPg34h5P6V") | |
.unwrap().script_pubkey(), | |
value: 9995000, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
*/ | |
let mut spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: Txid::from_hex("06941b3a6b65860877a5459819269c35715868b545771e0b14db496854d831b7").unwrap(), | |
vout: 1 | |
}, | |
sequence: 0, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("2Mvp1FFijppV7HfQLTJ11CHoRqpAJi8qXbf") | |
.unwrap().script_pubkey(), | |
value: 5000000, | |
}, | |
TxOut { | |
script_pubkey: Address | |
::from_str("2Mvp1FFijppV7HfQLTJ11CHoRqpAJi8qXbf") | |
.unwrap().script_pubkey(), | |
value: 4998000, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
let prev_out = TxOut { | |
script_pubkey: Address::from_str( | |
"bcrt1q7llc9avh7tklhcq6wucj5jss2jn69dcte4n4pq") | |
.unwrap() | |
.script_pubkey(), | |
value: 10000000 | |
}; | |
master.sign(&mut spending_tx, SigHashType::All, &|_| Some(prev_out.clone()), | |
&mut unlocker).expect("cannot sign"); | |
let sighash = spending_tx.signature_hash(0, &prev_out.script_pubkey, | |
SigHashType::All as u32); | |
println!("sighash = {}", sighash); | |
println!("fully signed txid = {}", spending_tx.txid()); | |
let txhex = serialize_hex(&spending_tx); | |
println!("txhex = {}", txhex); | |
} | |
fn do_bip32() { | |
//let seed: [u8; 32] = [0; 32]; | |
//let seed = Vec::from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); | |
//possibly use: | |
//extern crate rand; use rand::OsRng; | |
//let mut rng = OsRng::new().expect("OsRng"); | |
//let xprv = bip32::ExtendedPrivKey::new_master(Network::Bitcoin, &seed).unwrap(); | |
let xprv = bip32::ExtendedPrivKey::from_str( | |
"xprv9s21ZrQH143K2JF8RafpqtKiTbsbaxEeUaMnNHsm5o6wCW3z8ySyH4UxFVSfZ8n7ESu7fgir8imbZKLYVBxFPND1pniTZ81vKfd45EHKX73" | |
).unwrap(); | |
println!("xprv = {}", xprv); | |
println!("deserialized xprv = {:#?}", xprv); | |
let secp = Secp256k1::new(); | |
let child_key = xprv.derive_priv(&secp, | |
&bip32::DerivationPath::from_str("m/0'/0").unwrap()).unwrap(); | |
println!("child_key = {}", child_key); | |
//if the xprv is obtained above with from_str() then this should be | |
//xprv9wHokC2KXdTSpEepFcu53hMDUHYfAtTaLEJEMyxBPAMf78hJg17WhL5FyeDUQH5KWmGjGgEb2j74gsZqgupWpPbZgP6uFmP8MYEy5BNbyET | |
println!("child privkey = {}", child_key.private_key); | |
} | |
fn rpc_descriptors() -> Result<(), Error> { | |
let auth = Auth::UserPass( | |
"regtestrpcuser".to_string(), | |
"regtestrpcpass".to_string() | |
); | |
let rpc = Client::new( | |
"http://localhost:18443/wallet/wallet.dat".to_string(), auth)?; | |
let info = rpc.get_blockchain_info()?; | |
//println!("info = {:#?}", info); | |
println!("best block hash = {}", info.best_block_hash); | |
let txes = rpc.list_transactions(Some("*"), Some(1), Some(0), | |
Some(true)).unwrap(); | |
println!("txes = {:#?}", txes); | |
let xpub = bip32::ExtendedPubKey::from_str( | |
"tpubDE5vtSEeHLG1iGPveZffKaMJsFsr5ijqb8rcZ3ex6NxwaGn58TD522jAyi3JsAWcYUBDXPGcrs6AB8s43zxkph9ou4t4HHAQvoJSv8SNaKE" | |
).unwrap(); | |
println!("xpub = {:?}", xpub); | |
//ill leave it, its obviously going to work | |
//have the xpub | |
//use deriveaddress to get an address | |
//check if its imported | |
//if not, use importmulti | |
//check the m/x number | |
//check if its paid to | |
// see if theres any info added to listtransactions or whatever | |
Ok(()) | |
} | |
fn create_and_spend_from_2of2_multisig() -> Result<(), Error> { | |
//creating multisig redeem scripts | |
//https://github.com/rust-bitcoin/rust-lightning/blob/22a0dd5f339058fd6733920ffca0f5eb64db4e32/lightning/src/routing/network_graph.rs#L104 | |
//ideas for how to sign here | |
// https://github.com/rust-bitcoin/rust-lightning/blob/master/lightning/src/chain/keysinterface.rs | |
//see under InMemoryChannelKeys | |
//create two private keys, and convert to public keys | |
//from those create address, print it | |
//check if address is funded with listunspent | |
// if not then exit | |
//spend from address | |
let secp = Secp256k1::new(); | |
let priv_a = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xab; 32]).unwrap() | |
}; | |
let pub_a = priv_a.public_key(&secp); | |
let priv_b = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xcd; 32]).unwrap() | |
}; | |
let pub_b = priv_b.public_key(&secp); | |
println!("priv_a = {}", priv_a); | |
println!("priv_b = {}", priv_b); | |
println!("pub_a = {}", pub_a); | |
println!("pub_b = {}", pub_b); | |
let redeemscript = Builder::new() | |
.push_opcode(opcodes::all::OP_PUSHNUM_2) | |
.push_key(&pub_a) | |
.push_key(&pub_b) | |
.push_opcode(opcodes::all::OP_PUSHNUM_2) | |
.push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script(); | |
println!("redeemscript hex = {:x}", redeemscript); | |
let addr = Address::p2wsh(&redeemscript, Network::Regtest); | |
println!("addr = {}", addr); | |
let auth = Auth::UserPass( | |
"regtestrpcuser".to_string(), | |
"regtestrpcpass".to_string() | |
); | |
let rpc = Client::new( | |
"http://localhost:18443/wallet/wallet.dat".to_string(), auth)?; | |
let utxos = rpc.list_unspent(Some(0), Some(999999), Some(&[&addr]), | |
None, None)?; | |
if utxos.len() != 1 { | |
println!("wrong number of coins on address, exiting"); | |
return Ok(()) | |
} | |
let utxo = &utxos[0]; | |
println!("utxo = {:#?}", utxo); | |
let output_value = utxo.amount.as_sat() - 50000; | |
let gettx = rpc.get_transaction(&utxo.txid, Some(true)).unwrap(); | |
println!("gettx = {:?}", gettx); | |
let funding_tx = deserialize::<Transaction>(&gettx.hex).unwrap(); | |
println!("funding_tx = {:?}", funding_tx); | |
let mut spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: utxo.txid, | |
vout: utxo.vout | |
}, | |
sequence: 0, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("2Mvp1FFijppV7HfQLTJ11CHoRqpAJi8qXbf") | |
.unwrap().script_pubkey(), | |
value: output_value, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
let sighash = secp256k1::Message::from_slice( | |
&bip143::SighashComponents::new(&spending_tx).sighash_all( | |
&spending_tx.input[0], &redeemscript, utxo.amount.as_sat())[..]) | |
.unwrap(); | |
println!("sighash = {:?}", sighash); | |
let sig_a = secp.sign(&sighash, &priv_a.key); | |
println!("sig_a = {}", sig_a); | |
let sig_b = secp.sign(&sighash, &priv_b.key); | |
println!("sig_b = {}", sig_b); | |
spending_tx.input[0].witness.push(Vec::new()); //first is multisig dummy | |
spending_tx.input[0].witness.push(sig_a.serialize_der().to_vec()); | |
spending_tx.input[0].witness.push(sig_b.serialize_der().to_vec()); | |
spending_tx.input[0].witness[1].push(SigHashType::All as u8); | |
spending_tx.input[0].witness[2].push(SigHashType::All as u8); | |
spending_tx.input[0].witness.push(redeemscript.into_bytes()); | |
println!("fully signed txid = {}", spending_tx.txid()); | |
let txhex = serialize_hex(&spending_tx); | |
println!("txhex = {}", txhex); | |
let accepted = rpc.test_mempool_accept(&[txhex]); | |
println!("testmempoolaccept = {:?}", accepted); | |
Ok(()) | |
} | |
fn create_and_spend_from_htlc() -> Result<(), Error> { | |
//create htlc redeem script | |
// https://github.com/rust-bitcoin/rust-lightning/blob/7dca3a9e29841d1d50f5b5f5ca226d65d7420710/lightning/src/ln/chan_utils.rs#L374 | |
/* | |
OP_HASH160 | |
H(X) | |
OP_EQUAL | |
OP_IF | |
pub_hashlock | |
OP_ELSE | |
locktime | |
OP_CHECKSEQUENCEVERIFY | |
OP_DROP | |
pub_timelock | |
OP_ENDIF | |
OP_CHECKSIG | |
*/ | |
//spent with witnesses: | |
//hashlock case: | |
//<hashlock_signature> <preimage> | |
//timelock case: | |
//<timelock_signature> <empty> | |
let secp = Secp256k1::new(); | |
let priv_hashlock = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xcd; 32]).unwrap() | |
}; | |
let pub_hashlock = priv_hashlock.public_key(&secp); | |
println!("hashlock pub key = {}", pub_hashlock); | |
let priv_timelock = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xab; 32]).unwrap() | |
}; | |
let pub_timelock = priv_timelock.public_key(&secp); | |
println!("timelock pub key = {}", pub_timelock); | |
let preimage = [0xef; 32]; | |
println!("preimage = {:?}", preimage); | |
let hashvalue = Hash160::hash(&preimage); | |
println!("hashvalue = {:?}", hashvalue); | |
let hashvalue = hashvalue.into_inner(); | |
let locktime = 10; //blocks | |
let redeemscript = Builder::new() | |
.push_opcode(opcodes::all::OP_HASH160) | |
.push_slice(&hashvalue[..]) | |
.push_opcode(opcodes::all::OP_EQUAL) | |
.push_opcode(opcodes::all::OP_IF) | |
.push_key(&pub_hashlock) | |
.push_opcode(opcodes::all::OP_ELSE) | |
.push_int(locktime) | |
.push_opcode(opcodes::all::OP_CSV) | |
.push_opcode(opcodes::all::OP_DROP) | |
.push_key(&pub_timelock) | |
.push_opcode(opcodes::all::OP_ENDIF) | |
.push_opcode(opcodes::all::OP_CHECKSIG) | |
.into_script(); | |
println!("redeemscript hex = {:x}", redeemscript); | |
println!("redeemscript len = {}", redeemscript.len()); | |
let addr = Address::p2wsh(&redeemscript, Network::Regtest); | |
println!("addr = {}", addr); | |
let auth = Auth::UserPass( | |
"regtestrpcuser".to_string(), | |
"regtestrpcpass".to_string() | |
); | |
let rpc = Client::new( | |
"http://localhost:18443/wallet/wallet.dat".to_string(), auth)?; | |
let utxos = rpc.list_unspent(Some(0), Some(999999), Some(&[&addr]), | |
None, None)?; | |
if utxos.len() != 1 { | |
println!("wrong number of coins on address, exiting"); | |
return Ok(()) | |
} | |
let utxo = &utxos[0]; | |
println!("utxo = {:#?}", utxo); | |
//spend by providing a hash value | |
let mut hash_spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: utxo.txid, | |
vout: utxo.vout | |
}, | |
sequence: 0, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("2Mvp1FFijppV7HfQLTJ11CHoRqpAJi8qXbf") | |
.unwrap().script_pubkey(), | |
value: utxo.amount.as_sat() - 50000, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
let sighash = secp256k1::Message::from_slice( | |
&bip143::SighashComponents::new(&hash_spending_tx).sighash_all( | |
&hash_spending_tx.input[0], &redeemscript, utxo.amount.as_sat())[..]) | |
.unwrap(); | |
let sig_hashlock = secp.sign(&sighash, &priv_hashlock.key); | |
println!("sig_hashlock = {}", sig_hashlock); | |
hash_spending_tx.input[0].witness.push(sig_hashlock.serialize_der() | |
.to_vec()); | |
hash_spending_tx.input[0].witness[0].push(SigHashType::All as u8); | |
hash_spending_tx.input[0].witness.push(preimage.to_vec()); | |
hash_spending_tx.input[0].witness.push(redeemscript.as_bytes().to_vec()); | |
println!("fully signed txid = {}", hash_spending_tx.txid()); | |
let txhex = serialize_hex(&hash_spending_tx); | |
println!("txhex = {}", txhex); | |
let accepted = rpc.test_mempool_accept(&[txhex]); | |
println!("testmempoolaccept = {:?}", accepted); | |
//spend with the timelock | |
let mut time_spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: utxo.txid, | |
vout: utxo.vout | |
}, | |
sequence: locktime as u32, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("2Mvp1FFijppV7HfQLTJ11CHoRqpAJi8qXbf") | |
.unwrap().script_pubkey(), | |
value: utxo.amount.as_sat() - 50000, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
let sighash = secp256k1::Message::from_slice( | |
&bip143::SighashComponents::new(&time_spending_tx).sighash_all( | |
&time_spending_tx.input[0], &redeemscript, utxo.amount.as_sat())[..]) | |
.unwrap(); | |
let sig_timelock = secp.sign(&sighash, &priv_timelock.key); | |
println!("sig_timelock = {}", sig_timelock); | |
time_spending_tx.input[0].witness.push(sig_timelock.serialize_der() | |
.to_vec()); | |
time_spending_tx.input[0].witness[0].push(SigHashType::All as u8); | |
time_spending_tx.input[0].witness.push(Vec::new()); | |
time_spending_tx.input[0].witness.push(redeemscript.into_bytes()); | |
println!("fully signed txid = {}", time_spending_tx.txid()); | |
let txhex = serialize_hex(&time_spending_tx); | |
println!("txhex = {}", txhex); | |
let accepted = rpc.test_mempool_accept(&[txhex]); | |
println!("testmempoolaccept = {:?}", accepted); | |
Ok(()) | |
} | |
#[allow(non_snake_case)] | |
fn create_tweaked_pubkey() { | |
/* | |
q = EC privkey generated by maker | |
Q = q.G = EC pubkey published by maker | |
p = nonce generated by taker | |
P = p.G = nonce point calculated by taker | |
R = Q + P = pubkey used in bitcoin transaction | |
= (q + p).G | |
*/ | |
let secp = Secp256k1::new(); | |
let q_n = [0x44; 32]; | |
//let q = | |
let mut q = secp256k1::SecretKey::from_slice(&q_n).unwrap(); | |
println!("q = {:?}", q); | |
let Q = secp256k1::PublicKey::from_secret_key(&secp, &q); | |
println!("Q = {}", Q); | |
let p_n = [0x22; 32]; | |
let p = secp256k1::SecretKey::from_slice(&p_n).unwrap(); | |
println!("p = {:?}", p); | |
let P = secp256k1::PublicKey::from_secret_key(&secp, &p); | |
println!("P = {}", P); | |
let R_taker = Q.combine(&P).unwrap(); | |
println!("R_taker = {}", R_taker); | |
q.add_assign(&p_n[..]).unwrap(); | |
let R_maker = secp256k1::PublicKey::from_secret_key(&secp, &q); | |
println!("R_maker = {}", R_maker); | |
} | |
fn get_tweakable_pubkey() { | |
const SEED_PHRASE: &str = | |
"great rice pitch bitter stay crash among position disease enable sell road"; | |
const EXTENSION: &str = "helloworld"; | |
//root key according to the bip39.org site | |
// xprv9s21ZrQH143K3x8kThmrmvTXGthvazWEVMuM18Rfz9cBijs5krbwsxSmU6PJTqQtYCb4EjinKaQzvaGQMXa45gckv1GBbahu5eh2ZWeppGA | |
//importing seed into electrum does indeed give the same address as below | |
// m/44'/1'/0' | |
//addr(0, 0) = 1HVyNRjLPQiWti9P8umBv2AfYP2cSHEXNo | |
const PASSPHRASE: &str = ""; | |
let master = MasterAccount::from_mnemonic( | |
&mnemonic::Mnemonic::from_str(SEED_PHRASE).unwrap(), | |
0, | |
Network::Regtest, | |
PASSPHRASE, | |
Some(EXTENSION) | |
).unwrap(); | |
let unlocker = Unlocker::new_for_master(&master, PASSPHRASE).unwrap(); | |
println!("master private key = {}\n = {:?}", unlocker.master_private(), | |
unlocker.master_private()); | |
let tweakable_privkey = unlocker.context().private_child( | |
unlocker.master_private(), | |
bip32::ChildNumber::from_hardened_idx(0).unwrap() | |
).unwrap().private_key; | |
println!("tweakable_privkey = {}", tweakable_privkey); | |
let secp = Secp256k1::new(); | |
let tweakable_pubkey = tweakable_privkey.public_key(&secp); | |
println!("tweakable_pubkey = {}", tweakable_pubkey); | |
} | |
fn create_and_spend_from_new_htlc_plus_tests() -> Result<(), Error> { | |
//new htlc refers to the script which fixes the oversize preimage attack | |
let secp = Secp256k1::new(); | |
let priv_hashlock = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xcd; 32]).unwrap() | |
}; | |
let pub_hashlock = priv_hashlock.public_key(&secp); | |
let priv_timelock = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xab; 32]).unwrap() | |
}; | |
let pub_timelock = priv_timelock.public_key(&secp); | |
let preimage = [0xef; 32]; | |
let hashvalue = Hash160::hash(&preimage); | |
let hashvalue = hashvalue.into_inner(); | |
let locktime = 80; //blocks | |
//this way avoids the malleability from OP_IF | |
//https://lists.linuxfoundation.org/pipermail/lightning-dev/2016-September/000605.html | |
//the attack here is that OP_IF accepts anything nonzero as true, so someone | |
// could replace the argument with something much bigger, which would | |
// reduce the tx fee rate, the solution is to only use OP_IF after OP_EQUAL | |
//27-10-2020 | |
//oversize preimage attack | |
//https://lists.linuxfoundation.org/pipermail/lightning-dev/2016-May/000529.html | |
//the purpose is also to prevent the attack reducing the fee rate of the | |
// transaction, possibly making it not get mined | |
//one naive solution is OP_SIZE 32 OP_EQUALVERIFY | |
// but then you force even the locktime case to waste 32 bytes of witness | |
//so we use this script which requires size zero for the locktime branch | |
//we also want the hashlock case to be locked with 1 OP_CSV | |
//which disables CPFP and therefore avoids transaction pinning | |
//see https://bitcoinops.org/en/topics/transaction-pinning/ | |
/* | |
opcodes | stack after execution | |
| | |
| <sig> <preimage> | |
OP_SIZE | <sig> <preimage> <size> | |
OP_SWAP | <sig> <size> <preimage> | |
OP_HASH160 | <sig> <size> <hash> | |
H(X) | <sig> <size> <hash> H(X) | |
OP_EQUAL | <sig> <size> 1|0 | |
OP_IF | | |
pub_hashlock | <sig> <size> <pub> | |
32 | <sig> <size> <pub> 32 | |
1 | <sig> <size> <pub> 32 1 | |
OP_ELSE | | |
pub_timelock | <sig> <size> <pub> | |
0 | <sig> <size> <pub> 0 | |
locktime | <sig> <size> <pub> 0 <locktime> | |
OP_ENDIF | | |
OP_CHECKSEQUENCEVERIFY | <sig> <size> <pub> (32|0) (1|<locktime>) | |
OP_DROP | <sig> <size> <pub> (32|0) | |
OP_ROT | <sig> <pub> (32|0) <size> | |
OP_EQUALVERIFY | <sig> <pub> | |
OP_CHECKSIG | true|false | |
*/ | |
//spent with witnesses: | |
//hashlock case: | |
//<hashlock_signature> <preimage> | |
//timelock case: | |
//<timelock_signature> <empty_vector> | |
let redeemscript = Builder::new() | |
.push_opcode(opcodes::all::OP_SIZE) | |
.push_opcode(opcodes::all::OP_SWAP) | |
.push_opcode(opcodes::all::OP_HASH160) | |
.push_slice(&hashvalue[..]) | |
.push_opcode(opcodes::all::OP_EQUAL) | |
.push_opcode(opcodes::all::OP_IF) | |
.push_key(&pub_hashlock) | |
.push_int(32) | |
.push_int(1) | |
.push_opcode(opcodes::all::OP_ELSE) | |
.push_key(&pub_timelock) | |
.push_int(0) | |
.push_int(locktime) | |
.push_opcode(opcodes::all::OP_ENDIF) | |
.push_opcode(opcodes::all::OP_CSV) | |
.push_opcode(opcodes::all::OP_DROP) | |
.push_opcode(opcodes::all::OP_ROT) | |
.push_opcode(opcodes::all::OP_EQUALVERIFY) | |
.push_opcode(opcodes::all::OP_CHECKSIG) | |
.into_script(); | |
println!("redeemscript hex = {:x}", redeemscript); | |
println!("redeemscript len = {}", redeemscript.len()); | |
let addr = Address::p2wsh(&redeemscript, Network::Regtest); | |
println!("addr = {}", addr); | |
//importaddress | |
let auth = Auth::UserPass( | |
"regtestrpcuser".to_string(), | |
"regtestrpcpass".to_string() | |
); | |
let rpc = Client::new( | |
"http://localhost:18443/wallet/wallet.dat".to_string(), auth)?; | |
let utxos = rpc.list_unspent(Some(0), Some(999999), Some(&[&addr]), | |
None, None)?; | |
if utxos.len() != 1 { | |
println!("wrong number of coins on address, exiting"); | |
return Ok(()) | |
} | |
let utxo = &utxos[0]; | |
println!("utxo = {:#?}", utxo); | |
//unsigned spending tx | |
let spending_tx = Transaction { | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: utxo.txid, | |
vout: utxo.vout | |
}, | |
sequence: locktime as u32, | |
witness: Vec::new(), | |
script_sig: Script::new() | |
} | |
], | |
output: vec![ | |
TxOut { | |
script_pubkey: Address | |
::from_str("2Mvp1FFijppV7HfQLTJ11CHoRqpAJi8qXbf") | |
.unwrap().script_pubkey(), | |
value: utxo.amount.as_sat() - 50000, | |
} | |
], | |
lock_time: 0, | |
version: 2, | |
}; | |
//spend validly with hashlock | |
let mut hash_spending_tx = spending_tx.clone(); | |
hash_spending_tx.input[0].sequence = 1; | |
let hash_sighash = secp256k1::Message::from_slice( | |
&bip143::SighashComponents::new(&hash_spending_tx).sighash_all( | |
&hash_spending_tx.input[0], &redeemscript, utxo.amount.as_sat() | |
)[..] | |
).unwrap(); | |
let sig_hashlock = secp.sign(&hash_sighash, &priv_hashlock.key); | |
hash_spending_tx.input[0].witness.push(sig_hashlock.serialize_der() | |
.to_vec()); | |
hash_spending_tx.input[0].witness[0].push(SigHashType::All as u8); | |
hash_spending_tx.input[0].witness.push(preimage.to_vec()); | |
hash_spending_tx.input[0].witness.push(redeemscript.as_bytes().to_vec()); | |
println!("hash spending txid = {}", hash_spending_tx.txid()); | |
let txhex = serialize_hex(&hash_spending_tx); | |
let accepted = rpc.test_mempool_accept(&[txhex]).unwrap(); | |
println!("testmempoolaccept = {:?}", accepted); | |
let time_sighash = secp256k1::Message::from_slice( | |
&bip143::SighashComponents::new(&spending_tx).sighash_all( | |
&spending_tx.input[0], &redeemscript, utxo.amount.as_sat() | |
)[..] | |
).unwrap(); | |
//spend validly with timelock | |
let sig_timelock = secp.sign(&time_sighash, &priv_timelock.key); | |
let mut time_spending_tx = spending_tx.clone(); | |
time_spending_tx.input[0].witness.push(sig_timelock.serialize_der() | |
.to_vec()); | |
time_spending_tx.input[0].witness[0].push(SigHashType::All as u8); | |
time_spending_tx.input[0].witness.push(Vec::new()); | |
time_spending_tx.input[0].witness.push(redeemscript.as_bytes().to_vec()); | |
println!("time spending txid = {}", time_spending_tx.txid()); | |
let txhex = serialize_hex(&time_spending_tx); | |
let accepted = rpc.test_mempool_accept(&[txhex]).unwrap(); | |
println!("testmempoolaccept = {:?}", accepted); | |
//spend invalidly with timelock but non-empty preimage | |
let sig_timelock = secp.sign(&time_sighash, &priv_timelock.key); | |
let mut time_spending_tx = spending_tx.clone(); | |
time_spending_tx.input[0].witness.push(sig_timelock.serialize_der() | |
.to_vec()); | |
time_spending_tx.input[0].witness[0].push(SigHashType::All as u8); | |
let non_preimage = [0xef; 1]; | |
time_spending_tx.input[0].witness.push(non_preimage.to_vec()); | |
time_spending_tx.input[0].witness.push(redeemscript.as_bytes().to_vec()); | |
println!("invalid time spending txid = {}", time_spending_tx.txid()); | |
let txhex = serialize_hex(&time_spending_tx); | |
let accepted = rpc.test_mempool_accept(&[txhex]).unwrap(); | |
println!("testmempoolaccept = {:?}", accepted); | |
Ok(()) | |
} | |
fn pattern_match_contract() { | |
let secp = Secp256k1::new(); | |
let pub_hashlock = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xcd; 32]).unwrap() | |
}.public_key(&secp); | |
let pub_timelock = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xab; 32]).unwrap() | |
}.public_key(&secp); | |
println!("pub_hashlock = {}", pub_hashlock); | |
println!("pub_timelock = {}", pub_timelock); | |
let preimage = [0xef; 32]; | |
let hashvalue = Hash160::hash(&preimage); | |
let hashvalue = hashvalue.into_inner(); | |
println!("hashvalue = {:x?}", hashvalue); | |
let locktime = 144*2; //blocks | |
println!("locktime = {:x}", locktime); | |
let redeemscript = Builder::new() | |
.push_opcode(opcodes::all::OP_SIZE) | |
.push_opcode(opcodes::all::OP_SWAP) | |
.push_opcode(opcodes::all::OP_HASH160) | |
.push_slice(&hashvalue[..]) | |
.push_opcode(opcodes::all::OP_EQUAL) | |
.push_opcode(opcodes::all::OP_IF) | |
.push_key(&pub_hashlock) | |
.push_int(32) | |
.push_int(1) | |
.push_opcode(opcodes::all::OP_ELSE) | |
.push_key(&pub_timelock) | |
.push_int(0) | |
.push_int(locktime) | |
.push_opcode(opcodes::all::OP_ENDIF) | |
.push_opcode(opcodes::all::OP_CSV) | |
.push_opcode(opcodes::all::OP_DROP) | |
.push_opcode(opcodes::all::OP_ROT) | |
.push_opcode(opcodes::all::OP_EQUALVERIFY) | |
.push_opcode(opcodes::all::OP_CHECKSIG) | |
.into_script(); | |
println!("redeemscript hex = {:x}", redeemscript); | |
let mut vec_rs: Vec<u8> = redeemscript.to_bytes(); | |
let slice = &vec_rs[0..8]; | |
println!("first 8 = {:x?}", slice); | |
let pattern = [130, 124, 169, 20, 142, 105, 180, 87]; | |
println!("same = {}", slice == pattern); | |
println!("hashvalue = {:x?}", &vec_rs[4..24]); | |
println!("pub_hashlock = {:x?}", &vec_rs[27..60]); | |
println!("pub_timelock = {:x?}", &vec_rs[65..98]); | |
println!("locktime = {:x?}", &vec_rs[100..102]); | |
let locktime: i64 = vec_rs[100] as i64 | (vec_rs[101] as i64) << 8; | |
println!("locktime int = {}", locktime); | |
const HASHVALUE_PLACEHOLDER: [u8; 20] = [0xcc; 20]; | |
const PUB_HASHLOCK_PLACEHOLDER: [u8; 33] = [0xee; 33]; | |
const PUB_TIMELOCK_PLACEHOLDER: [u8; 33] = [0xdd; 33]; | |
const LOCKTIME_PLACEHOLDER: [u8; 2] = [0xb3, 0x15]; //number 0x15b3 = 5555 | |
vec_rs.splice(4..24, HASHVALUE_PLACEHOLDER.iter().cloned()); | |
vec_rs.splice(27..60, PUB_HASHLOCK_PLACEHOLDER.iter().cloned()); | |
vec_rs.splice(65..98, PUB_TIMELOCK_PLACEHOLDER.iter().cloned()); | |
vec_rs.splice(100..102, LOCKTIME_PLACEHOLDER.iter().cloned()); | |
let template_redeemscript = Script::from(vec_rs); | |
println!("template_rs = {}", template_redeemscript.to_hex()); | |
//827ca9148e69b45727a12351d05d133b43b81a43d55f4d6c87632102b98a7fb8cc007048625b6446ad49a1b3a722df8c1ca975b87160023e14d1909701206755b275210381aaadc8a5e83f4576df823cf22a5b1969cf704a0d5f6f68bd757410c9917aac00687b88ac | |
} | |
//wrote this for indium contract.rs but it turned out to be useless | |
//so saving it here | |
/* | |
fn does_redeemscript_match_contract(redeemscript: &Script) -> bool { | |
const PUB_HASHLOCK_PLACEHOLDER: [u8; 33] = [0xee; 33]; | |
const PUB_TIMELOCK_PLACEHOLDER: [u8; 33] = [0xdd; 33]; | |
const HASHVALUE_PLACEHOLDER: [u8; 20] = [0xcc; 20]; | |
const LOCKTIME_PLACEHOLDER_BYTES: [u8; 2] = [0x7f; 2]; | |
const LOCKTIME_PLACEHOLDER_NUMBER: i64 = 0x7f7f; | |
let template_redeemscript = create_contract_redeemscript( | |
&PublicKey::from_slice(&PUB_HASHLOCK_PLACEHOLDER).unwrap(), | |
&PublicKey::from_slice(&PUB_TIMELOCK_PLACEHOLDER).unwrap(), | |
HASHVALUE_PLACEHOLDER, | |
LOCKTIME_PLACEHOLDER_NUMBER | |
).into_bytes(); | |
let mut rs_bytes = redeemscript.to_bytes(); | |
if rs_bytes.len() != template_redeemscript.len() { | |
return false; | |
} | |
rs_bytes.splice(4..24, HASHVALUE_PLACEHOLDER.iter().cloned()); | |
rs_bytes.splice(27..60, PUB_HASHLOCK_PLACEHOLDER.iter().cloned()); | |
rs_bytes.splice(64..66, LOCKTIME_PLACEHOLDER_BYTES.iter().cloned()); | |
rs_bytes.splice(69..102, PUB_TIMELOCK_PLACEHOLDER.iter().cloned()); | |
rs_bytes == template_redeemscript | |
} | |
*/ | |
fn create_multisig_redeemscript(key1: &PublicKey, key2: &PublicKey | |
) -> Script { | |
let builder = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2); | |
if key1.serialize()[..] < key2.serialize()[..] { | |
builder | |
.push_key(key1) | |
.push_key(key2) | |
} else { | |
builder | |
.push_key(key2) | |
.push_key(key1) | |
} | |
.push_opcode(opcodes::all::OP_PUSHNUM_2) | |
.push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script() | |
} | |
fn pattern_match_multisig_script() { | |
let secp = Secp256k1::new(); | |
let pub1 = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xcd; 32]).unwrap() | |
}.public_key(&secp); | |
let pub2 = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xab; 32]).unwrap() | |
}.public_key(&secp); | |
let redeemscript = create_multisig_redeemscript(&pub1, &pub2); | |
println!("redeemscript = {:x}", redeemscript); | |
let rs = redeemscript.to_bytes(); | |
println!("pub1 = {:x?}", &rs[2..35]); | |
println!("pub2 = {:x?}", &rs[36..69]); | |
const PUB1_PLACEHOLDER: [u8; 33] = [0x02; 33]; | |
const PUB2_PLACEHOLDER: [u8; 33] = [0x03; 33]; | |
let pubkey1 = PublicKey::from_slice(&PUB1_PLACEHOLDER).unwrap(); | |
let pubkey2 = PublicKey::from_slice(&PUB2_PLACEHOLDER).unwrap(); | |
println!("pubkey1 = {}\npubkey2 = {}", pubkey1, pubkey2); | |
/* | |
let template_msig_redeemscript = wallet_sync::create_multisig_redeemscript( | |
&pubkey1, &pubkey2 | |
); | |
*/ | |
} | |
#[derive(serde::Serialize, serde::Deserialize, Debug)] | |
struct WalletFileData { | |
version: u32, | |
seedphrase: String, | |
extension: String, | |
external_index: u32, | |
swap_coins: Vec<SwapCoin>, | |
prevout_to_contract_map: HashMap<OutPoint, Script> | |
} | |
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] | |
pub struct SwapCoin { | |
pub my_privkey: SecretKey, | |
pub other_pubkey: PublicKey, | |
pub other_privkey: Option<SecretKey>, | |
pub contract_tx: Transaction, | |
pub contract_redeemscript: Script, | |
pub funding_amount: u64, | |
pub others_contract_sig: Option<Signature>, | |
pub hash_preimage: Option<[u8; 32]> | |
} | |
fn serde_secret_key() { | |
/* | |
let wallet_file = File::open("taker.indium").unwrap(); | |
let wallet_file_data: WalletFileData = serde_json::from_reader( | |
wallet_file).unwrap(); | |
*/ | |
let s = "{\"version\":0,\"seedphrase\":\"finger topple before stock embrace liar air now trouble beauty forum protect\",\"extension\":\"www\",\"external_index\":0,\"swap_coins\":[{\"my_privkey\":\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"other_pubkey\":\"02f64651c1fd49cf167d825c730e6af2d28a796ac78103dadb2070834b9300df9a\",\"other_privkey\":null,\"contract_tx\":{\"version\":2,\"lock_time\":0,\"input\":[{\"previous_output\":\"f1d5b2fbf9789f2249d2eefb44ec1aa4cb9609f97dd67c058459333e473f873d:1\",\"script_sig\":\"\",\"sequence\":0,\"witness\":[]}],\"output\":[{\"value\":499000,\"script_pubkey\":\"0020694226d1ba306bc4a6f6ebd443cbb456837055d5b4cbfa53f0572f04f3736a8c\"}]},\"contract_redeemscript\":\"827ca914794ba5ff38c2883538890a4fe167da2301d4a41e876321034c983b4b386d2b3e0b701b4a5f76f50a1297cb151463913fadb4ede276f342040120516721021617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b00016468b2757b88ac\",\"funding_amount\":500000,\"others_contract_sig\":\"304402207d795fab95e6e40e2cafe7342a314501079c66d5a331f6fa0abb4599e468e77a02207cce9ed2c5db1119b6c339ee31883013f6883f38aa9b753afeb3f83c1dc5af8c\",\"hash_preimage\":null}],\"prevout_to_contract_map\":{}}"; | |
let wallet_file_data = serde_json::from_str::<WalletFileData>(&s); | |
println!("wfd = {:?}", wallet_file_data); | |
} | |
fn convert_string_redeemscript_to_obj() { | |
let redeemscript_str = "52210268680737c76dabb801cb2204f57dbe4e4579e4f710cd67dc1b4227592c81e9b521035ebc97ce40dd83f1d51fa69ede034f6bff0feb56d084b50b9992d353de29832452ae"; | |
let redeemscript = Script::from(Vec::from_hex(redeemscript_str).unwrap()); | |
println!("rs = {}", redeemscript); | |
} | |
fn pubkey_pair_to_descriptor() { | |
let secp = Secp256k1::new(); | |
let priv_a = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xab; 32]).unwrap() | |
}; | |
let pub_a = priv_a.public_key(&secp); | |
let priv_b = PrivateKey { | |
compressed: true, | |
network: Network::Regtest, | |
key: secp256k1::SecretKey::from_slice(&[0xcd; 32]).unwrap() | |
}; | |
let pub_b = priv_b.public_key(&secp); | |
println!("pub_a = {}", pub_a); | |
println!("pub_b = {}", pub_b); | |
let pubkey_pair = (pub_a, pub_b); | |
println!("pair = {:?}", pubkey_pair); | |
//let d_str = format!("wsh(multi(2,{},{}))", pubkey_pair); | |
//println!("d_str = {}", d_str); | |
} | |
fn reproduce_testnet_signing_bug() { | |
let mut tx = Transaction { | |
version: 2, | |
lock_time: 0, | |
input: vec![ | |
TxIn { | |
previous_output: OutPoint { | |
txid: Txid::from_hex( | |
"38924f09fdd18dea537661bb3bd15576d377406423e0e5db370b9897f0601305" | |
).unwrap(), | |
vout: 0, | |
}, | |
script_sig: Script::new(), | |
sequence: 0, | |
witness: Vec::new(), | |
}, | |
], | |
output: vec![ | |
TxOut { | |
value: 4990000, | |
script_pubkey: Address::from_str( | |
"tb1qrcl26s2909ty9r734yr4dhpn6q09x2cecf2c4vllwfpnp4my6vxqq84us4" | |
).unwrap().script_pubkey(), | |
}, | |
TxOut { | |
value: 120006951, | |
script_pubkey: Address::from_str( | |
"tb1qhdu4wc2aekml0lhh2czuttuyucc4m5mug3jkl2" | |
).unwrap().script_pubkey(), | |
}, | |
], | |
}; | |
let privkey = PrivateKey::from_wif( | |
"cVm2dvd7791RszBwy4Q2Uf7Efz1GCStfqErZyumr26P2WuxMVdeA").unwrap(); | |
let amount = 124997104; | |
println!("txid = {}", tx.txid()); | |
println!("privkey = {}", privkey); | |
let secp = bitcoin::secp256k1::Secp256k1::new(); | |
let pubkey = privkey.public_key(&secp); | |
let scriptcode = Builder::new() | |
.push_opcode(opcodes::all::OP_DUP) | |
.push_opcode(opcodes::all::OP_HASH160) | |
.push_slice(&Hash160::hash( | |
pubkey.to_bytes().as_slice())[..] | |
) | |
.push_opcode(opcodes::all::OP_EQUALVERIFY) | |
.push_opcode(opcodes::all::OP_CHECKSIG) | |
.into_script(); | |
let sighash = bip143::SighashComponents::new(&tx.clone()).sighash_all( | |
&tx.input[0], &scriptcode, amount | |
); | |
println!("sighash = {}", sighash); | |
let signature = secp.sign( | |
&bitcoin::secp256k1::Message::from_slice(&sighash[..]).unwrap(), | |
&privkey.key | |
); | |
println!("sig = {}", signature); | |
tx.input[0].witness.push(signature.serialize_der().to_vec()); | |
tx.input[0].witness[0].push(SigHashType::All as u8); | |
tx.input[0].witness.push(pubkey.to_bytes()); | |
println!("txhex = {}", serialize_hex(&tx)); | |
} | |
fn main() { | |
let a = 15; | |
match a { | |
0 => sign_tx(), | |
1 => sign_tx_from_indium_test_wallet(), | |
2 => generate_without_rustwallet_masteraccount(), | |
3 => do_bip32(), | |
4 => match rpc_descriptors() { | |
Ok(_) => println!("ok"), | |
Err(e) => println!("err = {}", e), | |
}, | |
5 => println!("{:?}", create_and_spend_from_2of2_multisig()), | |
6 => println!("{:?}", create_and_spend_from_htlc()), | |
7 => create_tweaked_pubkey(), | |
8 => get_tweakable_pubkey(), | |
9 => println!("{:?}", create_and_spend_from_new_htlc_plus_tests()), | |
10 => pattern_match_contract(), | |
11 => pattern_match_multisig_script(), | |
12 => serde_secret_key(), | |
13 => convert_string_redeemscript_to_obj(), | |
14 => pubkey_pair_to_descriptor(), | |
15 => reproduce_testnet_signing_bug(), | |
_ => println!("unknown"), | |
}; | |
//plan | |
//come up with a bip32 privkey | |
//send coins to addresses and spend from them | |
//from that generate xpubs, use descriptors to generate single-sig addresses | |
// will need to use descriptors at least for importing into core | |
// rust code wont store all the addresses, but will only have the xprv | |
// and using the index will be able to generate any privkey | |
// so i must be able to use rust to sign a tx with a privkey | |
//create a 2of2 multisig address, fund it and spend from it | |
//figure out ECDH, i.e. tweaking a pubkey to get another pubkey | |
// where the privkey can be used to get the privkey of the other pubkey | |
// and so agree on a pubkey with one less step of interaction | |
// its not exactly ECDH because an eavesdropper isnt a threat | |
// rust-wallet context.rs has tweak_add() | |
//figure out signing a hash | |
//combine tokio and bitcoin json-rpc | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment