Skip to content

Instantly share code, notes, and snippets.

@pierrenoizat
Last active February 3, 2022 17:00
Show Gist options
  • Save pierrenoizat/16ba9e5b7455c6fe9358840ce38e0021 to your computer and use it in GitHub Desktop.
Save pierrenoizat/16ba9e5b7455c6fe9358840ce38e0021 to your computer and use it in GitHub Desktop.
Build a Segwit transaction spending a Segwit native P2WPKH utxo and another (standard) transaction funding a native P2WPKH Segwit address
require 'bitcoin'
require 'money-tree'
require 'bip44'
require 'bech32'
include Bitcoin
include Bitcoin::Builder
include Bitcoin::Protocol
include Bitcoin::Util
include Bitcoin::Secp256k1
include Bech32
base_factor = 100000000
mnemonic = "beyond .. satoshi" # enter your own passphrase here
seed = BipMnemonic.to_seed(mnemonic: mnemonic)
@btc_wallet = Bip44::Wallet.from_seed(seed, "m/44'/0")
i=2
@btc_node = @btc_wallet.sub_wallet "m/0/#{i}"
destination_address = "12qeTKzrK7wm1V8rCjYEysAdtD5D3PxfPV"
public_key = Key.new(nil,@btc_node.public_key).pub_compressed
script_pubkey = Script.from_string("#{Script::OP_0} " + "#{hash160(public_key)}").to_payload # to spend a Segwit input
native_p2wpkh_address = SegwitAddr.new
native_p2wpkh_address.hrp = 'bc' # hrp = human-readable part
native_p2wpkh_address.script_pubkey = script_pubkey.bth
address = native_p2wpkh_address.addr # P2WPKH native Segwit address: must be funded !
utxo_txid = "03e60e4671588c2dc94415ab21a95960e19ebe9316c5ee25a706ccba25535e36"
utxo_index = 0
amount = 0.00075135 * base_factor
fee = 0.0001 * base_factor
# sent_amount = 0.0005 * base_factor
# change = amount - sent_amount - fee
sent_amount = amount - fee
# Segwit transaction to spend the native Segwit address:
tx = build_tx do |t|
t.lock_time 0
t.input do |i|
i.prev_out utxo_txid, utxo_index, script_pubkey, amount
i.sequence "ffffffff".htb
end
t.output do |o|
o.value sent_amount
o.script {|s| s.recipient destination_address }
end
# t.output do |o|
# o.value change
# o.script { |s| s.recipient change_address }
# end
end
tx.to_payload.bth # unsigned tx
sig_hash = tx.signature_hash_for_witness_input(0, script_pubkey, amount)
sig = Secp256k1.sign(sig_hash, @btc_node.private_key.htb)
tx.in[0].script_witness.stack << sig + [Tx::SIGHASH_TYPE[:all]].pack("C")
tx.in[0].script_witness.stack << public_key.htb
puts tx.to_payload.unpack('H*')[0] # signed tx (in hex)
puts tx.hash
destination_address = "bc1q2xdndlad2njdd6vsnvqhhyu9l4dan2sm4dhrml"
utxo_txid = "b2e85846105077e0eadaa570ba5962872f6bf22ef43ab50c58571f8cb39a3ce1"
utxo_index = 1
amount = 0.00073192 * base_factor
fee = 0.0001 * base_factor
sent_amount = amount - fee
public_key = Key.new(nil,@btc_node.public_key).pub
# puts Key.new(nil,@btc_node.public_key).addr # standard (legacy) uncompressed address being spent
script_pubkey = Script.to_hash160_script(hash160(public_key)) # to spend a standard utxo
# Standard transaction to fund the native Segwit address:
tx = build_tx do |t|
t.input do |i|
i.prev_out utxo_txid
i.prev_out_index utxo_index
end
t.output do |o|
o.value sent_amount
o.script {|s| s.recipient destination_address }
end
end
tx.to_payload.bth # unsigned tx
sig_hash = tx.signature_hash_for_input(0, script_pubkey)
sig = Secp256k1.sign(sig_hash, @btc_node.private_key.htb)
tx.in[0].script_sig = Script.to_signature_pubkey_script(sig, public_key.htb)
puts tx.to_payload.unpack('H*')[0] # signed tx (in hex)
puts tx.hash
@pierrenoizat
Copy link
Author

This gist is using the remarkable bech32 gem from Shigeyuki Azuchi : [email protected]:azuchi/bech32rb.git

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment