bitcoinrb 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81a899ff49ba8888d3479e7f0c93c2124098c07c68dfd293349881d724c9393b
4
- data.tar.gz: '052092011357a7abb68ec74bbbd5939b74d75aa83d5561ce088fc80c152b3341'
3
+ metadata.gz: a0cc5450016d6ffbb76d36dcccd415a485a8532ab8414d9beddb9a7e25305dba
4
+ data.tar.gz: 75d509c36069ce2dee667a0330962ae310c8062f24aa01d299e5692f6f069461
5
5
  SHA512:
6
- metadata.gz: ce9d912ba951f85c95dd30b29f1f2d0330a168e6fdd119296186fc6e651ba92ab838c90b7d31cf1a99e17def444c6bcb9784eff6ceab0816b210428fbc77b36a
7
- data.tar.gz: bc9f7c2e34f9f47def957b549e5c33f5a4940fb56ad2fb379f717811aedd0b5f1cc4089c0f67ff7ad1363eb9a7d7b4a4ea4405124a06b20a1674d02802720cfe
6
+ metadata.gz: c2457464548bd24c937eaf55aa53ff2b7ca01b5db0b57b66d7e6fef60142cde6ff2752d670e06bd6052470b6eef25af9f3b676ffe1842fd98c1fd9d590888556
7
+ data.tar.gz: 95d5649e3cacc1a155b0e033f0275d9f1806a9e39aad2bbe0c9b61809a10b76d117dddf94c0c68eae50e9dd52aa8e3f266244e2a400821d9172e2706a371c654
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['3.0', '3.1', '3.2', '3.3']
22
+ ruby-version: ['3.1', '3.2', '3.3', '3.4']
23
23
 
24
24
  steps:
25
25
  - uses: actions/checkout@v4
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.3.0
1
+ ruby-3.4.1
data/Gemfile CHANGED
@@ -4,3 +4,11 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'parallel_tests'
7
+ gem 'leveldb-native'
8
+ gem 'bundler'
9
+ gem 'rake', '>= 12.3.3'
10
+ gem 'rspec', '~> 3.0'
11
+ gem 'timecop'
12
+ gem 'webmock', '>= 3.11.1'
13
+ gem 'parallel', '>= 1.20.1'
14
+ gem 'csv', '~> 3.3'
data/bitcoinrb.gemspec CHANGED
@@ -26,22 +26,14 @@ Gem::Specification.new do |spec|
26
26
  spec.add_runtime_dependency 'bech32', '>= 1.3.0'
27
27
  spec.add_runtime_dependency 'daemon-spawn'
28
28
  spec.add_runtime_dependency 'thor'
29
- spec.add_runtime_dependency 'ffi'
30
29
  spec.add_runtime_dependency 'leb128', '~> 1.0.0'
31
30
  spec.add_runtime_dependency 'eventmachine_httpserver'
32
31
  spec.add_runtime_dependency 'iniparse'
33
32
  spec.add_runtime_dependency 'siphash'
34
- spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
33
+ spec.add_runtime_dependency 'json_pure', '>= 2.3.1', '< 2.8.0'
35
34
  spec.add_runtime_dependency 'bip-schnorr', '>= 0.7.0'
36
35
  spec.add_runtime_dependency 'base32', '>= 0.3.4'
37
-
38
- # for options
39
- spec.add_development_dependency 'leveldb-native'
40
-
41
- spec.add_development_dependency 'bundler'
42
- spec.add_development_dependency 'rake', '>= 12.3.3'
43
- spec.add_development_dependency 'rspec', '~> 3.0'
44
- spec.add_development_dependency 'timecop'
45
- spec.add_development_dependency 'webmock', '>= 3.11.1'
46
- spec.add_development_dependency 'parallel', '>= 1.20.1'
36
+ spec.add_runtime_dependency 'base64', '~> 0.2.0'
37
+ spec.add_runtime_dependency 'observer', '~> 0.1.2'
38
+ spec.add_runtime_dependency 'secp256k1rb', '0.1.1'
47
39
  end
@@ -19,7 +19,7 @@ module Bitcoin
19
19
  # Decode to public key.
20
20
  # @return [Bitcoin::Key] Decoded public key.
21
21
  def decode
22
- if Bitcoin.secp_impl.is_a?(Bitcoin::Secp256k1::Native)
22
+ if Bitcoin.secp_impl.native?
23
23
  pubkey = Bitcoin.secp_impl.ellswift_decode(key)
24
24
  Bitcoin::Key.new(pubkey: pubkey, key_type: Bitcoin::Key::TYPES[:compressed])
25
25
  else
@@ -131,8 +131,8 @@ module Bitcoin
131
131
  raise ArgumentError, "ellswift_theirs must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_theirs.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
132
132
  raise ArgumentError, "ellswift_ours must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_ours.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
133
133
 
134
- if Bitcoin.secp_impl.is_a?(Bitcoin::Secp256k1::Native)
135
- Bitcoin::Secp256k1::Native.ellswift_ecdh_xonly(ellswift_theirs, ellswift_ours, priv_key, initiating)
134
+ if Bitcoin.secp_impl.native?
135
+ Bitcoin::Secp256k1::Native.ellswift_ecdh_xonly(ellswift_theirs.key, ellswift_ours.key, priv_key, initiating)
136
136
  else
137
137
  ecdh_point_x32 = ellswift_ecdh_xonly(ellswift_theirs, priv_key).htb
138
138
  content = initiating ? ellswift_ours.key + ellswift_theirs.key + ecdh_point_x32 :
@@ -56,6 +56,11 @@ module Bitcoin
56
56
  init('signet')
57
57
  end
58
58
 
59
+ # testnet 4 genesis
60
+ def self.testnet4
61
+ init('testnet4')
62
+ end
63
+
59
64
  def mainnet?
60
65
  network == 'mainnet'
61
66
  end
@@ -72,6 +77,10 @@ module Bitcoin
72
77
  network == 'signet'
73
78
  end
74
79
 
80
+ def testnet4?
81
+ network == 'testnet4'
82
+ end
83
+
75
84
  def genesis_block
76
85
  header = Bitcoin::BlockHeader.new(
77
86
  genesis['version'], genesis['prev_hash'].rhex, genesis['merkle_root'].rhex,
@@ -27,9 +27,13 @@ proof_of_work_limit: 0x1d00ffff
27
27
  dns_seeds:
28
28
  - "seed.bitcoin.sipa.be"
29
29
  - "dnsseed.bluematt.me"
30
- - "dnsseed.bitcoin.dashjr.org"
31
- - "seed.bitcoinstats.com"
30
+ - "dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us"
32
31
  - "seed.bitcoin.jonasschnelli.ch"
32
+ - "seed.btc.petertodd.net"
33
+ - "seed.bitcoin.sprovoost.nl"
34
+ - "dnsseed.emzy.de"
35
+ - "seed.bitcoin.wiz.biz"
36
+ - "seed.mainnet.achownodes.xyz"
33
37
  genesis:
34
38
  hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
35
39
  merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
@@ -22,8 +22,7 @@ retarget_interval: 2016
22
22
  retarget_time: 1209600 # 2 weeks
23
23
  target_spacing: 600 # block interval
24
24
  max_money: 21000000
25
- bip34_height: 0
26
- genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
25
+ bip34_height: 1
27
26
  proof_of_work_limit: 0x207fffff
28
27
  dns_seeds:
29
28
  genesis:
@@ -22,12 +22,11 @@ retarget_interval: 2016
22
22
  retarget_time: 1209600 # 2 weeks
23
23
  target_spacing: 600 # block interval
24
24
  max_money: 21000000
25
- bip34_height: 227931
26
- genesis_hash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
25
+ bip34_height: 1
27
26
  proof_of_work_limit: 0x1d00ffff
28
27
  dns_seeds:
29
- - "178.128.221.177"
30
- - "2a01:7c8:d005:390::5"
28
+ - "seed.signet.bitcoin.sprovoost.nl"
29
+ - "seed.signet.achownodes.xyz"
31
30
  genesis:
32
31
  hash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"
33
32
  merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
@@ -22,14 +22,14 @@ retarget_interval: 2016
22
22
  retarget_time: 1209600 # 2 weeks
23
23
  target_spacing: 600 # block interval
24
24
  max_money: 21000000
25
- bip34_height: 227931
26
- genesis_hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
25
+ bip34_height: 21111
27
26
  proof_of_work_limit: 0x1d00ffff
28
27
  dns_seeds:
29
28
  - "testnet-seed.bitcoin.jonasschnelli.ch"
30
- - "seed.tbtc.petertodd.org"
29
+ - "seed.tbtc.petertodd.net"
30
+ - "seed.testnet.bitcoin.sprovoost.nl"
31
31
  - "testnet-seed.bluematt.me"
32
- - "testnet-seed.bitcoin.schildbach.de"
32
+ - "seed.testnet.achownodes.xyz"
33
33
  genesis:
34
34
  hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
35
35
  merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
@@ -0,0 +1,38 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "testnet4"
3
+ magic_head: "1c163f28"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "6f"
6
+ p2sh_version: "c4"
7
+ bech32_hrp: 'tb'
8
+ privkey_version: "ef"
9
+ extended_privkey_version: "04358394"
10
+ extended_pubkey_version: "043587cf"
11
+ bip49_pubkey_p2wpkh_p2sh_version: "044a5262"
12
+ bip49_pubkey_p2wsh_p2sh_version: "024289ef"
13
+ bip49_privkey_p2wpkh_p2sh_version: "044a4e28"
14
+ bip49_privkey_p2wsh_p2sh_version: "024285b5"
15
+ bip84_pubkey_p2wpkh_version: "045f1cf6"
16
+ bip84_pubkey_p2wsh_version: "02575483"
17
+ bip84_privkey_p2wpkh_version: "045f18bc"
18
+ bip84_privkey_p2wsh_version: "02575048"
19
+ default_port: 48333
20
+ protocol_version: 70013
21
+ retarget_interval: 2016
22
+ retarget_time: 1209600 # 2 weeks
23
+ target_spacing: 600 # block interval
24
+ max_money: 21000000
25
+ bip34_height: 1
26
+ proof_of_work_limit: 0x1d00ffff
27
+ dns_seeds:
28
+ - "seed.testnet4.bitcoin.sprovoost.nl"
29
+ - "seed.testnet4.wiz.biz"
30
+ genesis:
31
+ hash: "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043"
32
+ merkle_root: "7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e"
33
+ time: 1714777860
34
+ nonce: 393743547
35
+ bits: 0x1d00ffff
36
+ version: 1
37
+ prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
38
+ bip44_coin_type: 1
data/lib/bitcoin/key.rb CHANGED
@@ -146,11 +146,8 @@ module Bitcoin
146
146
  # @return [Bitcoin::Key] Recovered public key.
147
147
  def self.recover_compact(data, signature)
148
148
  rec_id = signature.unpack1('C')
149
- rec = (rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE) & 3
150
- raise ArgumentError, 'Invalid signature parameter' if rec < 0 || rec > 3
151
-
152
149
  compressed = (rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE) & 4 != 0
153
- Bitcoin.secp_impl.recover_compact(data, signature, rec, compressed)
150
+ Bitcoin.secp_impl.recover_compact(data, signature, compressed)
154
151
  end
155
152
 
156
153
  # verify signature using public key
@@ -349,7 +346,7 @@ module Bitcoin
349
346
  # @raise ArgumentError If ent32 does not 32 bytes.
350
347
  def create_ell_pubkey
351
348
  raise ArgumentError, "private_key required." unless priv_key
352
- if secp256k1_module.is_a?(Bitcoin::Secp256k1::Native)
349
+ if secp256k1_module.native?
353
350
  Bitcoin::BIP324::EllSwiftPubkey.new(secp256k1_module.ellswift_create(priv_key))
354
351
  else
355
352
  Bitcoin::BIP324::EllSwiftPubkey.new(Bitcoin::BIP324.xelligatorswift(xonly_pubkey))
@@ -62,13 +62,23 @@ module Bitcoin
62
62
  pubkey = Bitcoin::Key.recover_compact(message_hash(message, prefix: prefix, legacy: true), sig)
63
63
  return false unless pubkey
64
64
  pubkey.to_p2pkh == address
65
- rescue RuntimeError
66
- return false
65
+ rescue Exception
66
+ false
67
67
  end
68
68
  elsif addr_script.witness_program?
69
69
  # BIP322 verification
70
- tx = to_sign_tx(message_hash(message, prefix: prefix, legacy: false), address)
71
- tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig)
70
+ digest = message_hash(message, prefix: prefix, legacy: false)
71
+ begin
72
+ # Full
73
+ tx = Bitcoin::Tx.parse_from_payload(sig)
74
+ validate_to_sign_tx!(tx)
75
+ to_spend = to_spend_tx(digest, address)
76
+ return false unless tx.in[0].out_point.tx_hash == to_spend.tx_hash
77
+ rescue Exception
78
+ # Simple
79
+ tx = to_sign_tx(digest, address)
80
+ tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig)
81
+ end
72
82
  script_pubkey = Bitcoin::Script.parse_from_addr(address)
73
83
  tx_out = Bitcoin::TxOut.new(script_pubkey: script_pubkey)
74
84
  flags = Bitcoin::STANDARD_SCRIPT_VERIFY_FLAGS
@@ -99,6 +109,14 @@ module Bitcoin
99
109
  end
100
110
  end
101
111
 
112
+ def validate_to_sign_tx!(tx)
113
+ raise ArgumentError, "Multiple inputs (proof of funds) are not supported." unless tx.in.length == 1
114
+ raise ArgumentError, "vin[0].prevout.n must be 0." unless tx.in[0].out_point.index == 0
115
+ raise ArgumentError, "Multiple outputs are not supported." unless tx.out.length == 1
116
+ raise ArgumentError, "vout[0].nValue must be 0." unless tx.out[0].value == 0
117
+ raise ArgumentError, "vout[0].scriptPubKey must be OP_RETURN." unless tx.out[0].script_pubkey == Bitcoin::Script.new << Bitcoin::Opcodes::OP_RETURN
118
+ end
119
+
102
120
  def to_spend_tx(digest, addr)
103
121
  validate_address!(addr)
104
122
  message_challenge = Bitcoin::Script.parse_from_addr(addr)
@@ -124,5 +142,6 @@ module Bitcoin
124
142
 
125
143
  private_class_method :validate_address!
126
144
  private_class_method :validate_format!
145
+ private_class_method :validate_to_sign_tx!
127
146
  end
128
147
  end
data/lib/bitcoin/psbt.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'base64'
2
-
3
1
  module Bitcoin
4
2
 
5
3
  module PSBT
@@ -1,5 +1,6 @@
1
1
  # Porting part of the code from bitcoin-ruby. see the license.
2
2
  # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+ require 'secp256k1'
3
4
 
4
5
  module Bitcoin
5
6
  module Secp256k1
@@ -10,91 +11,15 @@ module Bitcoin
10
11
  # for linux, ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so' or '/usr/lib64/libsecp256k1.so'
11
12
  # for mac,
12
13
  module Native
13
- include ::FFI::Library
14
- extend self
15
-
16
- SECP256K1_FLAGS_TYPE_MASK = ((1 << 8) - 1)
17
- SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
18
- SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1)
19
-
20
- SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
21
- SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
22
- SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8)
23
-
24
- # Flags to pass to secp256k1_context_create.
25
- SECP256K1_CONTEXT_VERIFY = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
26
- SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
27
-
28
- # Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export.
29
- SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
30
- SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
31
14
 
32
15
  module_function
33
16
 
34
- def init
35
- raise 'secp256k1 library dose not found.' unless File.exist?(ENV['SECP256K1_LIB_PATH'])
36
- ffi_lib(ENV['SECP256K1_LIB_PATH'])
37
- load_functions
38
- end
39
-
40
- def load_functions
41
- attach_function(:secp256k1_context_create, [:uint], :pointer)
42
- attach_function(:secp256k1_context_destroy, [:pointer], :void)
43
- attach_function(:secp256k1_context_randomize, [:pointer, :pointer], :int)
44
- attach_function(:secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int)
45
- attach_function(:secp256k1_ec_seckey_verify, [:pointer, :pointer], :int)
46
- attach_function(:secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
47
- attach_function(:secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int)
48
- attach_function(:secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int)
49
- attach_function(:secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int)
50
- attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
51
- attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
52
- attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
53
- attach_function(:secp256k1_schnorrsig_sign32, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
54
- attach_function(:secp256k1_schnorrsig_verify, [:pointer, :pointer, :pointer, :size_t, :pointer], :int)
55
- attach_function(:secp256k1_keypair_create, [:pointer, :pointer, :pointer], :int)
56
- attach_function(:secp256k1_xonly_pubkey_parse, [:pointer, :pointer, :pointer], :int)
57
- attach_function(:secp256k1_ecdsa_sign_recoverable, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
58
- attach_function(:secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int)
59
- attach_function(:secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int)
60
- attach_function(:secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int)
61
- attach_function(:secp256k1_ellswift_decode, [:pointer, :pointer, :pointer], :int)
62
- attach_function(:secp256k1_ellswift_create, [:pointer, :pointer, :pointer, :pointer], :int)
63
- # Define function pointer
64
- callback(:secp256k1_ellswift_xdh_hash_function, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
65
- attach_variable(:secp256k1_ellswift_xdh_hash_function_bip324, :secp256k1_ellswift_xdh_hash_function)
66
- attach_function(:secp256k1_ellswift_xdh, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :pointer, :pointer], :int)
67
- end
68
-
69
- def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
70
- init
71
- begin
72
- context = secp256k1_context_create(flags)
73
- ret, tries, max = 0, 0, 20
74
- while ret != 1
75
- raise 'secp256k1_context_randomize failed.' if tries >= max
76
- tries += 1
77
- ret = secp256k1_context_randomize(context, FFI::MemoryPointer.from_string(SecureRandom.random_bytes(32)))
78
- end
79
- yield(context) if block_given?
80
- ensure
81
- secp256k1_context_destroy(context)
82
- end
83
- end
17
+ extend ::Secp256k1
84
18
 
85
- # generate ec private key and public key
86
- def generate_key_pair(compressed: true)
87
- with_context do |context|
88
- ret, tries, max = 0, 0, 20
89
- while ret != 1
90
- raise 'secp256k1_ec_seckey_verify in generate_key_pair failed.' if tries >= max
91
- tries += 1
92
- priv_key = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32))
93
- ret = secp256k1_ec_seckey_verify(context, priv_key)
94
- end
95
- private_key = priv_key.read_string(32).bth
96
- [private_key , generate_pubkey_in_context(context, private_key, compressed: compressed) ]
97
- end
19
+ # Whether this module is native c wrapper or not?
20
+ # @return [Boolean]
21
+ def native?
22
+ true
98
23
  end
99
24
 
100
25
  # generate bitcoin key object
@@ -103,295 +28,76 @@ module Bitcoin
103
28
  Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
104
29
  end
105
30
 
106
- def generate_pubkey(priv_key, compressed: true)
107
- with_context do |context|
108
- generate_pubkey_in_context(context, priv_key, compressed: compressed)
109
- end
110
- end
111
-
112
- # sign data.
113
- # @param [String] data a data to be signed with binary format
114
- # @param [String] privkey a private key with hex format using sign
115
- # @param [String] extra_entropy a extra entropy with binary format for rfc6979
116
- # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
117
- # @return [String] signature data with binary format
118
- def sign_data(data, privkey, extra_entropy = nil, algo: :ecdsa)
119
- case algo
120
- when :ecdsa
121
- sign_ecdsa(data, privkey, extra_entropy)
122
- when :schnorr
123
- sign_schnorr(data, privkey, extra_entropy)
124
- else
125
- nil
126
- end
127
- end
128
-
129
31
  # Sign data with compact format.
130
32
  # @param [String] data a data to be signed with binary format
131
33
  # @param [String] privkey a private key using sign with hex format
132
34
  # @return [Array[signature, recovery id]]
133
35
  def sign_compact(data, privkey)
134
- with_context do |context|
135
- sig = FFI::MemoryPointer.new(:uchar, 65)
136
- hash =FFI::MemoryPointer.new(:uchar, data.bytesize).put_bytes(0, data)
137
- priv_key = privkey.htb
138
- sec_key = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
139
- result = secp256k1_ecdsa_sign_recoverable(context, sig, hash, sec_key, nil, nil)
140
- raise 'secp256k1_ecdsa_sign_recoverable failed.' if result == 0
141
-
142
- output = FFI::MemoryPointer.new(:uchar, 64)
143
- rec = FFI::MemoryPointer.new(:uint64)
144
- result = secp256k1_ecdsa_recoverable_signature_serialize_compact(context, output, rec, sig)
145
- raise 'secp256k1_ecdsa_recoverable_signature_serialize_compact failed.' unless result == 1
146
-
147
- raw_sig = output.read_string(64)
148
- [ECDSA::Signature.new(raw_sig[0...32].bti, raw_sig[32..-1].bti), rec.read(:int)]
149
- end
36
+ sig, rec_id = sign_recoverable(data, privkey)
37
+ [ECDSA::Signature.new(sig[0...64].to_i(16), sig[64..-1].to_i(16)), rec_id]
150
38
  end
151
39
 
152
40
  # Recover public key from compact signature.
153
41
  # @param [String] data message digest using signature.
154
- # @param [String] signature signature with binary format.
155
- # @param [Integer] rec recovery id.
42
+ # @param [String] signature signature with binary format(65 bytes).
156
43
  # @param [Boolean] compressed whether compressed public key or not.
157
44
  # @return [Bitcoin::Key] Recovered public key.
158
- def recover_compact(data, signature, rec, compressed)
159
- with_context do |context|
160
- sig = FFI::MemoryPointer.new(:uchar, 65)
161
- input = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1])
162
- result = secp256k1_ecdsa_recoverable_signature_parse_compact(context, sig, input, rec)
163
- raise 'secp256k1_ecdsa_recoverable_signature_parse_compact failed.' unless result == 1
164
-
165
- pubkey = FFI::MemoryPointer.new(:uchar, 64)
166
- msg = FFI::MemoryPointer.new(:uchar, data.bytesize).put_bytes(0, data)
167
- result = secp256k1_ecdsa_recover(context, pubkey, sig, msg)
168
- raise 'secp256k1_ecdsa_recover failed.' unless result == 1
169
-
170
- pubkey = serialize_pubkey_internal(context, pubkey.read_string(64), compressed)
171
- Bitcoin::Key.new(pubkey: pubkey, compressed: compressed)
172
- end
45
+ def recover_compact(data, signature, compressed)
46
+ pubkey = recover(data, signature, compressed)
47
+ Bitcoin::Key.new(pubkey: pubkey, compressed: compressed)
173
48
  end
174
49
 
175
- # verify signature
176
- # @param [String] data a data with binary format.
177
- # @param [String] sig signature data with binary format
178
- # @param [String] pubkey a public key with hex format using verify.
179
- # # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
180
- # @return [Boolean] verification result.
181
- def verify_sig(data, sig, pubkey, algo: :ecdsa)
50
+ # Sign to data.
51
+ # @param [String] data The 32-byte message hash being signed with binary format.
52
+ # @param [String] private_key a private key with hex format using sign.
53
+ # @param [String] extra_entropy a extra entropy with binary format for rfc6979.
54
+ # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
55
+ # @return [String] signature data with binary format. If unsupported algorithm specified, return nil.
56
+ # @raise [ArgumentError] If invalid arguments specified.
57
+ def sign_data(data, private_key, extra_entropy = nil, algo: :ecdsa)
182
58
  case algo
183
59
  when :ecdsa
184
- verify_ecdsa(data, sig, pubkey)
60
+ begin
61
+ sign_ecdsa(data, private_key, extra_entropy)
62
+ rescue ArgumentError
63
+ false
64
+ end
185
65
  when :schnorr
186
- verify_schnorr(data, sig, pubkey)
66
+ begin
67
+ sign_schnorr(data, private_key, extra_entropy)
68
+ rescue ArgumentError
69
+ false
70
+ end
187
71
  else
188
- false
189
- end
190
- end
191
-
192
- # # validate whether this is a valid public key (more expensive than IsValid())
193
- # @param [String] pub_key public key with hex format.
194
- # @param [Boolean] allow_hybrid whether support hybrid public key.
195
- # @return [Boolean] If valid public key return true, otherwise false.
196
- def parse_ec_pubkey?(pub_key, allow_hybrid = false)
197
- pub_key = pub_key.htb
198
- return false if !allow_hybrid && ![0x02, 0x03, 0x04].include?(pub_key[0].ord)
199
- with_context do |context|
200
- pubkey = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
201
- internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
202
- result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pub_key.bytesize)
203
- result == 1
72
+ raise ArgumentError, "unknown algo: #{algo}"
204
73
  end
205
74
  end
206
75
 
207
- # Create key pair data from private key.
208
- # @param [String] priv_key with hex format
209
- # @return [String] key pair data with hex format. data = private key(32 bytes) | public key(64 bytes).
210
- def create_keypair(priv_key)
211
- with_context do |context|
212
- priv_key = priv_key.htb
213
- secret = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
214
- raise 'priv_key is invalid.' unless secp256k1_ec_seckey_verify(context, secret)
215
- keypair = FFI::MemoryPointer.new(:uchar, 96)
216
- raise 'priv_key is invalid.' unless secp256k1_keypair_create(context, keypair, secret) == 1
217
- keypair.read_string(96).bth
218
- end
219
- end
220
-
221
- # Check whether valid x-only public key or not.
222
- # @param [String] pub_key x-only public key with hex format(32 bytes).
223
- # @return [Boolean] result.
224
- def valid_xonly_pubkey?(pub_key)
225
- begin
226
- full_pubkey_from_xonly_pubkey(pub_key)
227
- rescue Exception
228
- return false
229
- end
230
- true
231
- end
232
-
233
- # Decode ellswift public key.
234
- # @param [String] ell_key ElligatorSwift key with binary format.
235
- # @return [String] Decoded public key with hex format
236
- def ellswift_decode(ell_key)
237
- with_context do |context|
238
- ell64 = FFI::MemoryPointer.new(:uchar, ell_key.bytesize).put_bytes(0, ell_key)
239
- internal = FFI::MemoryPointer.new(:uchar, 64)
240
- result = secp256k1_ellswift_decode(context, internal, ell64)
241
- raise ArgumentError, 'Decode failed.' unless result == 1
242
- serialize_pubkey_internal(context, internal, true)
243
- end
244
- end
245
-
246
- # Compute an ElligatorSwift public key for a secret key.
247
- # @param [String] priv_key private key with hex format
248
- # @return [String] ElligatorSwift public key with hex format.
249
- def ellswift_create(priv_key)
250
- with_context(flags: SECP256K1_CONTEXT_SIGN) do |context|
251
- ell64 = FFI::MemoryPointer.new(:uchar, 64)
252
- seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb)
253
- result = secp256k1_ellswift_create(context, ell64, seckey32, nil)
254
- raise ArgumentError, 'Failed to create ElligatorSwift public key.' unless result == 1
255
- ell64.read_string(64).bth
256
- end
257
- end
258
-
259
- # Compute X coordinate of shared ECDH point between elswift pubkey and privkey.
260
- # @param [Bitcoin::BIP324::EllSwiftPubkey] their_ell_pubkey Their EllSwift public key.
261
- # @param [Bitcoin::BIP324::EllSwiftPubkey] our_ell_pubkey Our EllSwift public key.
262
- # @param [String] priv_key private key with hex format.
263
- # @param [Boolean] initiating Whether your initiator or not.
264
- # @return [String] x coordinate with hex format.
265
- def ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, priv_key, initiating)
266
- with_context(flags: SECP256K1_CONTEXT_SIGN) do |context|
267
- output = FFI::MemoryPointer.new(:uchar, 32)
268
- our_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, our_ell_pubkey.key)
269
- their_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, their_ell_pubkey.key)
270
- seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb)
271
- hashfp = secp256k1_ellswift_xdh_hash_function_bip324
272
- result = secp256k1_ellswift_xdh(context, output,
273
- initiating ? our_ell_ptr : their_ell_ptr,
274
- initiating ? their_ell_ptr : our_ell_ptr,
275
- seckey32,
276
- initiating ? 0 : 1,
277
- hashfp, nil)
278
- raise ArgumentError, "secret was invalid or hashfp returned 0" unless result == 1
279
- output.read_string(32).bth
280
- end
281
- end
282
-
283
- private
284
-
285
- # Calculate full public key(64 bytes) from public key(32 bytes).
286
- # @param [String] pub_key x-only public key with hex format(32 bytes).
287
- # @return [String] x-only public key with hex format(64 bytes).
288
- def full_pubkey_from_xonly_pubkey(pub_key)
289
- with_context do |context|
290
- pubkey = pub_key.htb
291
- raise ArgumentError, "Pubkey size must be #{X_ONLY_PUBKEY_SIZE} bytes." unless pubkey.bytesize == X_ONLY_PUBKEY_SIZE
292
- xonly_pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
293
- full_pubkey = FFI::MemoryPointer.new(:uchar, 64)
294
- raise ArgumentError, 'An invalid public key was specified.' unless secp256k1_xonly_pubkey_parse(context, full_pubkey, xonly_pubkey) == 1
295
- full_pubkey.read_string(64).bth
296
- end
297
- end
298
-
299
- def generate_pubkey_in_context(context, privkey, compressed: true)
300
- internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
301
- result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb)
302
- raise 'error creating pubkey' unless result
303
- serialize_pubkey_internal(context, internal_pubkey, compressed)
304
- end
305
-
306
- def sign_ecdsa(data, privkey, extra_entropy)
307
- with_context do |context|
308
- secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
309
- raise 'priv_key is invalid' unless secp256k1_ec_seckey_verify(context, secret)
310
-
311
- internal_signature = FFI::MemoryPointer.new(:uchar, 64)
312
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
313
- entropy = extra_entropy ? FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, extra_entropy) : nil
314
-
315
- ret, tries, max = 0, 0, 20
316
-
317
- while ret != 1
318
- raise 'secp256k1_ecdsa_sign failed.' if tries >= max
319
- tries += 1
320
- ret = secp256k1_ecdsa_sign(context, internal_signature, msg32, secret, nil, entropy)
76
+ # Verify signature.
77
+ # @param [String] data The 32-byte message hash assumed to be signed.
78
+ # @param [String] signature signature data with binary format
79
+ # @param [String] pubkey a public key with hex format using verify.
80
+ # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
81
+ # @return [Boolean] verification result.
82
+ # @raise [ArgumentError] If invalid arguments specified.
83
+ def verify_sig(data, signature, pubkey, algo: :ecdsa)
84
+ case algo
85
+ when :ecdsa
86
+ begin
87
+ verify_ecdsa(data, signature, pubkey)
88
+ rescue ArgumentError
89
+ false
321
90
  end
322
-
323
- signature = FFI::MemoryPointer.new(:uchar, 72)
324
- signature_len = FFI::MemoryPointer.new(:uint64).put_uint64(0, 72)
325
- result = secp256k1_ecdsa_signature_serialize_der(context, signature, signature_len, internal_signature)
326
- raise 'secp256k1_ecdsa_signature_serialize_der failed' unless result
327
-
328
- signature.read_string(signature_len.read_uint64)
329
- end
330
- end
331
-
332
- def sign_schnorr(data, privkey, aux_rand = nil)
333
- with_context do |context|
334
- keypair = create_keypair(privkey).htb
335
- keypair = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
336
- signature = FFI::MemoryPointer.new(:uchar, 64)
337
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
338
- aux_rand = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, aux_rand) if aux_rand
339
- raise 'Failed to generate schnorr signature.' unless secp256k1_schnorrsig_sign32(context, signature, msg32, keypair, aux_rand) == 1
340
- signature.read_string(64)
341
- end
342
- end
343
-
344
- def verify_ecdsa(data, sig, pubkey)
345
- with_context do |context|
346
- return false if data.bytesize == 0
347
- pubkey = pubkey.htb
348
- pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
349
- internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
350
- result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size)
351
- return false unless result
352
-
353
- signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
354
- internal_signature = FFI::MemoryPointer.new(:uchar, 64)
355
- result = secp256k1_ecdsa_signature_parse_der(context, internal_signature, signature, signature.size)
356
- return false unless result
357
-
358
- # libsecp256k1's ECDSA verification requires lower-S signatures, which have not historically been enforced in Bitcoin, so normalize them first.
359
- secp256k1_ecdsa_signature_normalize(context, internal_signature, internal_signature)
360
-
361
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
362
- result = secp256k1_ecdsa_verify(context, internal_signature, msg32, internal_pubkey)
363
-
364
- result == 1
365
- end
366
- end
367
-
368
- def verify_schnorr(data, sig, pubkey)
369
- with_context do |context|
370
- return false if data.bytesize == 0
371
- pubkey = full_pubkey_from_xonly_pubkey(pubkey).htb
372
- xonly_pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
373
- signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
374
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
375
- result = secp256k1_schnorrsig_verify(context, signature, msg32, 32, xonly_pubkey)
376
- result == 1
91
+ when :schnorr
92
+ begin
93
+ verify_schnorr(data, signature, pubkey)
94
+ rescue ArgumentError
95
+ false
96
+ end
97
+ else
98
+ raise ArgumentError, "unknown algo: #{algo}"
377
99
  end
378
100
  end
379
-
380
- # Serialize public key.
381
- def serialize_pubkey_internal(context, pubkey_input, compressed)
382
- pubkey = FFI::MemoryPointer.new(:uchar, 65)
383
- pubkey_len = FFI::MemoryPointer.new(:uint64)
384
- result = if compressed
385
- pubkey_len.put_uint64(0, 33)
386
- secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, pubkey_input, SECP256K1_EC_COMPRESSED)
387
- else
388
- pubkey_len.put_uint64(0, 65)
389
- secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, pubkey_input, SECP256K1_EC_UNCOMPRESSED)
390
- end
391
- raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
392
- pubkey.read_string(pubkey_len.read_uint64).bth
393
- end
394
-
395
101
  end
396
102
  end
397
103
  end
@@ -9,6 +9,13 @@ module Bitcoin
9
9
  module Ruby
10
10
 
11
11
  module_function
12
+ extend Schnorr::Util
13
+
14
+ # Whether this module is native c wrapper or not?
15
+ # @return [Boolean]
16
+ def native?
17
+ false
18
+ end
12
19
 
13
20
  # generate ec private key and public key
14
21
  def generate_key_pair(compressed: true)
@@ -72,11 +79,20 @@ module Bitcoin
72
79
 
73
80
  # Recover public key from compact signature.
74
81
  # @param [String] data message digest using signature.
75
- # @param [String] signature signature with binary format.
76
- # @param [Integer] rec recovery id.
82
+ # @param [String] signature signature with binary format(65 bytes).
77
83
  # @param [Boolean] compressed whether compressed public key or not.
78
84
  # @return [Bitcoin::Key] Recovered public key.
79
- def recover_compact(data, signature, rec, compressed)
85
+ # @raise [ArgumentError] If invalid arguments specified.
86
+ def recover_compact(data, signature, compressed)
87
+ raise ArgumentError, "data must be String." unless data.is_a?(String)
88
+ raise ArgumentError, "signature must be String." unless signature.is_a?(String)
89
+ signature = hex2bin(signature)
90
+ raise ArgumentError, "signature must be 64 bytes." unless signature.bytesize == 65
91
+ data = hex2bin(data)
92
+ raise ArgumentError, "data must be 32 bytes." unless data.bytesize == 32
93
+ rec = (signature[0].ord - 0x1b) & 3
94
+ raise ArgumentError, "rec must be between 0 and 3." if rec < 0 || rec > 3
95
+
80
96
  group = Bitcoin::Secp256k1::GROUP
81
97
  r = ECDSA::Format::IntegerOctetString.decode(signature[1...33])
82
98
  s = ECDSA::Format::IntegerOctetString.decode(signature[33..-1])
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "1.7.0"
2
+ VERSION = "1.8.0"
3
3
  end
data/lib/bitcoin.rb CHANGED
@@ -7,7 +7,7 @@ require 'schnorr'
7
7
  require 'securerandom'
8
8
  require 'json'
9
9
  require 'bech32'
10
- require 'ffi'
10
+ require 'base64'
11
11
  require 'observer'
12
12
  require 'tmpdir'
13
13
  require_relative 'openassets'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-08-17 00:00:00.000000000 Z
10
+ date: 2025-02-03 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: ecdsa_ext
@@ -94,20 +93,6 @@ dependencies:
94
93
  - - ">="
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: ffi
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
96
  - !ruby/object:Gem::Dependency
112
97
  name: leb128
113
98
  requirement: !ruby/object:Gem::Requirement
@@ -171,6 +156,9 @@ dependencies:
171
156
  - - ">="
172
157
  - !ruby/object:Gem::Version
173
158
  version: 2.3.1
159
+ - - "<"
160
+ - !ruby/object:Gem::Version
161
+ version: 2.8.0
174
162
  type: :runtime
175
163
  prerelease: false
176
164
  version_requirements: !ruby/object:Gem::Requirement
@@ -178,6 +166,9 @@ dependencies:
178
166
  - - ">="
179
167
  - !ruby/object:Gem::Version
180
168
  version: 2.3.1
169
+ - - "<"
170
+ - !ruby/object:Gem::Version
171
+ version: 2.8.0
181
172
  - !ruby/object:Gem::Dependency
182
173
  name: bip-schnorr
183
174
  requirement: !ruby/object:Gem::Requirement
@@ -207,103 +198,47 @@ dependencies:
207
198
  - !ruby/object:Gem::Version
208
199
  version: 0.3.4
209
200
  - !ruby/object:Gem::Dependency
210
- name: leveldb-native
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - ">="
214
- - !ruby/object:Gem::Version
215
- version: '0'
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - ">="
221
- - !ruby/object:Gem::Version
222
- version: '0'
223
- - !ruby/object:Gem::Dependency
224
- name: bundler
225
- requirement: !ruby/object:Gem::Requirement
226
- requirements:
227
- - - ">="
228
- - !ruby/object:Gem::Version
229
- version: '0'
230
- type: :development
231
- prerelease: false
232
- version_requirements: !ruby/object:Gem::Requirement
233
- requirements:
234
- - - ">="
235
- - !ruby/object:Gem::Version
236
- version: '0'
237
- - !ruby/object:Gem::Dependency
238
- name: rake
239
- requirement: !ruby/object:Gem::Requirement
240
- requirements:
241
- - - ">="
242
- - !ruby/object:Gem::Version
243
- version: 12.3.3
244
- type: :development
245
- prerelease: false
246
- version_requirements: !ruby/object:Gem::Requirement
247
- requirements:
248
- - - ">="
249
- - !ruby/object:Gem::Version
250
- version: 12.3.3
251
- - !ruby/object:Gem::Dependency
252
- name: rspec
201
+ name: base64
253
202
  requirement: !ruby/object:Gem::Requirement
254
203
  requirements:
255
204
  - - "~>"
256
205
  - !ruby/object:Gem::Version
257
- version: '3.0'
258
- type: :development
206
+ version: 0.2.0
207
+ type: :runtime
259
208
  prerelease: false
260
209
  version_requirements: !ruby/object:Gem::Requirement
261
210
  requirements:
262
211
  - - "~>"
263
212
  - !ruby/object:Gem::Version
264
- version: '3.0'
213
+ version: 0.2.0
265
214
  - !ruby/object:Gem::Dependency
266
- name: timecop
215
+ name: observer
267
216
  requirement: !ruby/object:Gem::Requirement
268
217
  requirements:
269
- - - ">="
270
- - !ruby/object:Gem::Version
271
- version: '0'
272
- type: :development
273
- prerelease: false
274
- version_requirements: !ruby/object:Gem::Requirement
275
- requirements:
276
- - - ">="
277
- - !ruby/object:Gem::Version
278
- version: '0'
279
- - !ruby/object:Gem::Dependency
280
- name: webmock
281
- requirement: !ruby/object:Gem::Requirement
282
- requirements:
283
- - - ">="
218
+ - - "~>"
284
219
  - !ruby/object:Gem::Version
285
- version: 3.11.1
286
- type: :development
220
+ version: 0.1.2
221
+ type: :runtime
287
222
  prerelease: false
288
223
  version_requirements: !ruby/object:Gem::Requirement
289
224
  requirements:
290
- - - ">="
225
+ - - "~>"
291
226
  - !ruby/object:Gem::Version
292
- version: 3.11.1
227
+ version: 0.1.2
293
228
  - !ruby/object:Gem::Dependency
294
- name: parallel
229
+ name: secp256k1rb
295
230
  requirement: !ruby/object:Gem::Requirement
296
231
  requirements:
297
- - - ">="
232
+ - - '='
298
233
  - !ruby/object:Gem::Version
299
- version: 1.20.1
300
- type: :development
234
+ version: 0.1.1
235
+ type: :runtime
301
236
  prerelease: false
302
237
  version_requirements: !ruby/object:Gem::Requirement
303
238
  requirements:
304
- - - ">="
239
+ - - '='
305
240
  - !ruby/object:Gem::Version
306
- version: 1.20.1
241
+ version: 0.1.1
307
242
  description: The implementation of Bitcoin Protocol for Ruby.
308
243
  email:
309
244
  - azuchi@chaintope.com
@@ -348,6 +283,7 @@ files:
348
283
  - lib/bitcoin/chainparams/regtest.yml
349
284
  - lib/bitcoin/chainparams/signet.yml
350
285
  - lib/bitcoin/chainparams/testnet.yml
286
+ - lib/bitcoin/chainparams/testnet4.yml
351
287
  - lib/bitcoin/constants.rb
352
288
  - lib/bitcoin/descriptor.rb
353
289
  - lib/bitcoin/descriptor/addr.rb
@@ -507,7 +443,6 @@ homepage: https://github.com/chaintope/bitcoinrb
507
443
  licenses:
508
444
  - MIT
509
445
  metadata: {}
510
- post_install_message:
511
446
  rdoc_options: []
512
447
  require_paths:
513
448
  - lib
@@ -522,8 +457,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
522
457
  - !ruby/object:Gem::Version
523
458
  version: '0'
524
459
  requirements: []
525
- rubygems_version: 3.5.3
526
- signing_key:
460
+ rubygems_version: 3.6.2
527
461
  specification_version: 4
528
462
  summary: The implementation of Bitcoin Protocol for Ruby.
529
463
  test_files: []