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 +4 -4
- data/.ruby-version +1 -1
- data/lib/bitcoin/key.rb +0 -2
- data/lib/bitcoin/psbt/tx.rb +9 -0
- data/lib/bitcoin/psbt.rb +8 -0
- data/lib/bitcoin/sighash_generator.rb +1 -0
- data/lib/bitcoin/taproot/leaf_node.rb +1 -1
- data/lib/bitcoin/taproot/simple_builder.rb +59 -43
- data/lib/bitcoin/taproot.rb +36 -0
- data/lib/bitcoin/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8103017ba6b480dc45caea8b9544720003c486167c3e867be738ccdc40faef7
|
4
|
+
data.tar.gz: 3807ee81a2ed26a526c19903c616ebb7af5b2a9c8434a76398e538a02c34e126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97a9df2fee4c9d2fb7c84555d82e0f1ef692e0889c964fc7615f328467925a5b099be19801b19321a9b12fc55493663f1813be4074e5fc95add8948cd52ca2e4
|
7
|
+
data.tar.gz: '0370268e40a85bf6face9592b8dc3cf260b6357e796f62c7e6abddaeeef2d3ec561e4336ba859c5f8426dcedd80eba6d9ec632941e284791df9571ff613182e5'
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-3.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
|
|
data/lib/bitcoin/psbt/tx.rb
CHANGED
@@ -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 :
|
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::
|
16
|
-
# @
|
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,
|
18
|
+
def initialize(internal_key, leaves = [])
|
20
19
|
raise Error, 'Internal public key must be 32 bytes' unless internal_key.htb.bytesize == 32
|
21
|
-
|
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
|
26
|
-
# @param [Bitcoin::
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
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
|
-
|
55
|
-
|
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::
|
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(
|
64
|
-
path = inclusion_proof(
|
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 +
|
70
|
-
# @param [Bitcoin::
|
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
|
-
|
74
|
-
|
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
|
-
|
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
|
-
|
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)
|
data/lib/bitcoin/taproot.rb
CHANGED
@@ -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
|
data/lib/bitcoin/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|