bitcoinrb 1.2.1 → 1.4.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: 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