bitcoinrb 1.8.1 → 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: a6e5ff6fa694353e122cd0028354edff2aa877debdc36d368503c25f6277c77f
4
- data.tar.gz: a6ef9fb214582173d6681dcad6547208b6c606f9d4b82f1c2b3423f388866db1
3
+ metadata.gz: cdae8f671d9fc07798c24ca9293a06ec684e840df4c506e67e2b5945d9b17658
4
+ data.tar.gz: 6a6a7c20a40c0f1be079da17c15b137240336ca6db029d78246bcc0154aee80f
5
5
  SHA512:
6
- metadata.gz: b4b858790060163880d19b8d771383b406facaa755386e7919e8c6d1caa6b7f362eeb611846f13f40b406114cae7d3e100cf4f5cb46adef01e37d40a43c35de6
7
- data.tar.gz: a4712b529713ad39bebd6f32d9124d2f07d3c77c22482297df550f384b6f3ebf0ee6000958494684a95cc70354f95a22d8361dd686241c2d67c645fcee20ea5c
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,7 +23,7 @@ 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'
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
@@ -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.1"
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.1
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-03-18 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
@@ -404,7 +404,6 @@ files:
404
404
  - lib/bitcoin/slip39/share.rb
405
405
  - lib/bitcoin/slip39/sss.rb
406
406
  - lib/bitcoin/slip39/wordlist/english.txt
407
- - lib/bitcoin/sp/addr.rb
408
407
  - lib/bitcoin/store.rb
409
408
  - lib/bitcoin/store/chain_entry.rb
410
409
  - lib/bitcoin/store/db.rb
@@ -450,7 +449,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
450
449
  - !ruby/object:Gem::Version
451
450
  version: '0'
452
451
  requirements: []
453
- rubygems_version: 3.6.3
452
+ rubygems_version: 3.6.9
454
453
  specification_version: 4
455
454
  summary: The implementation of Bitcoin Protocol for Ruby.
456
455
  test_files: []
@@ -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