bitcoinrb 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af836e45167490d93d1d9b5d8f29f4e473a78dc314b98cd789bb47e347a42037
4
- data.tar.gz: 020fe745057f3b987c8bd8828be9f7f657d3085a7d8ee37cf0218b0761202325
3
+ metadata.gz: e8103017ba6b480dc45caea8b9544720003c486167c3e867be738ccdc40faef7
4
+ data.tar.gz: 3807ee81a2ed26a526c19903c616ebb7af5b2a9c8434a76398e538a02c34e126
5
5
  SHA512:
6
- metadata.gz: 0db2c744f372a2e71c337cb8970bb8cad81f37aa81824504d5fe6ee46819516ca6869c3cab0af1474aa0906905496af3e8f97eacc6cdaf9b7eced0a6cd247ff2
7
- data.tar.gz: dab1256db8085c43ec5ab5d784d465cbdb3d74e663d4f052fd76161decacd9fb5fe22b3c2f99cc20381dda8ea203d63866db45e6e8c8ed6d8b4a2d4c27acfe89
6
+ metadata.gz: 97a9df2fee4c9d2fb7c84555d82e0f1ef692e0889c964fc7615f328467925a5b099be19801b19321a9b12fc55493663f1813be4074e5fc95add8948cd52ca2e4
7
+ data.tar.gz: '0370268e40a85bf6face9592b8dc3cf260b6357e796f62c7e6abddaeeef2d3ec561e4336ba859c5f8426dcedd80eba6d9ec632941e284791df9571ff613182e5'
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.0.0
1
+ ruby-3.0.2
data/lib/bitcoin/key.rb CHANGED
@@ -30,7 +30,6 @@ module Bitcoin
30
30
  # @param [Boolean] compressed [Deprecated] whether public key is compressed.
31
31
  # @return [Bitcoin::Key] a key object.
32
32
  def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false)
33
- puts "[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future." if key_type.nil? && !compressed.nil? && pubkey.nil?
34
33
  if key_type
35
34
  @key_type = key_type
36
35
  compressed = @key_type != TYPES[:uncompressed]
@@ -213,7 +212,6 @@ module Bitcoin
213
212
  # get xonly public key (32 bytes).
214
213
  # @return [String] xonly public key with hex format
215
214
  def xonly_pubkey
216
- puts "Derive a public key whose y-coordinate is different from this public key." if compressed? && pubkey[0...2] != '02'
217
215
  pubkey[2..65]
218
216
  end
219
217
 
@@ -162,6 +162,15 @@ module Bitcoin
162
162
  Base64.strict_encode64(to_payload)
163
163
  end
164
164
 
165
+ # Store the PSBT to a file.
166
+ # @param [String] path File path to store.
167
+ def to_file(path)
168
+ raise ArgumentError, 'The file already exists' if File.exist?(path)
169
+ File.open(path, 'w') do |f|
170
+ f.write(to_payload)
171
+ end
172
+ end
173
+
165
174
  # update input key-value maps.
166
175
  # @param [Bitcoin::Tx] prev_tx previous tx reference by input.
167
176
  # @param [Bitcoin::Script] redeem_script redeem script to set input.
data/lib/bitcoin/psbt.rb CHANGED
@@ -31,6 +31,14 @@ module Bitcoin
31
31
  s << Bitcoin.pack_var_int(value.bytesize) << value
32
32
  s
33
33
  end
34
+
35
+ # Load PSBT from file.
36
+ # @param [String] path File path of PSBT.
37
+ # @return [Bitcoin::PSBT::Tx] PSBT object.
38
+ def load_from_file(path)
39
+ raise ArgumentError, 'File not found' unless File.exist?(path)
40
+ Bitcoin::PSBT::Tx.parse_from_payload(File.read(path))
41
+ end
34
42
  end
35
43
 
36
44
  end
@@ -62,6 +62,7 @@ module Bitcoin
62
62
 
63
63
  def generate(tx, input_index, hash_type, opts)
64
64
  amount = opts[:amount]
65
+ raise ArgumentError, 'segwit sighash requires amount.' unless amount
65
66
  output_script = opts[:script_code]
66
67
  skip_separator_index = opts[:skip_separator_index]
67
68
  hash_prevouts = Bitcoin.double_sha256(tx.inputs.map{|i|i.out_point.to_payload}.join)
@@ -7,7 +7,7 @@ module Bitcoin
7
7
  # Initialize
8
8
  # @param [Bitcoin::Script] script Locking script
9
9
  # @param [Integer] leaf_ver The leaf version of this script.
10
- def initialize(script, leaf_ver)
10
+ def initialize(script, leaf_ver = Bitcoin::TAPROOT_LEAF_TAPSCRIPT)
11
11
  raise Taproot::Error, 'script must be Bitcoin::Script object' unless script.is_a?(Bitcoin::Script)
12
12
  @script = script
13
13
  @leaf_ver = leaf_ver
@@ -1,34 +1,50 @@
1
1
  module Bitcoin
2
2
  module Taproot
3
3
 
4
- # Utility class to construct Taproot outputs from internal key and script tree.
4
+ # Utility class to construct Taproot outputs from internal key and script tree.keyPathSpending
5
5
  # SimpleBuilder builds a script tree that places all lock scripts, in the order they are added, as leaf nodes.
6
6
  # It is not possible to specify the depth of the locking script or to insert any intermediate nodes.
7
7
  class SimpleBuilder
8
8
  include Bitcoin::Opcodes
9
9
 
10
10
  attr_reader :internal_key # String with hex format
11
- attr_reader :leaves # Array[LeafNode]
11
+ attr_reader :branches # List of branch that has two child leaves
12
12
 
13
13
  # Initialize builder.
14
14
  # @param [String] internal_key Internal public key with hex format.
15
- # @param [Array[Bitcoin::Script]] scripts Scripts for each lock condition.
16
- # @param [Integer] leaf_ver (Optional) The leaf version of tapscript.
17
- # @raise [Bitcoin::Taproot::Builder] +internal_pubkey+ dose not xonly public key or script in +scripts+ does not instance of Bitcoin::Script.
15
+ # @param [Array[Bitcoin::Taproot::LeafNode]] leaves (Optional) Array of leaf nodes for each lock condition.
16
+ # @raise [Bitcoin::Taproot::Builder] +internal_pubkey+ dose not xonly public key or leaf in +leaves+ does not instance of Bitcoin::Taproot::LeafNode.
18
17
  # @return [Bitcoin::Taproot::SimpleBuilder]
19
- def initialize(internal_key, *scripts, leaf_ver: Bitcoin::TAPROOT_LEAF_TAPSCRIPT)
18
+ def initialize(internal_key, leaves = [])
20
19
  raise Error, 'Internal public key must be 32 bytes' unless internal_key.htb.bytesize == 32
21
- @leaves = scripts.map { |script| LeafNode.new(script, leaf_ver) }
20
+ raise Error, 'leaf must be Bitcoin::Taproot::LeafNode object' if leaves.find{ |leaf| !leaf.is_a?(Bitcoin::Taproot::LeafNode)}
21
+
22
+ @leaves = leaves
23
+ @branches = leaves.each_slice(2).map.to_a
22
24
  @internal_key = internal_key
23
25
  end
24
26
 
25
- # Add lock script to leaf node.
26
- # @param [Bitcoin::Script] script lock script.
27
- # @param [Integer] leaf_ver (Optional) The leaf version of tapscript.
28
- # @raise [Bitcoin::Taproot::Builder] If +script+ does not instance of Bitcoin::Script.
29
- # @return [Bitcoin::Taproot::SimpleBuilder] self
30
- def <<(script, leaf_ver: Bitcoin::TAPROOT_LEAF_TAPSCRIPT)
31
- leaves << LeafNode.new(script, leaf_ver)
27
+ # Add a leaf node to the end of the current branch.
28
+ # @param [Bitcoin::Taproot::LeafNode] leaf Leaf node to be added.
29
+ def add_leaf(leaf)
30
+ raise Error, 'leaf must be Bitcoin::Taproot::LeafNode object' unless leaf.is_a?(Bitcoin::Taproot::LeafNode)
31
+
32
+ if branches.last&.size == 1
33
+ branches.last << leaf
34
+ else
35
+ branches << [leaf]
36
+ end
37
+ self
38
+ end
39
+
40
+ # Add a pair of leaf nodes as a branch. If there is only one, add a branch with only one child.
41
+ # @param [Bitcoin::Taproot::LeafNode] leaf1 Leaf node to be added.
42
+ # @param [Bitcoin::Taproot::LeafNode] leaf2 Leaf node to be added.
43
+ def add_branch(leaf1, leaf2 = nil)
44
+ raise Error, 'leaf1 must be Bitcoin::Taproot::LeafNode object' unless leaf1.is_a?(Bitcoin::Taproot::LeafNode)
45
+ raise Error, 'leaf2 must be Bitcoin::Taproot::LeafNode object' if leaf2 && !leaf2.is_a?(Bitcoin::Taproot::LeafNode)
46
+
47
+ branches << (leaf2.nil? ? [leaf1] : [leaf1, leaf2])
32
48
  self
33
49
  end
34
50
 
@@ -42,8 +58,7 @@ module Bitcoin
42
58
  # Compute the tweaked public key.
43
59
  # @return [Bitcoin::Key] the tweaked public key
44
60
  def tweak_public_key
45
- key = Bitcoin::Key.new(priv_key: tweak.bth, key_type: Key::TYPES[:compressed])
46
- Bitcoin::Key.from_point(key.to_point + Bitcoin::Key.from_xonly_pubkey(internal_key).to_point)
61
+ Taproot.tweak_public_key(Bitcoin::Key.from_xonly_pubkey(internal_key), merkle_root)
47
62
  end
48
63
 
49
64
  # Compute the secret key for a tweaked public key.
@@ -51,29 +66,33 @@ module Bitcoin
51
66
  # @return [Bitcoin::Key] secret key for a tweaked public key
52
67
  def tweak_private_key(key)
53
68
  raise Error, 'Requires private key' unless key.priv_key
54
- p = key.to_point
55
- private_key = p.has_even_y? ? key.priv_key.to_i(16) : ECDSA::Group::Secp256k1.order - key.priv_key.to_i(16)
56
- Bitcoin::Key.new(priv_key: ((tweak.bti + private_key) % ECDSA::Group::Secp256k1.order).to_even_length_hex)
69
+
70
+ Taproot.tweak_private_key(key, merkle_root)
57
71
  end
58
72
 
59
73
  # Generate control block needed to unlock with script-path.
60
- # @param [Bitcoin::Script] script Script to use for unlocking.
61
- # @param [Integer] leaf_ver leaf version of script.
74
+ # @param [Bitcoin::Taproot::LeafNode] leaf Leaf to use for unlocking.
62
75
  # @return [String] control block with binary format.
63
- def control_block(script, leaf_ver: Bitcoin::TAPROOT_LEAF_TAPSCRIPT)
64
- path = inclusion_proof(script, leaf_ver: leaf_ver)
76
+ def control_block(leaf)
77
+ path = inclusion_proof(leaf)
65
78
  parity = tweak_public_key.to_point.has_even_y? ? 0 : 1
66
- [parity + leaf_ver].pack("C") + internal_key.htb + path.join
79
+ [parity + leaf.leaf_ver].pack("C") + internal_key.htb + path.join
67
80
  end
68
81
 
69
- # Generate inclusion proof for +script+.
70
- # @param [Bitcoin::Script] script The script in script tree.
71
- # @param [Integer] leaf_ver (Optional) The leaf version of tapscript.
82
+ # Generate inclusion proof for +leaf+.
83
+ # @param [Bitcoin::Taproot::LeafNode] leaf The leaf node in script tree.
72
84
  # @return [Array[String]] Inclusion proof.
73
- def inclusion_proof(script, leaf_ver: Bitcoin::TAPROOT_LEAF_TAPSCRIPT)
74
- parents = leaves
75
- parent_hash = leaf_hash(script, leaf_ver: leaf_ver)
85
+ # @raise [Bitcoin::Taproot::Error] If the specified +leaf+ does not exist
86
+ def inclusion_proof(leaf)
76
87
  proofs = []
88
+ target_branch = branches.find{|b| b.include?(leaf)}
89
+ raise Error 'Specified leaf does not exist' unless target_branch
90
+
91
+ # flatten each branch
92
+ proofs << hash_value(target_branch.find{|b| b != leaf}) if target_branch.size == 2
93
+ parent_hash = combine_hash(target_branch)
94
+ parents = branches.map {|pair| combine_hash(pair)}
95
+
77
96
  until parents.size == 1
78
97
  parents = parents.each_slice(2).map do |pair|
79
98
  combined = combine_hash(pair)
@@ -92,29 +111,26 @@ module Bitcoin
92
111
  proofs
93
112
  end
94
113
 
95
- # Computes leaf hash
96
- # @param [Bitcoin::Script] script
97
- # @param [Integer] leaf_ver leaf version
98
- # @@return [String] leaf hash with binary format.
99
- def leaf_hash(script, leaf_ver: Bitcoin::TAPROOT_LEAF_TAPSCRIPT)
100
- raise Error, 'script does not exist' unless leaves.find{ |leaf| leaf.script == script}
101
- LeafNode.new(script, leaf_ver).leaf_hash
102
- end
103
-
104
114
  private
105
115
 
106
116
  # Compute tweak from script tree.
107
117
  # @return [String] tweak with binary format.
108
118
  def tweak
109
- parents = leaves
119
+ Taproot.tweak(Bitcoin::Key.from_xonly_pubkey(internal_key), merkle_root)
120
+ end
121
+
122
+ # Calculate merkle root from branches.
123
+ # @return [String] merkle root with hex format.
124
+ def merkle_root
125
+ parents = branches.map {|pair| combine_hash(pair)}
110
126
  if parents.empty?
111
127
  parents = ['']
128
+ elsif parents.size == 1
129
+ parents = [combine_hash(parents)]
112
130
  else
113
131
  parents = parents.each_slice(2).map { |pair| combine_hash(pair) } until parents.size == 1
114
132
  end
115
- t = Bitcoin.tagged_hash('TapTweak', internal_key.htb + parents.first)
116
- raise Error, 'tweak value exceeds the curve order' if t.bti >= ECDSA::Group::Secp256k1.order
117
- t
133
+ parents.first.bth
118
134
  end
119
135
 
120
136
  def combine_hash(pair)
@@ -5,5 +5,41 @@ module Bitcoin
5
5
 
6
6
  autoload :LeafNode, 'bitcoin/taproot/leaf_node'
7
7
  autoload :SimpleBuilder, 'bitcoin/taproot/simple_builder'
8
+
9
+ module_function
10
+
11
+ # Calculate tweak value from +internal_pubkey+ and +merkle_root+.
12
+ # @param [Bitcoin::Key] internal_key Internal key with hex format(x-only public key).
13
+ # @param [String] merkle_root Merkle root value of script tree with hex format.
14
+ # @return [String] teak value with binary format.
15
+ def tweak(internal_key, merkle_root)
16
+ raise Error, 'internal_key must be Bitcoin::Key object.' unless internal_key.is_a?(Bitcoin::Key)
17
+
18
+ merkle_root ||= ''
19
+ t = Bitcoin.tagged_hash('TapTweak', internal_key.xonly_pubkey.htb + merkle_root.htb)
20
+ raise Error, 'tweak value exceeds the curve order' if t.bti >= ECDSA::Group::Secp256k1.order
21
+
22
+ t
23
+ end
24
+
25
+ # Generate tweak public key form +internal_pubkey+ and +merkle_root+.
26
+ # @param [Bitcoin::Key] internal_key Internal key with hex format(x-only public key).
27
+ # @param [String] merkle_root Merkle root value of script tree with hex format.
28
+ # @return [Bitcoin::Key] Tweaked public key.
29
+ def tweak_public_key(internal_key, merkle_root)
30
+ t = tweak(internal_key, merkle_root)
31
+ key = Bitcoin::Key.new(priv_key: t.bth, key_type: Key::TYPES[:compressed])
32
+ Bitcoin::Key.from_point(key.to_point + internal_key.to_point)
33
+ end
34
+
35
+ # Generate tweak private key
36
+ #
37
+ def tweak_private_key(internal_private_key, merkle_root)
38
+ p = internal_private_key.to_point
39
+ private_key = p.has_even_y? ? internal_private_key.priv_key.to_i(16) :
40
+ ECDSA::Group::Secp256k1.order - internal_private_key.priv_key.to_i(16)
41
+ t = tweak(internal_private_key, merkle_root)
42
+ Bitcoin::Key.new(priv_key: ((t.bti + private_key) % ECDSA::Group::Secp256k1.order).to_even_length_hex)
43
+ end
8
44
  end
9
45
  end
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-29 00:00:00.000000000 Z
11
+ date: 2021-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -514,7 +514,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
514
514
  - !ruby/object:Gem::Version
515
515
  version: '0'
516
516
  requirements: []
517
- rubygems_version: 3.2.3
517
+ rubygems_version: 3.2.22
518
518
  signing_key:
519
519
  specification_version: 4
520
520
  summary: The implementation of Bitcoin Protocol for Ruby.