Class: Bitcoin::Taproot::SimpleBuilder

Inherits:
Object
  • Object
show all
Includes:
Opcodes
Defined in:
lib/bitcoin/taproot/simple_builder.rb

Overview

Utility class to construct Taproot outputs from internal key and script tree.keyPathSpending SimpleBuilder builds a script tree that places all lock scripts, in the order they are added, as leaf nodes. It is not possible to specify the depth of the locking script or to insert any intermediate nodes.

Constant Summary

Constants included from Opcodes

Opcodes::DUPLICATE_KEY, Opcodes::NAME_MAP, Opcodes::OPCODES_MAP, Opcodes::OP_0, Opcodes::OP_0NOTEQUAL, Opcodes::OP_1, Opcodes::OP_10, Opcodes::OP_11, Opcodes::OP_12, Opcodes::OP_13, Opcodes::OP_14, Opcodes::OP_15, Opcodes::OP_16, Opcodes::OP_1ADD, Opcodes::OP_1NEGATE, Opcodes::OP_1SUB, Opcodes::OP_2, Opcodes::OP_2DIV, Opcodes::OP_2DROP, Opcodes::OP_2DUP, Opcodes::OP_2MUL, Opcodes::OP_2OVER, Opcodes::OP_2ROT, Opcodes::OP_2SWAP, Opcodes::OP_3, Opcodes::OP_3DUP, Opcodes::OP_4, Opcodes::OP_5, Opcodes::OP_6, Opcodes::OP_7, Opcodes::OP_8, Opcodes::OP_9, Opcodes::OP_ABS, Opcodes::OP_ADD, Opcodes::OP_AND, Opcodes::OP_BOOLAND, Opcodes::OP_BOOLOR, Opcodes::OP_CAT, Opcodes::OP_CHECKMULTISIG, Opcodes::OP_CHECKMULTISIGVERIFY, Opcodes::OP_CHECKSIG, Opcodes::OP_CHECKSIGADD, Opcodes::OP_CHECKSIGVERIFY, Opcodes::OP_CODESEPARATOR, Opcodes::OP_DEPTH, Opcodes::OP_DIV, Opcodes::OP_DROP, Opcodes::OP_DUP, Opcodes::OP_ELSE, Opcodes::OP_ENDIF, Opcodes::OP_EQUAL, Opcodes::OP_EQUALVERIFY, Opcodes::OP_FROMALTSTACK, Opcodes::OP_GREATERTHAN, Opcodes::OP_GREATERTHANOREQUAL, Opcodes::OP_HASH160, Opcodes::OP_HASH256, Opcodes::OP_IF, Opcodes::OP_IFDUP, Opcodes::OP_INVALIDOPCODE, Opcodes::OP_INVERT, Opcodes::OP_LEFT, Opcodes::OP_LESSTHAN, Opcodes::OP_LESSTHANOREQUAL, Opcodes::OP_LSHIFT, Opcodes::OP_MAX, Opcodes::OP_MIN, Opcodes::OP_MOD, Opcodes::OP_MUL, Opcodes::OP_NEGATE, Opcodes::OP_NIP, Opcodes::OP_NOP, Opcodes::OP_NOP1, Opcodes::OP_NOP10, Opcodes::OP_NOP2, Opcodes::OP_NOP3, Opcodes::OP_NOP4, Opcodes::OP_NOP5, Opcodes::OP_NOP6, Opcodes::OP_NOP7, Opcodes::OP_NOP8, Opcodes::OP_NOP9, Opcodes::OP_NOT, Opcodes::OP_NOTIF, Opcodes::OP_NUMEQUAL, Opcodes::OP_NUMEQUALVERIFY, Opcodes::OP_NUMNOTEQUAL, Opcodes::OP_OR, Opcodes::OP_OVER, Opcodes::OP_PICK, Opcodes::OP_PUBKEY, Opcodes::OP_PUBKEYHASH, Opcodes::OP_PUSHDATA1, Opcodes::OP_PUSHDATA2, Opcodes::OP_PUSHDATA4, Opcodes::OP_RESERVED, Opcodes::OP_RESERVED1, Opcodes::OP_RESERVED2, Opcodes::OP_RETURN, Opcodes::OP_RIGHT, Opcodes::OP_RIPEMD160, Opcodes::OP_ROLL, Opcodes::OP_ROT, Opcodes::OP_RSHIFT, Opcodes::OP_SHA1, Opcodes::OP_SHA256, Opcodes::OP_SIZE, Opcodes::OP_SUB, Opcodes::OP_SUBSTR, Opcodes::OP_SUCCESSES, Opcodes::OP_SWAP, Opcodes::OP_TOALTSTACK, Opcodes::OP_TUCK, Opcodes::OP_VER, Opcodes::OP_VERIF, Opcodes::OP_VERIFY, Opcodes::OP_VERNOTIF, Opcodes::OP_WITHIN, Opcodes::OP_XOR

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Opcodes

defined?, name_to_opcode, op_success?, opcode_to_name, opcode_to_small_int, small_int_to_opcode

Constructor Details

#initialize(internal_key, leaves = []) ⇒ Bitcoin::Taproot::SimpleBuilder

Initialize builder.

Parameters:

  • internal_key (String)

    Internal public key with hex format.

  • leaves (Array[Bitcoin::Taproot::LeafNode]) (defaults to: [])

    (Optional) Array of leaf nodes for each lock condition.

Raises:

  • (Bitcoin::Taproot::Builder)

    internal_pubkey dose not xonly public key or leaf in leaves does not instance of Bitcoin::Taproot::LeafNode.



18
19
20
21
22
23
24
25
# File 'lib/bitcoin/taproot/simple_builder.rb', line 18

def initialize(internal_key, leaves = [])
  raise Error, "Internal public key must be #{X_ONLY_PUBKEY_SIZE} bytes" unless internal_key.htb.bytesize == X_ONLY_PUBKEY_SIZE
  raise Error, 'leaf must be Bitcoin::Taproot::LeafNode object' if leaves.find{ |leaf| !leaf.is_a?(Bitcoin::Taproot::LeafNode)}

  @leaves = leaves
  @branches = leaves.each_slice(2).map.to_a
  @internal_key = internal_key
end

Instance Attribute Details

#branchesObject (readonly)

List of branch that has two child leaves



11
12
13
# File 'lib/bitcoin/taproot/simple_builder.rb', line 11

def branches
  @branches
end

#internal_keyObject (readonly)

String with hex format



10
11
12
# File 'lib/bitcoin/taproot/simple_builder.rb', line 10

def internal_key
  @internal_key
end

Instance Method Details

#add_branch(leaf1, leaf2 = nil) ⇒ Object

Add a pair of leaf nodes as a branch. If there is only one, add a branch with only one child.

Parameters:

Raises:



43
44
45
46
47
48
49
# File 'lib/bitcoin/taproot/simple_builder.rb', line 43

def add_branch(leaf1, leaf2 = nil)
  raise Error, 'leaf1 must be Bitcoin::Taproot::LeafNode object' unless leaf1.is_a?(Bitcoin::Taproot::LeafNode)
  raise Error, 'leaf2 must be Bitcoin::Taproot::LeafNode object' if leaf2 && !leaf2.is_a?(Bitcoin::Taproot::LeafNode)

  branches << (leaf2.nil? ? [leaf1] : [leaf1, leaf2])
  self
end

#add_leaf(leaf) ⇒ Object

Add a leaf node to the end of the current branch.

Parameters:

Raises:



29
30
31
32
33
34
35
36
37
38
# File 'lib/bitcoin/taproot/simple_builder.rb', line 29

def add_leaf(leaf)
  raise Error, 'leaf must be Bitcoin::Taproot::LeafNode object' unless leaf.is_a?(Bitcoin::Taproot::LeafNode)

  if branches.last&.size == 1
    branches.last << leaf
  else
    branches << [leaf]
  end
  self
end

#buildBitcoin::Script

Build P2TR script.

Returns:



53
54
55
56
# File 'lib/bitcoin/taproot/simple_builder.rb', line 53

def build
  q = tweak_public_key
  Bitcoin::Script.new << OP_1 << q.xonly_pubkey
end

#control_block(leaf) ⇒ Bitcoin::Taproot::ControlBlock

Generate control block needed to unlock with script-path.

Parameters:

Returns:



76
77
78
79
80
# File 'lib/bitcoin/taproot/simple_builder.rb', line 76

def control_block(leaf)
  path = inclusion_proof(leaf)
  parity = tweak_public_key.to_point.has_even_y? ? 0 : 1
  ControlBlock.new(parity, leaf.leaf_ver, internal_key, path.map(&:bth))
end

#inclusion_proof(leaf) ⇒ Array[String]

Generate inclusion proof for leaf.

Parameters:

Returns:

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/bitcoin/taproot/simple_builder.rb', line 86

def inclusion_proof(leaf)
  proofs = []
  target_branch = branches.find{|b| b.include?(leaf)}
  raise Error 'Specified leaf does not exist' unless target_branch

  # flatten each branch
  proofs << hash_value(target_branch.find{|b| b != leaf}) if target_branch.size == 2
  parent_hash = combine_hash(target_branch)
  parents = branches.map {|pair| combine_hash(pair)}

  until parents.size == 1
    parents = parents.each_slice(2).map do |pair|
      combined = combine_hash(pair)
      unless pair.size == 1
        if hash_value(pair[0]) == parent_hash
          proofs << hash_value(pair[1])
          parent_hash = combined
        elsif hash_value(pair[1]) == parent_hash
          proofs << hash_value(pair[0])
          parent_hash = combined
        end
      end
      combined
    end
  end
  proofs
end

#tweak_private_key(key) ⇒ Bitcoin::Key

Compute the secret key for a tweaked public key.

Parameters:

Returns:

Raises:



67
68
69
70
71
# File 'lib/bitcoin/taproot/simple_builder.rb', line 67

def tweak_private_key(key)
  raise Error, 'Requires private key' unless key.priv_key

  Taproot.tweak_private_key(key, merkle_root)
end

#tweak_public_keyBitcoin::Key

Compute the tweaked public key.

Returns:



60
61
62
# File 'lib/bitcoin/taproot/simple_builder.rb', line 60

def tweak_public_key
  Taproot.tweak_public_key(Bitcoin::Key.from_xonly_pubkey(internal_key), merkle_root)
end