bitcoinrb 1.2.1 → 1.4.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: 3c9473a6381568e857b55cadaaad942065a2d9189fab7da542248c9e998d528e
4
- data.tar.gz: 34b3ebf0c5826edf8241a1294aaa6662bdf1e92973968351ecb2a9d24eeb8402
3
+ metadata.gz: 544382f715d87eb79e185e310c827b2349c0af75c1bd89887a653510e226cd1f
4
+ data.tar.gz: c807d0965da7c06f71b248d5a543ccfff2375d702a6ab4db70698c4be245720d
5
5
  SHA512:
6
- metadata.gz: eb63914be0f81eb46e76d6c7fb2bbc9e13db948da8c757fd7c9a7235918faff82b6cd770620a4096480b1d183495f0a930dcdd55aeb014484368b2b1ee91d660
7
- data.tar.gz: 696e9d8081863c22c0b3322d32f4cca5200ceaf7553f8a97226b4696a04ee3a091ed384bdef6b28b573f4d9d81b80d37a0d65168d7eb86570df9325232ac1663
6
+ metadata.gz: 22ea36535fb045c35d28809f87c60f4451e2d37801e7a231772068c268d6e58cdeb086456ffc649091a5a28a82815af77f6d35ef286a7a101e53fa3f97e55a9a
7
+ data.tar.gz: 7ca7b637b77bdde57d2af3faf056184397b1d87fd98dd5c46933dfb40e716d5bf8ef56890691771b97d8e66dde1c9684cb1d03e9f74f99d4f8b5bbaeb6a37e2c
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['2.6', '2.7', '3.0', '3.1']
22
+ ruby-version: ['3.0', '3.1', '3.2']
23
23
 
24
24
  steps:
25
25
  - uses: actions/checkout@v2
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.1.2
1
+ ruby-3.2.0
data/README.md CHANGED
@@ -67,6 +67,8 @@ And then add to your .rb file:
67
67
 
68
68
  ## Usage
69
69
 
70
+ Examples can be found on the [wiki](https://github.com/chaintope/bitcoinrb/wiki).
71
+
70
72
  ### Chain selection
71
73
 
72
74
  The parameters of the blockchain are managed by `Bitcoin::ChainParams`. Switch chain parameters as follows:
data/bitcoinrb.gemspec CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_runtime_dependency 'ecdsa'
23
+ spec.add_runtime_dependency 'ecdsa_ext', '~> 0.5.0'
24
24
  spec.add_runtime_dependency 'eventmachine'
25
- spec.add_runtime_dependency 'murmurhash3'
25
+ spec.add_runtime_dependency 'murmurhash3', '~> 0.1.7'
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'
@@ -31,9 +31,8 @@ Gem::Specification.new do |spec|
31
31
  spec.add_runtime_dependency 'eventmachine_httpserver'
32
32
  spec.add_runtime_dependency 'iniparse'
33
33
  spec.add_runtime_dependency 'siphash'
34
- spec.add_runtime_dependency 'protobuf', '3.8.5'
35
34
  spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
36
- spec.add_runtime_dependency 'bip-schnorr', '>= 0.4.0'
35
+ spec.add_runtime_dependency 'bip-schnorr', '>= 0.5.0'
37
36
  spec.add_runtime_dependency 'base32', '>= 0.3.4'
38
37
 
39
38
  # for options
@@ -57,9 +57,8 @@ module Bitcoin
57
57
  SCRIPT_VERIFY_CONST_SCRIPTCODE = (1 << 16) # Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
58
58
  SCRIPT_VERIFY_TAPROOT = (1 << 17) # Taproot/Tapscript validation (BIPs 341 & 342)
59
59
  SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION = (1 << 18) # Making unknown Taproot leaf versions non-standard
60
- SCRIPT_VERIFY_DISCOURAGE_UNKNOWN_ANNEX = (1 << 19) # Making the use of (unknown) annexes non-standard (currently no annexes are known)
61
- SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1 << 20) # Making unknown OP_SUCCESS non-standard
62
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1 << 21) # Making unknown public key versions (in BIP 342 scripts) non-standard
60
+ SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS = (1 << 19) # Making unknown OP_SUCCESS non-standard
61
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1 << 20) # Making unknown public key versions (in BIP 342 scripts) non-standard
63
62
 
64
63
  MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
65
64
 
@@ -27,7 +27,8 @@ module Bitcoin
27
27
  l = Bitcoin.hmac_sha512('Bitcoin seed', seed.htb)
28
28
  left = l[0..31].bth.to_i(16)
29
29
  raise 'invalid key' if left >= CURVE_ORDER || left == 0
30
- ext_key.key = Bitcoin::Key.new(priv_key: l[0..31].bth, key_type: Bitcoin::Key::TYPES[:compressed])
30
+ l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
31
+ ext_key.key = Bitcoin::Key.new(priv_key: l_priv.bth, key_type: Bitcoin::Key::TYPES[:compressed])
31
32
  ext_key.chain_code = l[32..-1]
32
33
  ext_key
33
34
  end
@@ -110,8 +111,8 @@ module Bitcoin
110
111
  raise 'invalid key' if left >= CURVE_ORDER
111
112
  child_priv = (left + key.priv_key.to_i(16)) % CURVE_ORDER
112
113
  raise 'invalid key ' if child_priv >= CURVE_ORDER
113
- new_key.key = Bitcoin::Key.new(
114
- priv_key: child_priv.to_even_length_hex.rjust(64, '0'), key_type: key_type)
114
+ child_priv = ECDSA::Format::IntegerOctetString.encode(child_priv, 32)
115
+ new_key.key = Bitcoin::Key.new(priv_key: child_priv.bth, key_type: key_type)
115
116
  new_key.chain_code = l[32..-1]
116
117
  new_key.ver = version
117
118
  new_key
@@ -280,7 +281,8 @@ module Bitcoin
280
281
  l = Bitcoin.hmac_sha512(chain_code, data)
281
282
  left = l[0..31].bth.to_i(16)
282
283
  raise 'invalid key' if left >= CURVE_ORDER
283
- p1 = Bitcoin::Key.new(priv_key: left.to_s(16), key_type: Bitcoin::Key::TYPES[:uncompressed]).to_point
284
+ l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
285
+ p1 = Bitcoin::Key.new(priv_key: l_priv.bth, key_type: Bitcoin::Key::TYPES[:uncompressed]).to_point
284
286
  p2 = Bitcoin::Key.new(pubkey: pubkey, key_type: key_type).to_point
285
287
  new_key.pubkey = (p1 + p2).to_hex
286
288
  new_key.chain_code = l[32..-1]
data/lib/bitcoin/key.rb CHANGED
@@ -39,6 +39,7 @@ module Bitcoin
39
39
  @secp256k1_module = Bitcoin.secp_impl
40
40
  @priv_key = priv_key
41
41
  if @priv_key
42
+ raise ArgumentError, 'Private key must be 32 bytes.' unless priv_key.htb.bytesize == 32
42
43
  raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
43
44
  end
44
45
  if pubkey
@@ -51,7 +52,7 @@ module Bitcoin
51
52
 
52
53
  # generate key pair
53
54
  def self.generate(key_type = TYPES[:compressed])
54
- priv_key, pubkey = Bitcoin.secp_impl.generate_key_pair
55
+ priv_key, pubkey = Bitcoin.secp_impl.generate_key_pair(compressed: key_type != TYPES[:uncompressed])
55
56
  new(priv_key: priv_key, pubkey: pubkey, key_type: key_type)
56
57
  end
57
58
 
@@ -260,6 +261,12 @@ module Bitcoin
260
261
  pubkey[2..65]
261
262
  end
262
263
 
264
+ # Convert this key to decompress key.
265
+ # @return [String] decompress public key with hex format.
266
+ def decompress_pubkey
267
+ pubkey.htb.bytesize == PUBLIC_KEY_SIZE ? pubkey : to_point.to_hex(false)
268
+ end
269
+
263
270
  # check +pubkey+ (hex) is compress or uncompress pubkey.
264
271
  def self.compress_or_uncompress_pubkey?(pubkey)
265
272
  p = pubkey.htb
@@ -6,14 +6,42 @@ module Bitcoin
6
6
 
7
7
  module_function
8
8
 
9
+ FORMAT_LEGACY = :legacy
10
+ FORMAT_SIMPLE = :simple
11
+ FORMAT_FULL = :full
12
+
9
13
  # Sign a message.
10
- # @param [Bitcoin::Key] key Private key to sign with.
11
- # @param [String] message The message to sign.
14
+ # @param [Bitcoin::Key] key Private key to sign.
15
+ # @param [String] message The message to be signed.
16
+ # @param [String] address An address of the key used for signing (required for full or simple format).
17
+ # @param [String] format Format of signature data. Default is +FORMAT_LEGACY+.
18
+ # @param [String] prefix (Optional) Prefix used in legacy format.
12
19
  # @return [String] Signature, base64 encoded.
13
- def sign_message(key, message, prefix: Bitcoin.chain_params.message_magic)
14
- digest = message_hash(message, prefix: prefix)
15
- compact_sig = key.sign_compact(digest)
16
- Base64.strict_encode64(compact_sig)
20
+ def sign_message(key, message, prefix: Bitcoin.chain_params.message_magic, format: FORMAT_LEGACY, address: nil)
21
+ validate_format!(format)
22
+ digest = message_hash(message, prefix: prefix, legacy: format == FORMAT_LEGACY)
23
+ sig = case format
24
+ when FORMAT_LEGACY
25
+ key.sign_compact(digest)
26
+ else
27
+ validate_address!(address)
28
+ addr = Bitcoin::Script.parse_from_addr(address)
29
+ sig_ver, algo = if addr.p2wpkh?
30
+ [:witness_v0, :ecdsa]
31
+ elsif addr.p2tr?
32
+ [:taproot, :schnorr]
33
+ else
34
+ raise ArgumentError "#{address} dose not supported."
35
+ end
36
+ tx = to_sign_tx(digest, address)
37
+ prev_out = Bitcoin::TxOut.new(script_pubkey: addr)
38
+ sighash = tx.sighash_for_input(0, addr, sig_version: sig_ver, amount: 0, prevouts: [prev_out])
39
+ sig = key.sign(sighash, algo: algo) + [Bitcoin::SIGHASH_TYPE[:all]].pack('C')
40
+ tx.in[0].script_witness.stack << sig
41
+ tx.in[0].script_witness.stack << key.pubkey.htb
42
+ format == FORMAT_SIMPLE ? tx.in[0].script_witness.to_payload : tx.to_payload
43
+ end
44
+ Base64.strict_encode64(sig)
17
45
  end
18
46
 
19
47
  # Verify a signed message.
@@ -23,25 +51,72 @@ module Bitcoin
23
51
  # @return [Boolean] Verification result.
24
52
  def verify_message(address, signature, message, prefix: Bitcoin.chain_params.message_magic)
25
53
  validate_address!(address)
26
- sig = Base64.decode64(signature)
27
- raise ArgumentError, 'Invalid signature length' unless sig.bytesize == Bitcoin::Key::COMPACT_SIGNATURE_SIZE
28
- digest = message_hash(message, prefix: prefix)
29
- pubkey = Bitcoin::Key.recover_compact(digest, sig)
30
- return false unless pubkey
31
- pubkey.to_p2pkh == address
54
+ begin
55
+ sig = Base64.strict_decode64(signature)
56
+ rescue ArgumentError
57
+ raise ArgumentError, 'Invalid signature'
58
+ end
59
+ begin
60
+ # Legacy verification
61
+ pubkey = Bitcoin::Key.recover_compact(message_hash(message, prefix: prefix, legacy: true), sig)
62
+ return false unless pubkey
63
+ pubkey.to_p2pkh == address
64
+ rescue ArgumentError
65
+ # BIP322 verification
66
+ tx = to_sign_tx(message_hash(message, prefix: prefix, legacy: false), address)
67
+ tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig)
68
+ script_pubkey = Bitcoin::Script.parse_from_addr(address)
69
+ tx_out = Bitcoin::TxOut.new(script_pubkey: script_pubkey)
70
+ interpreter = Bitcoin::ScriptInterpreter.new(checker: Bitcoin::TxChecker.new(tx: tx, input_index: 0, prevouts: [tx_out]))
71
+ interpreter.verify_script(Bitcoin::Script.new, script_pubkey, tx.in[0].script_witness)
72
+ end
32
73
  end
33
74
 
34
75
  # Hashes a message for signing and verification.
35
- def message_hash(message, prefix: Bitcoin.chain_params.message_magic)
36
- Bitcoin.double_sha256(Bitcoin.pack_var_string(prefix) << Bitcoin.pack_var_string(message))
76
+ def message_hash(message, prefix: Bitcoin.chain_params.message_magic, legacy: true)
77
+ if legacy
78
+ Bitcoin.double_sha256(Bitcoin.pack_var_string(prefix) << Bitcoin.pack_var_string(message))
79
+ else
80
+ Bitcoin.tagged_hash('BIP0322-signed-message', message)
81
+ end
37
82
  end
38
83
 
39
84
  def validate_address!(address)
40
85
  raise ArgumentError, 'Invalid address' unless Bitcoin.valid_address?(address)
41
86
  script = Bitcoin::Script.parse_from_addr(address)
42
- raise ArgumentError, 'Address has no key' unless script.p2pkh?
87
+ raise ArgumentError, 'This address unsupported' if script.p2sh? || script.p2wsh?
88
+ end
89
+
90
+ def validate_format!(format)
91
+ unless [FORMAT_LEGACY, FORMAT_FULL, FORMAT_SIMPLE].include?(format)
92
+ raise ArgumentError "Invalid format specified."
93
+ end
94
+ end
95
+
96
+ def to_spend_tx(digest, addr)
97
+ validate_address!(addr)
98
+ message_challenge = Bitcoin::Script.parse_from_addr(addr)
99
+ tx = Bitcoin::Tx.new
100
+ tx.version = 0
101
+ tx.lock_time = 0
102
+ prev_out = Bitcoin::OutPoint.create_coinbase_outpoint
103
+ script_sig = Bitcoin::Script.new << Bitcoin::Opcodes::OP_0 << digest
104
+ tx.in << Bitcoin::TxIn.new(out_point: prev_out, sequence: 0, script_sig: script_sig)
105
+ tx.out << Bitcoin::TxOut.new(script_pubkey: message_challenge)
106
+ tx
107
+ end
108
+
109
+ def to_sign_tx(digest, addr)
110
+ tx = Bitcoin::Tx.new
111
+ tx.version = 0
112
+ tx.lock_time = 0
113
+ prev_out = Bitcoin::OutPoint.from_txid(to_spend_tx(digest, addr).txid, 0)
114
+ tx.in << Bitcoin::TxIn.new(out_point: prev_out, sequence: 0)
115
+ tx.out << Bitcoin::TxOut.new(script_pubkey: Bitcoin::Script.new << Bitcoin::Opcodes::OP_RETURN)
116
+ tx
43
117
  end
44
118
 
45
119
  private_class_method :validate_address!
120
+ private_class_method :validate_format!
46
121
  end
47
122
  end
@@ -9,6 +9,12 @@ module Bitcoin
9
9
 
10
10
  class_option :network, aliases: '-n', default: :mainnet
11
11
 
12
+ desc 'decodepsbt <base64 psbt string>', "Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction."
13
+ def decodepsbt(base64)
14
+ psbt = Bitcoin::PSBT::Tx.parse_from_base64(base64)
15
+ puts JSON.pretty_generate(psbt.to_h)
16
+ end
17
+
12
18
  desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
13
19
  def getblockchaininfo
14
20
  request('getblockchaininfo')
@@ -61,7 +61,7 @@ module Bitcoin
61
61
  raise ArgumentError, 'invalid version byte' unless hex[0..1] == VERSION_BYTE
62
62
  raise ArgumentError, 'invalid version' unless PaymentCode.support_version?(version)
63
63
  raise ArgumentError, 'invalid sign' unless PaymentCode.support_sign?(sign)
64
- raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless Bitcoin::Key.new(priv_key: nil, pubkey: sign + public_key).fully_valid_pubkey?
64
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless Bitcoin::Key.new(pubkey: sign + public_key).fully_valid_pubkey?
65
65
  raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Bitcoin.calc_checksum(payment_code) == hex[-8..-1]
66
66
 
67
67
  x_value = payment_code[8..71]
@@ -283,6 +283,32 @@ module Bitcoin
283
283
  self
284
284
  end
285
285
 
286
+ def to_h
287
+ h = {}
288
+ h[:non_witness_utxo] = non_witness_utxo.to_h if non_witness_utxo
289
+ h[:witness_utxo] = witness_utxo.to_h if witness_utxo
290
+ h[:redeem_script] = redeem_script.to_h if redeem_script
291
+ h[:witness_script] = witness_script.to_h if redeem_script
292
+ h[:final_script_sig] = final_script_sig.to_h if final_script_sig
293
+ h[:final_script_witness] = final_script_witness.to_h if final_script_witness
294
+ h[:bip32_derivs] = hd_key_paths.values.map(&:to_h) unless hd_key_paths.empty?
295
+ h[:partial_signatures] = partial_sigs.map {|k, v| {"#{k}": v.bth}} unless partial_sigs.empty?
296
+ h[:sighash_type] = sighash_type if sighash_type
297
+ h[:ripemd160_preimages] = ripemd160_preimages.map {|k, v| {"#{k}": v}} unless ripemd160_preimages.empty?
298
+ h[:sha256_preimages] = sha256_preimages.map {|k, v| {"#{k}": v}} unless sha256_preimages.empty?
299
+ h[:hash160_preimages] = hash160_preimages.map {|k, v| {"#{k}": v}} unless hash160_preimages.empty?
300
+ h[:hash256_preimages] = hash256_preimages.map {|k, v| {"#{k}": v}} unless hash256_preimages.empty?
301
+ h[:proprietary] = proprietaries.map(&:to_h) unless proprietaries.empty?
302
+ h[:tap_key_sig] = tap_key_sig if tap_key_sig
303
+ h[:tap_script_sig] = tap_script_sigs.map {|k, v| {"#{k}": v}} unless tap_script_sigs.empty?
304
+ h[:tap_leaf_script] = tap_leaf_scripts.map {|k, v| {"#{k}": v}} unless tap_leaf_scripts.empty?
305
+ h[:tap_bip32_derivs] = tap_bip32_derivations.map{|k, v| {"#{k}": v}} unless tap_bip32_derivations.empty?
306
+ h[:tap_internal_key] = tap_internal_key if tap_internal_key
307
+ h[:tap_merkle_root] = tap_merkle_root if tap_merkle_root
308
+ h[:unknown] = unknowns.map {|k, v| {"#{k}": v.bth}} unless unknowns.empty?
309
+ h
310
+ end
311
+
286
312
  end
287
313
 
288
314
  end
@@ -100,7 +100,18 @@ module Bitcoin
100
100
  combined
101
101
  end
102
102
 
103
+ def to_h
104
+ h = {}
105
+ h[:redeem_script] = redeem_script.bth if redeem_script
106
+ h[:witness_script] = witness_script.bth if witness_script
107
+ h[:bip32_derivs] = hd_key_paths.values.map(&:to_h) unless hd_key_paths.empty?
108
+ h[:proprietary] = proprietaries.map(&:to_h) unless proprietaries.empty?
109
+ h[:tap_internal_key] = tap_internal_key if tap_internal_key
110
+ h[:tap_tree] = tap_tree if tap_tree
111
+ h[:tap_bip32_derivs] = tap_bip32_derivations.map{|k, v| {"#{k}": v}} unless tap_bip32_derivations.empty?
112
+ h[:unknown] = unknowns.map {|k, v| {"#{k}": v.bth}} unless unknowns.empty?
113
+ h
114
+ end
103
115
  end
104
-
105
116
  end
106
117
  end
@@ -39,6 +39,10 @@ module Bitcoin
39
39
  k = key
40
40
  Bitcoin.pack_var_int(k.bytesize) + k + Bitcoin.pack_var_int(value.bytesize) + value
41
41
  end
42
+
43
+ def to_h
44
+ {identifier: identifier.bth, sub_type: sub_type, value: value.bth}
45
+ end
42
46
  end
43
47
  end
44
48
  end
@@ -177,6 +177,18 @@ module Bitcoin
177
177
  end
178
178
  end
179
179
 
180
+ def to_h
181
+ {
182
+ tx: tx.to_h,
183
+ global_xpubs: xpubs.map(&:to_h),
184
+ psbt_version: version,
185
+ proprietary: proprietaries.map(&:to_h),
186
+ unknown: unknowns.map {|k, v| {"#{k}": v}},
187
+ inputs: inputs.map(&:to_h),
188
+ outputs: outputs.map(&:to_h)
189
+ }
190
+ end
191
+
180
192
  # update input key-value maps.
181
193
  # @param [Bitcoin::Tx] prev_tx previous tx reference by input.
182
194
  # @param [Bitcoin::Script] redeem_script redeem script to set input.
@@ -233,11 +233,28 @@ module Bitcoin
233
233
  OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
234
234
  end
235
235
 
236
+ # Check whether this script is a P2PK format script.
237
+ # @return [Boolean] if P2PK return true, otherwise false
238
+ def p2pk?
239
+ return false unless chunks.size == 2
240
+ return false unless chunks[0].pushdata?
241
+ key_type = chunks[0].pushed_data[0].ord
242
+ case key_type
243
+ when 0x02, 0x03
244
+ return false unless chunks[0].pushed_data.bytesize == 33
245
+ when 0x04
246
+ return false unless chunks[0].pushed_data.bytesize == 65
247
+ else
248
+ return false
249
+ end
250
+ chunks[1].ord == OP_CHECKSIG
251
+ end
252
+
236
253
  def multisig?
237
254
  return false if chunks.size < 4 || chunks.last.ord != OP_CHECKMULTISIG
238
255
  pubkey_count = Opcodes.opcode_to_small_int(chunks[-2].opcode)
239
256
  sig_count = Opcodes.opcode_to_small_int(chunks[0].opcode)
240
- return false unless pubkey_count || sig_count
257
+ return false if pubkey_count.nil? || sig_count.nil?
241
258
  sig_count <= pubkey_count
242
259
  end
243
260
 
@@ -543,11 +560,8 @@ module Bitcoin
543
560
 
544
561
  def to_h
545
562
  h = {asm: to_s, hex: to_hex, type: type}
546
- addrs = addresses
547
- unless addrs.empty?
548
- h[:req_sigs] = multisig? ? Bitcoin::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
549
- h[:addresses] = addrs
550
- end
563
+ addr = to_addr
564
+ h[:address] = addr if addr
551
565
  h
552
566
  end
553
567
 
@@ -1,3 +1,6 @@
1
+ require 'ecdsa_ext'
2
+ require 'ecdsa/ext/sign_verify'
3
+
1
4
  module Bitcoin
2
5
  module Secp256k1
3
6
 
@@ -10,9 +13,9 @@ module Bitcoin
10
13
  # generate ec private key and public key
11
14
  def generate_key_pair(compressed: true)
12
15
  private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
13
- public_key = GROUP.generator.multiply_by_scalar(private_key)
16
+ public_key = GROUP.generator.to_jacobian * private_key
14
17
  privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
15
- pubkey = public_key.to_hex(compressed)
18
+ pubkey = public_key.to_affine.to_hex(compressed)
16
19
  [privkey.bth, pubkey]
17
20
  end
18
21
 
@@ -23,8 +26,8 @@ module Bitcoin
23
26
  end
24
27
 
25
28
  def generate_pubkey(privkey, compressed: true)
26
- public_key = ECDSA::Group::Secp256k1.generator.multiply_by_scalar(privkey.to_i(16))
27
- public_key.to_hex(compressed)
29
+ public_key = GROUP.generator.to_jacobian * privkey.to_i(16)
30
+ public_key.to_affine.to_hex(compressed)
28
31
  end
29
32
 
30
33
  # Check whether valid x-only public key or not.
@@ -132,7 +135,7 @@ module Bitcoin
132
135
  nonce = RFC6979.generate_rfc6979_nonce(privkey + data, extra_entropy)
133
136
 
134
137
  # port form ecdsa gem.
135
- r_point = GROUP.new_point(nonce)
138
+ r_point = (GROUP.generator.to_jacobian * nonce).to_affine
136
139
 
137
140
  point_field = ECDSA::PrimeField.new(GROUP.order)
138
141
  r = point_field.mod(r_point.x)
@@ -40,7 +40,9 @@ module Bitcoin
40
40
  private_key = p.has_even_y? ? internal_private_key.priv_key.to_i(16) :
41
41
  ECDSA::Group::Secp256k1.order - internal_private_key.priv_key.to_i(16)
42
42
  t = tweak(internal_private_key, merkle_root)
43
- Bitcoin::Key.new(priv_key: ((t.bti + private_key) % ECDSA::Group::Secp256k1.order).to_even_length_hex)
43
+ private_key = ECDSA::Format::IntegerOctetString.encode(
44
+ (t.bti + private_key) % ECDSA::Group::Secp256k1.order, 32)
45
+ Bitcoin::Key.new(priv_key: private_key.bth)
44
46
  end
45
47
  end
46
48
  end
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "1.2.1"
2
+ VERSION = "1.4.0"
3
3
  end
data/lib/bitcoin.rb CHANGED
@@ -46,7 +46,6 @@ module Bitcoin
46
46
  autoload :RPC, 'bitcoin/rpc'
47
47
  autoload :Wallet, 'bitcoin/wallet'
48
48
  autoload :BloomFilter, 'bitcoin/bloom_filter'
49
- autoload :Payments, 'bitcoin/payments'
50
49
  autoload :PSBT, 'bitcoin/psbt'
51
50
  autoload :GCSFilter, 'bitcoin/gcs_filter'
52
51
  autoload :BlockFilter, 'bitcoin/block_filter'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-28 00:00:00.000000000 Z
11
+ date: 2023-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ecdsa
14
+ name: ecdsa_ext
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.5.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.5.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: eventmachine
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: murmurhash3
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 0.1.7
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 0.1.7
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bech32
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -164,20 +164,6 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- - !ruby/object:Gem::Dependency
168
- name: protobuf
169
- requirement: !ruby/object:Gem::Requirement
170
- requirements:
171
- - - '='
172
- - !ruby/object:Gem::Version
173
- version: 3.8.5
174
- type: :runtime
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - '='
179
- - !ruby/object:Gem::Version
180
- version: 3.8.5
181
167
  - !ruby/object:Gem::Dependency
182
168
  name: json_pure
183
169
  requirement: !ruby/object:Gem::Requirement
@@ -198,14 +184,14 @@ dependencies:
198
184
  requirements:
199
185
  - - ">="
200
186
  - !ruby/object:Gem::Version
201
- version: 0.4.0
187
+ version: 0.5.0
202
188
  type: :runtime
203
189
  prerelease: false
204
190
  version_requirements: !ruby/object:Gem::Requirement
205
191
  requirements:
206
192
  - - ">="
207
193
  - !ruby/object:Gem::Version
208
- version: 0.4.0
194
+ version: 0.5.0
209
195
  - !ruby/object:Gem::Dependency
210
196
  name: base32
211
197
  requirement: !ruby/object:Gem::Requirement
@@ -439,13 +425,6 @@ files:
439
425
  - lib/bitcoin/opcodes.rb
440
426
  - lib/bitcoin/out_point.rb
441
427
  - lib/bitcoin/payment_code.rb
442
- - lib/bitcoin/payments.rb
443
- - lib/bitcoin/payments/output.pb.rb
444
- - lib/bitcoin/payments/payment.pb.rb
445
- - lib/bitcoin/payments/payment_ack.pb.rb
446
- - lib/bitcoin/payments/payment_details.pb.rb
447
- - lib/bitcoin/payments/payment_request.pb.rb
448
- - lib/bitcoin/payments/x509_certificates.pb.rb
449
428
  - lib/bitcoin/psbt.rb
450
429
  - lib/bitcoin/psbt/hd_key_path.rb
451
430
  - lib/bitcoin/psbt/input.rb
@@ -517,7 +496,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
517
496
  - !ruby/object:Gem::Version
518
497
  version: '0'
519
498
  requirements: []
520
- rubygems_version: 3.3.23
499
+ rubygems_version: 3.4.1
521
500
  signing_key:
522
501
  specification_version: 4
523
502
  summary: The implementation of Bitcoin Protocol for Ruby.
@@ -1,20 +0,0 @@
1
- module Bitcoin
2
- module Payments
3
-
4
- # https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#Output
5
- class Output < Protobuf::Message
6
-
7
- optional :uint64, :amount, 1, {default: 0}
8
-
9
- required :bytes, :script, 2
10
-
11
- # convert to TxOut object.
12
- # @return [Bitcoin::TxOut]
13
- def to_tx_out
14
- Bitcoin::TxOut.new(value: amount, script_pubkey: Bitcoin::Script.parse_from_payload(script))
15
- end
16
-
17
- end
18
-
19
- end
20
- end
@@ -1,26 +0,0 @@
1
- module Bitcoin
2
- module Payments
3
-
4
- # https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#Payment
5
- class Payment < Protobuf::Message
6
-
7
- optional :bytes, :merchant_data, 1
8
-
9
- repeated :bytes, :transactions, 2
10
-
11
- repeated Bitcoin::Payments::Output, :refund_to, 3
12
-
13
- optional :string, :memo, 4
14
-
15
- def self.parse_from_payload(payload)
16
- decode(payload)
17
- end
18
-
19
- def transactions
20
- @values[:transactions].map{|raw_tx|Bitcoin::Tx.parse_from_payload(raw_tx, strict: true)}
21
- end
22
-
23
- end
24
-
25
- end
26
- end
@@ -1,17 +0,0 @@
1
- module Bitcoin
2
- module Payments
3
-
4
- # https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#PaymentACK
5
- class PaymentACK < Protobuf::Message
6
-
7
- required Bitcoin::Payments::Payment, :payment, 1
8
-
9
- optional :string, :memo, 2
10
-
11
- def self.parse_from_payload(payload)
12
- decode(payload)
13
- end
14
- end
15
-
16
- end
17
- end
@@ -1,24 +0,0 @@
1
- module Bitcoin
2
- module Payments
3
-
4
- # https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#PaymentDetailsPaymentRequest
5
- class PaymentDetails < Protobuf::Message
6
-
7
- optional :string, :network, 1, {default: 'main'}
8
-
9
- repeated Bitcoin::Payments::Output, :outputs, 2
10
-
11
- required :uint64, :time, 3
12
-
13
- optional :uint64, :expires, 4
14
-
15
- optional :string, :memo, 5
16
-
17
- optional :string, :payment_url, 6
18
-
19
- optional :bytes, :merchant_data, 7
20
-
21
- end
22
-
23
- end
24
- end
@@ -1,79 +0,0 @@
1
- module Bitcoin
2
- module Payments
3
-
4
- # https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#PaymentDetailsPaymentRequest
5
- class PaymentRequest < Protobuf::Message
6
-
7
- optional :uint32, :payment_details_version, 1, {default: 1}
8
-
9
- optional :string, :pki_type, 2, {default: 'none'}
10
-
11
- optional :bytes, :pki_data, 3
12
-
13
- required :bytes, :serialized_payment_details, 4
14
-
15
- optional :bytes, :signature, 5
16
-
17
- def self.parse_from_payload(payload)
18
- self.decode(payload)
19
- end
20
-
21
- # verify +pki_data+.
22
- # @return [Struct] pki information.
23
- def verify_pki_data
24
- d = Struct.new(:display_name, :merchant_sign_key, :root_auth, :root_auth_name)
25
- d
26
- end
27
-
28
- # get payment details
29
- # @return [Bitcoin::Payments:PaymentDetails]
30
- def details
31
- PaymentDetails.decode(serialized_payment_details)
32
- end
33
-
34
- # get certificates
35
- # @return [Array[OpenSSL::X509::Certificate]]
36
- def certs
37
- return [] unless has_pki?
38
- X509Certificates.decode(pki_data).certs
39
- end
40
-
41
- # whether exist +pki_data+.
42
- def has_pki?
43
- pki_type != 'none'
44
- end
45
-
46
- # verify signature.
47
- def valid_sig?
48
- return false unless has_pki?
49
- digest = case pki_type
50
- when 'x509+sha256'
51
- OpenSSL::Digest::SHA256.new
52
- when 'x509+sha1'
53
- OpenSSL::Digest::SHA1.new
54
- else
55
- raise "pki_type: #{pki_type} is invalid type."
56
- end
57
- certs.first.public_key.verify(digest, signature, sig_message)
58
- end
59
-
60
- # verify expire time for payment request.
61
- def valid_time?
62
- expires = details.expires
63
- return true if expires == 0
64
- Time.now.to_i <= expires
65
- end
66
-
67
- private
68
-
69
- # Generate data to be signed
70
- def sig_message
71
- PaymentRequest.new(payment_details_version: payment_details_version,
72
- pki_type: pki_type, pki_data: pki_data, signature: '',
73
- serialized_payment_details: serialized_payment_details).encode
74
- end
75
-
76
- end
77
-
78
- end
79
- end
@@ -1,18 +0,0 @@
1
- module Bitcoin
2
- module Payments
3
-
4
- # https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#Certificates
5
- class X509Certificates < Protobuf::Message
6
-
7
- repeated :bytes, :certificate, 1
8
-
9
- # get certificates
10
- # @return [Array[OpenSSL::X509::Certificate]]
11
- def certs
12
- certificate.map{|v|OpenSSL::X509::Certificate.new(v)}
13
- end
14
-
15
- end
16
-
17
- end
18
- end
@@ -1,14 +0,0 @@
1
- require 'protobuf'
2
-
3
- module Bitcoin
4
- module Payments
5
-
6
- autoload :Output, 'bitcoin/payments/output.pb'
7
- autoload :Payment, 'bitcoin/payments/payment.pb'
8
- autoload :PaymentACK, 'bitcoin/payments/payment_ack.pb'
9
- autoload :PaymentDetails, 'bitcoin/payments/payment_details.pb'
10
- autoload :PaymentRequest, 'bitcoin/payments/payment_request.pb'
11
- autoload :X509Certificates, 'bitcoin/payments/x509_certificates.pb'
12
-
13
- end
14
- end