diff --git a/docs/_examples/taproot_coinbase.adoc b/docs/_examples/taproot_coinbase.adoc new file mode 100644 index 0000000..faf3c9e --- /dev/null +++ b/docs/_examples/taproot_coinbase.adoc @@ -0,0 +1,17 @@ += Taproot Coinbase +:page-layout: page +:page-title: Taproot Coinbase +:page-nav_order: 3 +:sourcedir: ../../ + +To generate a coinbase transaction with a taproot script pubkey, use +the `taproot` keyword argument and provide the internal key and +optionally the leaves just as in a normal link:taproot.html[Taproot +output]. + +.Taproot coinbase +[source,ruby] +---- +include::{sourcedir}/lib/contracts/taproot/coinbase_with_taproot.rb[tags=coinbase] +---- +<1> Use the taproot keyword with the `internal_key` and `leaves` arguments diff --git a/lib/contracts/coinbase_with_descriptor.rb b/lib/contracts/coinbase_with_descriptor.rb index 4227c4c..fbd3bc3 100644 --- a/lib/contracts/coinbase_with_descriptor.rb +++ b/lib/contracts/coinbase_with_descriptor.rb @@ -26,7 +26,7 @@ @coinbase_with_descriptor = get_coinbase_at 1 -# Make descripto coinbases spendable by spending to p2wkh +# Make descriptor coinbase spendable by spending to p2wkh extend_chain to: @alice, num_blocks: 100 assert_confirmations @coinbase_with_descriptor, confirmations: 100 diff --git a/lib/contracts/multiple_inputs.rb b/lib/contracts/multiple_inputs.rb new file mode 100644 index 0000000..e6d80f3 --- /dev/null +++ b/lib/contracts/multiple_inputs.rb @@ -0,0 +1,55 @@ +# Copyright 2024 Kulpreet Singh +# +# This file is part of Bitcoin-DSL +# +# Bitcoin-DSL is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bitcoin-DSL is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bitcoin-DSL. If not, see . + +# frozen_string_literal: false + +# Generate new keys +@alice = key :new +@bob = key :new +@carol = key :new + +# Seed alice with some coins +extend_chain to: @alice + +# Seed bob with some coins and make coinbase spendable +extend_chain num_blocks: 1, to: @alice +extend_chain num_blocks: 101, to: @bob + +@alice_coinbase = spendable_coinbase_for @alice +@bob_coinbase = spendable_coinbase_for @bob + +@multiple_inputs_tx = transaction inputs: [ + { tx: @alice_coinbase, vout: 0, script_sig: 'sig:wpkh(@alice)' }, + { tx: @bob_coinbase, vout: 0, script_sig: 'sig:wpkh(@bob)' } + ], + outputs: [ + { + descriptor: 'wpkh(@carol)', + amount: 99.999.sats + } + ] + +# verify_signature for_transaction: @multiple_inputs_tx, +# at_index: 0, +# with_prevout: [@alice_coinbase, 0] + +assert_mempool_accept @multiple_inputs_tx +broadcast @multiple_inputs_tx +confirm transaction: @multiple_inputs_tx, to: @carol + +assert_output_is_spent transaction: @alice_coinbase, vout: 0 +assert_output_is_spent transaction: @bob_coinbase, vout: 0 diff --git a/lib/contracts/taproot/coinbase_with_taproot.rb b/lib/contracts/taproot/coinbase_with_taproot.rb new file mode 100644 index 0000000..873fb2a --- /dev/null +++ b/lib/contracts/taproot/coinbase_with_taproot.rb @@ -0,0 +1,49 @@ +# Copyright 2024 Kulpreet Singh +# +# This file is part of Bitcoin-DSL +# +# Bitcoin-DSL is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Bitcoin-DSL is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Bitcoin-DSL. If not, see . + +# frozen_string_literal: false + +@alice = key :new +@bob = key :new +@carol = key :new + +# tag::coinbase[] +# Mine block with taproot coinbase +extend_chain taproot: { internal_key: @bob, # <1> + leaves: ['pk(@carol)', 'pk(@alice)'] } +# end::coinbase[] + +assert_height 1 + +@coinbase_with_taproot = get_coinbase_at 1 + +# Make coinbase spendable by spending to p2wkh +extend_chain to: @alice, num_blocks: 100 + +@spend_taproot_coinbase = transaction inputs: [ + { tx: @coinbase_with_taproot, + vout: 0, + script_sig: { keypath: @bob }, + sighash: :all } + ], + outputs: [ + { descriptor: 'wpkh(@carol)', + amount: 49.998.sats } + ] + +broadcast @spend_taproot_coinbase +confirm transaction: @spend_taproot_coinbase diff --git a/lib/contracts/taproot/taproot_coinbase.rb b/lib/contracts/taproot/taproot_coinbase.rb deleted file mode 100644 index 4640904..0000000 --- a/lib/contracts/taproot/taproot_coinbase.rb +++ /dev/null @@ -1 +0,0 @@ -# TODO diff --git a/lib/dsl/broadcast.rb b/lib/dsl/broadcast.rb index e5bdfac..bddd01f 100644 --- a/lib/dsl/broadcast.rb +++ b/lib/dsl/broadcast.rb @@ -19,7 +19,7 @@ # DSL module for broadcasting transactions module Broadcast - def extend_chain(to: nil, policy: nil, descriptor: nil, script: nil, num_blocks: 1) + def extend_chain(to: nil, policy: nil, descriptor: nil, script: nil, taproot: nil, num_blocks: 1) _ = get_height # We need to seem to call getheight before generating to address if descriptor script_pubkey, = compile_descriptor(descriptor) @@ -30,6 +30,9 @@ def extend_chain(to: nil, policy: nil, descriptor: nil, script: nil, num_blocks: elsif script script_pubkey, = compile_script_pubkey(script) address = script_pubkey.to_addr + elsif taproot + script_pubkey, = compile_taproot(taproot) + address = script_pubkey.to_addr else to ||= key :new address = to.to_p2wpkh diff --git a/lib/dsl/query_node.rb b/lib/dsl/query_node.rb index 60ecc5f..292ae5a 100644 --- a/lib/dsl/query_node.rb +++ b/lib/dsl/query_node.rb @@ -55,6 +55,8 @@ def get_coinbase_at(height) block['tx'][0] end + alias coinbase_at get_coinbase_at + # Return a spendable coinbase for a key # If a key is provided, we use the p2wpkh address for the key # Later on we will add options to query by a given address diff --git a/spec/runner_spec.rb b/spec/runner_spec.rb index d3f6500..207c4b3 100644 --- a/spec/runner_spec.rb +++ b/spec/runner_spec.rb @@ -35,6 +35,7 @@ it_behaves_like 'script evaluation', './lib/contracts/anchor_transactions.rb' it_behaves_like 'script evaluation', './lib/contracts/fold_transactions.rb' it_behaves_like 'script evaluation', './lib/contracts/multisig.rb' + it_behaves_like 'script evaluation', './lib/contracts/multiple_inputs.rb' it_behaves_like 'script evaluation', './lib/contracts/simple.rb' end @@ -76,6 +77,7 @@ describe 'taproot transactions' do it_behaves_like 'script evaluation', './lib/contracts/taproot/keypath_spend.rb' it_behaves_like 'script evaluation', './lib/contracts/taproot/scriptpath_spend.rb' + it_behaves_like 'script evaluation', './lib/contracts/taproot/coinbase_with_taproot.rb' end describe 'silent payment' do