bitcoinrb 1.8.0 → 1.8.2

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: a0cc5450016d6ffbb76d36dcccd415a485a8532ab8414d9beddb9a7e25305dba
4
- data.tar.gz: 75d509c36069ce2dee667a0330962ae310c8062f24aa01d299e5692f6f069461
3
+ metadata.gz: cdae8f671d9fc07798c24ca9293a06ec684e840df4c506e67e2b5945d9b17658
4
+ data.tar.gz: 6a6a7c20a40c0f1be079da17c15b137240336ca6db029d78246bcc0154aee80f
5
5
  SHA512:
6
- metadata.gz: c2457464548bd24c937eaf55aa53ff2b7ca01b5db0b57b66d7e6fef60142cde6ff2752d670e06bd6052470b6eef25af9f3b676ffe1842fd98c1fd9d590888556
7
- data.tar.gz: 95d5649e3cacc1a155b0e033f0275d9f1806a9e39aad2bbe0c9b61809a10b76d117dddf94c0c68eae50e9dd52aa8e3f266244e2a400821d9172e2706a371c654
6
+ metadata.gz: 359a3c6d2883a5ae3bda6ffce3987cf01b36c47c29e94d39006d684f48c16cebc892408ddd6c1af1bc27cd9e0d23e18634a219ab6ec585b230d33eeb90402156
7
+ data.tar.gz: 16b6e7e8c5cf702a2f1d9c56a20815b9a41db5aa9e16b60e8dc7a109af56924f98674ec86c3925dd5fd71c9fd3ec0fe4c2a47beac1cd960c0b7a0a87f425fff4
data/README.md CHANGED
@@ -83,6 +83,8 @@ This parameter is described in https://github.com/chaintope/bitcoinrb/blob/maste
83
83
 
84
84
  ```ruby
85
85
  Bitcoin.chain_params = :testnet
86
+ # or
87
+ Bitcoin.chain_params = :testnet4
86
88
  ```
87
89
 
88
90
  This parameter is described in https://github.com/chaintope/bitcoinrb/blob/master/lib/bitcoin/chainparams/testnet.yml.
data/bitcoinrb.gemspec CHANGED
@@ -23,17 +23,17 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'ecdsa_ext', '~> 0.5.1'
24
24
  spec.add_runtime_dependency 'eventmachine'
25
25
  spec.add_runtime_dependency 'murmurhash3', '~> 0.1.7'
26
- spec.add_runtime_dependency 'bech32', '>= 1.3.0'
26
+ spec.add_runtime_dependency 'bech32', '>= 1.5.0'
27
27
  spec.add_runtime_dependency 'daemon-spawn'
28
28
  spec.add_runtime_dependency 'thor'
29
29
  spec.add_runtime_dependency 'leb128', '~> 1.0.0'
30
30
  spec.add_runtime_dependency 'eventmachine_httpserver'
31
31
  spec.add_runtime_dependency 'iniparse'
32
32
  spec.add_runtime_dependency 'siphash'
33
- spec.add_runtime_dependency 'json_pure', '>= 2.3.1', '< 2.8.0'
34
33
  spec.add_runtime_dependency 'bip-schnorr', '>= 0.7.0'
35
34
  spec.add_runtime_dependency 'base32', '>= 0.3.4'
36
35
  spec.add_runtime_dependency 'base64', '~> 0.2.0'
37
36
  spec.add_runtime_dependency 'observer', '~> 0.1.2'
38
37
  spec.add_runtime_dependency 'secp256k1rb', '0.1.1'
38
+ spec.add_runtime_dependency 'logger'
39
39
  end
data/lib/bitcoin/ext.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  module Bitcoin
2
2
  module Ext
3
- autoload :JsonParser, 'bitcoin/ext/json_parser'
4
3
  autoload :ArrayExt, 'bitcoin/ext/array_ext'
5
4
  autoload :ObjectExt, 'bitcoin/ext/object_ext'
6
5
  end
data/lib/bitcoin/key.rb CHANGED
@@ -34,6 +34,9 @@ module Bitcoin
34
34
  @key_type = key_type
35
35
  compressed = @key_type != TYPES[:uncompressed]
36
36
  else
37
+ if pubkey && compressed && pubkey.length != COMPRESSED_PUBLIC_KEY_SIZE * 2
38
+ raise ArgumentError, "Invalid compressed pubkey length."
39
+ end
37
40
  @key_type = compressed ? TYPES[:compressed] : TYPES[:uncompressed]
38
41
  end
39
42
  @secp256k1_module = Bitcoin.secp_impl
@@ -62,7 +62,8 @@ module Bitcoin
62
62
  request.body = data.to_json
63
63
  response = http.request(request)
64
64
  body = response.body
65
- response = Bitcoin::Ext::JsonParser.new(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') }).parse
65
+ json_data = JSON.parse(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') })
66
+ response = convert_floats_to_strings(json_data)
66
67
  raise response['error'].to_json if response['error']
67
68
  response['result']
68
69
  end
@@ -77,6 +78,19 @@ module Bitcoin
77
78
  end
78
79
  end
79
80
 
81
+ # Convert float value
82
+ def convert_floats_to_strings(obj)
83
+ case obj
84
+ when Float
85
+ obj.to_s
86
+ when Hash
87
+ obj.transform_values { |v| convert_floats_to_strings(v) }
88
+ when Array
89
+ obj.map { |item| convert_floats_to_strings(item) }
90
+ else
91
+ obj
92
+ end
93
+ end
80
94
  end
81
95
  end
82
96
  end
@@ -37,7 +37,7 @@ module Bitcoin
37
37
 
38
38
  # check schnorr signature.
39
39
  # @param [String] sig schnorr signature with hex format.
40
- # @param [String] pubkey a public key with hex fromat.
40
+ # @param [String] pubkey a public key with hex format.
41
41
  # @param [Symbol] sig_version whether :taproot or :tapscript
42
42
  # @return [Boolean] verification result
43
43
  def check_schnorr_sig(sig, pubkey, sig_version, opts = {})
@@ -1,5 +1,92 @@
1
1
  module Bitcoin
2
2
  module SilentPayment
3
- autoload :Addr, 'bitcoin/sp/addr'
3
+
4
+
5
+ # Derive payment
6
+ # @param [Array] prevouts An array of previous output script(Bitcoin::Script).
7
+ # @param [Array] private_keys An array of private key corresponding to each public key in prevouts.
8
+ # @param [Array] recipients
9
+ # @return [Array]
10
+ # @raise [ArgumentError]
11
+ def derive_payment_points(prevouts, private_keys, recipients)
12
+ raise ArgumentError, "prevouts must be Array." unless prevouts.is_a? Array
13
+ raise ArgumentError, "private_keys must be Array." unless private_keys.is_a? Array
14
+ raise ArgumentError, "prevouts and private_keys must be the same length." unless prevouts.length == private_keys.length
15
+ raise ArgumentError, "recipients must be Array." unless recipients.is_a? Array
16
+
17
+ outpoint_l = inputs.map{|i|i.out_point.to_hex}.sort.first
18
+
19
+ input_pub_keys = []
20
+ field = ECDSA::PrimeField.new(Bitcoin::Secp256k1::GROUP.order)
21
+ sum_priv_keys = 0
22
+ prevouts.each_with_index do |prevout, index|
23
+ k = Bitcoin::Key.new(priv_key: private_keys[index].to_s(16))
24
+ public_key = extract_public_key(prevout, inputs[index])
25
+ next if public_key.nil?
26
+ private_key = if public_key.p2tr? && k.to_point.y.odd?
27
+ field.mod(-private_keys[index])
28
+ else
29
+ private_keys[index]
30
+ end
31
+ input_pub_keys << public_key
32
+ sum_priv_keys = field.mod(sum_priv_keys + private_key)
33
+ end
34
+ agg_pubkey = (Bitcoin::Secp256k1::GROUP.generator.to_jacobian * sum_priv_keys).to_affine
35
+ return [] if agg_pubkey.infinity?
36
+
37
+ input_hash = Bitcoin.tagged_hash("BIP0352/Inputs", outpoint_l.htb + agg_pubkey.to_hex.htb).bth
38
+
39
+ destinations = {}
40
+ recipients.each do |sp_addr|
41
+ raise ArgumentError, "recipients element must be Bech32::SilentPaymentAddr." unless sp_addr.is_a? Bech32::SilentPaymentAddr
42
+ destinations[sp_addr.scan_key] = [] unless destinations.has_key?(sp_addr.scan_key)
43
+ destinations[sp_addr.scan_key] << sp_addr.spend_key
44
+ end
45
+ outputs = []
46
+ destinations.each do |scan_key, spends|
47
+ scan_key = Bitcoin::Key.new(pubkey: scan_key).to_point.to_jacobian
48
+ ecdh_shared_secret = (scan_key * field.mod(input_hash.to_i(16) * sum_priv_keys)).to_affine.to_hex.htb
49
+ spends.each.with_index do |spend, i|
50
+ t_k = Bitcoin.tagged_hash('BIP0352/SharedSecret', ecdh_shared_secret + [i].pack('N'))
51
+ spend_key = Bitcoin::Key.new(pubkey: spend).to_point.to_jacobian
52
+ outputs << (spend_key + Bitcoin::Secp256k1::GROUP.generator.to_jacobian * t_k.bth.to_i(16)).to_affine
53
+ end
54
+ end
55
+ outputs
56
+ end
57
+
58
+ # Extract public keys from +prevout+ and input.
59
+ def extract_public_key(prevout, input)
60
+ if prevout.p2pkh?
61
+ spk_hash = prevout.chunks[2].pushed_data.bth
62
+ input.script_sig.chunks.reverse.each do |chunk|
63
+ next unless chunk.pushdata?
64
+ pubkey = chunk.pushed_data.bth
65
+ if Bitcoin.hash160(pubkey) == spk_hash
66
+ return Bitcoin::Key.new(pubkey: pubkey) if pubkey.htb.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
67
+ end
68
+ end
69
+ elsif prevout.p2sh?
70
+ redeem_script = Bitcoin::Script.parse_from_payload(input.script_sig.chunks.last.pushed_data)
71
+ if redeem_script.p2wpkh?
72
+ pk = input.script_witness.stack.last
73
+ return Bitcoin::Key.new(pubkey: pk.bth) if pk.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
74
+ end
75
+ elsif prevout.p2wpkh?
76
+ pk = input.script_witness.stack.last
77
+ return Bitcoin::Key.new(pubkey: pk.bth) if pk.bytesize == Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE
78
+ elsif prevout.p2tr?
79
+ witness_stack = input.script_witness.stack.dup
80
+ witness_stack.pop if witness_stack.last.bth.start_with?("50")
81
+ if witness_stack.length > 1
82
+ # script-path
83
+ cb = Bitcoin::Taproot::ControlBlock.parse_from_payload(witness_stack.last)
84
+ return nil if cb.internal_key == Bitcoin::Taproot::NUMS_H
85
+ end
86
+ pubkey = Bitcoin::Key.from_xonly_pubkey(prevout.chunks[1].pushed_data.bth)
87
+ return pubkey if pubkey.compressed?
88
+ end
89
+ nil
90
+ end
4
91
  end
5
92
  end
@@ -8,6 +8,8 @@ module Bitcoin
8
8
  autoload :SimpleBuilder, 'bitcoin/taproot/simple_builder'
9
9
  autoload :CustomDepthBuilder, 'bitcoin/taproot/custom_depth_builder'
10
10
 
11
+ NUMS_H = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
12
+
11
13
  module_function
12
14
 
13
15
  # Calculate tweak value from +internal_pubkey+ and +merkle_root+.
data/lib/bitcoin/tx.rb CHANGED
@@ -7,6 +7,7 @@ module Bitcoin
7
7
  class Tx
8
8
 
9
9
  include Bitcoin::HexConverter
10
+ include SilentPayment
10
11
 
11
12
  MAX_STANDARD_VERSION = 2
12
13
 
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "1.8.0"
2
+ VERSION = "1.8.2"
3
3
  end
data/lib/bitcoin.rb CHANGED
@@ -71,9 +71,11 @@ module Bitcoin
71
71
 
72
72
  @chain_param = :mainnet
73
73
 
74
+ AVAILABLE_NETWORKS = %i(mainnet testnet regtest signet testnet4)
75
+
74
76
  # set bitcoin network chain params
75
77
  def self.chain_params=(name)
76
- raise "chain params for #{name} is not defined." unless %i(mainnet testnet regtest signet).include?(name.to_sym)
78
+ raise "chain params for #{name} is not defined." unless AVAILABLE_NETWORKS.include?(name.to_sym)
77
79
  @current_chain = nil
78
80
  @chain_param = name.to_sym
79
81
  end
@@ -90,6 +92,8 @@ module Bitcoin
90
92
  @current_chain = Bitcoin::ChainParams.regtest
91
93
  when :signet
92
94
  @current_chain = Bitcoin::ChainParams.signet
95
+ when :testnet4
96
+ @current_chain = Bitcoin::ChainParams.testnet4
93
97
  end
94
98
  @current_chain
95
99
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-02-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ecdsa_ext
@@ -57,14 +57,14 @@ dependencies:
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 1.3.0
60
+ version: 1.5.0
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
- version: 1.3.0
67
+ version: 1.5.0
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: daemon-spawn
70
70
  requirement: !ruby/object:Gem::Requirement
@@ -149,26 +149,6 @@ dependencies:
149
149
  - - ">="
150
150
  - !ruby/object:Gem::Version
151
151
  version: '0'
152
- - !ruby/object:Gem::Dependency
153
- name: json_pure
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - ">="
157
- - !ruby/object:Gem::Version
158
- version: 2.3.1
159
- - - "<"
160
- - !ruby/object:Gem::Version
161
- version: 2.8.0
162
- type: :runtime
163
- prerelease: false
164
- version_requirements: !ruby/object:Gem::Requirement
165
- requirements:
166
- - - ">="
167
- - !ruby/object:Gem::Version
168
- version: 2.3.1
169
- - - "<"
170
- - !ruby/object:Gem::Version
171
- version: 2.8.0
172
152
  - !ruby/object:Gem::Dependency
173
153
  name: bip-schnorr
174
154
  requirement: !ruby/object:Gem::Requirement
@@ -239,6 +219,20 @@ dependencies:
239
219
  - - '='
240
220
  - !ruby/object:Gem::Version
241
221
  version: 0.1.1
222
+ - !ruby/object:Gem::Dependency
223
+ name: logger
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - ">="
227
+ - !ruby/object:Gem::Version
228
+ version: '0'
229
+ type: :runtime
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
242
236
  description: The implementation of Bitcoin Protocol for Ruby.
243
237
  email:
244
238
  - azuchi@chaintope.com
@@ -308,7 +302,6 @@ files:
308
302
  - lib/bitcoin/ext.rb
309
303
  - lib/bitcoin/ext/array_ext.rb
310
304
  - lib/bitcoin/ext/ecdsa.rb
311
- - lib/bitcoin/ext/json_parser.rb
312
305
  - lib/bitcoin/ext/object_ext.rb
313
306
  - lib/bitcoin/ext_key.rb
314
307
  - lib/bitcoin/gcs_filter.rb
@@ -411,7 +404,6 @@ files:
411
404
  - lib/bitcoin/slip39/share.rb
412
405
  - lib/bitcoin/slip39/sss.rb
413
406
  - lib/bitcoin/slip39/wordlist/english.txt
414
- - lib/bitcoin/sp/addr.rb
415
407
  - lib/bitcoin/store.rb
416
408
  - lib/bitcoin/store/chain_entry.rb
417
409
  - lib/bitcoin/store/db.rb
@@ -457,7 +449,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
457
449
  - !ruby/object:Gem::Version
458
450
  version: '0'
459
451
  requirements: []
460
- rubygems_version: 3.6.2
452
+ rubygems_version: 3.6.9
461
453
  specification_version: 4
462
454
  summary: The implementation of Bitcoin Protocol for Ruby.
463
455
  test_files: []
@@ -1,46 +0,0 @@
1
- require 'json/pure'
2
-
3
- module Bitcoin
4
- module Ext
5
- # Extension of JSON::Pure::Parser.
6
- # This class convert Float value to String value.
7
- class JsonParser < JSON::Pure::Parser
8
-
9
- def parse_value
10
- case
11
- when scan(FLOAT)
12
- self[1].to_s
13
- when scan(INTEGER)
14
- Integer(self[1])
15
- when scan(TRUE)
16
- true
17
- when scan(FALSE)
18
- false
19
- when scan(NULL)
20
- nil
21
- when !UNPARSED.equal?(string = parse_string)
22
- string
23
- when scan(ARRAY_OPEN)
24
- @current_nesting += 1
25
- ary = parse_array
26
- @current_nesting -= 1
27
- ary
28
- when scan(OBJECT_OPEN)
29
- @current_nesting += 1
30
- obj = parse_object
31
- @current_nesting -= 1
32
- obj
33
- when @allow_nan && scan(NAN)
34
- NaN
35
- when @allow_nan && scan(INFINITY)
36
- Infinity
37
- when @allow_nan && scan(MINUS_INFINITY)
38
- MinusInfinity
39
- else
40
- UNPARSED
41
- end
42
- end
43
-
44
- end
45
- end
46
- end
@@ -1,55 +0,0 @@
1
- module Bitcoin
2
- module SilentPayment
3
- class Addr
4
-
5
- HRP_MAINNET = 'sp'
6
- HRP_TESTNET = 'tsp'
7
- MAX_CHARACTERS = 1023
8
-
9
- attr_reader :version
10
- attr_reader :scan_key
11
- attr_reader :spend_key
12
-
13
- # Constructor.
14
- # @param [Bitcoin::Key] scan_key
15
- # @param [Bitcoin::Key] spend_key
16
- def initialize(version, scan_key:, spend_key:)
17
- raise ArgumentError, "version must be integer." unless version.is_a?(Integer)
18
- raise ArgumentError, "scan_key must be Bitcoin::Key." unless scan_key.is_a?(Bitcoin::Key)
19
- raise ArgumentError, "spend_key must be Bitcoin::Key." unless spend_key.is_a?(Bitcoin::Key)
20
- raise ArgumentError, "version '#{version}' is unsupported." unless version.zero?
21
-
22
- @version = version
23
- @scan_key = scan_key
24
- @spend_key = spend_key
25
- end
26
-
27
- # Parse silent payment address.
28
- # @param [String] A silent payment address.
29
- # @return [Bitcoin::SilentPayment::Addr]
30
- def self.from_string(addr)
31
- raise ArgumentError, "addr must be string." unless addr.is_a?(String)
32
- hrp, data, spec = Bech32.decode(addr, MAX_CHARACTERS)
33
- unless hrp == Bitcoin.chain_params.mainnet? ? HRP_MAINNET : HRP_TESTNET
34
- raise ArgumentError, "The specified hrp is different from the current network HRP."
35
- end
36
- raise ArgumentError, "spec must be bech32m." unless spec == Bech32::Encoding::BECH32M
37
-
38
- ver = data[0]
39
- payload = Bech32.convert_bits(data[1..-1], 5, 8, false).pack("C*")
40
- scan_key = Bitcoin::Key.new(pubkey: payload[0...33].bth, key_type: Bitcoin::Key::TYPES[:compressed])
41
- spend_key = Bitcoin::Key.new(pubkey: payload[33..-1].bth, key_type: Bitcoin::Key::TYPES[:compressed])
42
- Addr.new(ver, scan_key: scan_key, spend_key: spend_key)
43
- end
44
-
45
- # Get silent payment address.
46
- # @return [String]
47
- def to_s
48
- hrp = Bitcoin.chain_params.mainnet? ? HRP_MAINNET : HRP_TESTNET
49
- payload = [scan_key.pubkey + spend_key.pubkey].pack("H*").unpack('C*')
50
- Bech32.encode(hrp, [version] + Bech32.convert_bits(payload, 8, 5), Bech32::Encoding::BECH32M)
51
- end
52
-
53
- end
54
- end
55
- end