bitcoinrb 0.2.8 → 0.2.9

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: ab7cf06beb0cf2d36a34c86558f3432fe951416c493c70782e7b7e647bc4e7fb
4
- data.tar.gz: a5e13cb5b79a0fd15f008405c5973326158d18d581fbc899f6bd6f9ded5b810a
3
+ metadata.gz: db45450b62847b14f755e93063e237e6f521a30de9125659dd2325b1b445d5b2
4
+ data.tar.gz: b1e446e457bdad6cc7d5449ffd7a97cf26fba50f0ec85659aa53584a01ac998d
5
5
  SHA512:
6
- metadata.gz: a5acbb1325f300e0e726f09447cb3f7806a2ff82e5f8f0bbef3c3879540b83e350f48ae33d424cec86f5bc5062a5ef64120c8f8aa4278cfeb173cbec57d96078
7
- data.tar.gz: ff5031d47259afb685e87ebfcb681efd6e041759943b5ce8753d69bd26763e87296ae3acadfa3eeee15e3e0ca20495f41e129e8c5ad6058017184c0460608df2
6
+ metadata.gz: 67767d3f68b7b1656730a43e1dce32c9dff0b43cdd603f813e6de1c4a1b3fe4c5c51784d6afb356db42a2cc230c2fa559e1ebc2f0992afe3bb55b3d635a05ffe
7
+ data.tar.gz: 4d38a01b81e90b79836c47cfd4912b6f70aba0ca257627fc0aba368ace227ee475b3205263e011c84fc00b43de4e33719b72c65fdddcb81210f5f35b19ba3bbd
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency 'siphash'
35
35
  spec.add_runtime_dependency 'protobuf', '3.8.5'
36
36
  spec.add_runtime_dependency 'scrypt'
37
+ spec.add_runtime_dependency 'activesupport', '~> 5.2.3'
37
38
 
38
39
  # for options
39
40
  spec.add_development_dependency 'leveldb-ruby'
@@ -51,6 +51,7 @@ module Bitcoin
51
51
  autoload :BlockFilter, 'bitcoin/block_filter'
52
52
  autoload :BitStreamWriter, 'bitcoin/bit_stream'
53
53
  autoload :BitStreamReader, 'bitcoin/bit_stream'
54
+ autoload :KeyPath, 'bitcoin/key_path'
54
55
 
55
56
  require_relative 'bitcoin/constants'
56
57
 
@@ -53,6 +53,7 @@ module Bitcoin
53
53
  data = 0
54
54
  while nbits > 0
55
55
  if offset == 8
56
+ raise IOError, 'stream is empty.' if stream.eof?
56
57
  self.buffer = stream.read(1).bth.to_i(16)
57
58
  self.offset = 0
58
59
  end
@@ -34,22 +34,24 @@ module Bitcoin
34
34
  attr_reader :genesis
35
35
  attr_reader :bip44_coin_type
36
36
 
37
+ attr_accessor :dust_relay_fee
38
+
37
39
  # fork coin id.
38
40
  attr_accessor :fork_id
39
41
 
40
42
  # mainnet genesis
41
43
  def self.mainnet
42
- YAML.load(File.open("#{__dir__}/chainparams/mainnet.yml"))
44
+ init('mainnet')
43
45
  end
44
46
 
45
47
  # testnet genesis
46
48
  def self.testnet
47
- YAML.load(File.open("#{__dir__}/chainparams/testnet.yml"))
49
+ init('testnet')
48
50
  end
49
51
 
50
52
  # regtest genesis
51
53
  def self.regtest
52
- YAML.load(File.open("#{__dir__}/chainparams/regtest.yml"))
54
+ init('regtest')
53
55
  end
54
56
 
55
57
  def mainnet?
@@ -76,6 +78,13 @@ module Bitcoin
76
78
  !fork_id.nil?
77
79
  end
78
80
 
81
+ def self.init(name)
82
+ i = YAML.load(File.open("#{__dir__}/chainparams/#{name}.yml"))
83
+ i.dust_relay_fee ||= Bitcoin::DUST_RELAY_TX_FEE
84
+ i
85
+ end
86
+
87
+ private_class_method :init
79
88
  end
80
89
 
81
90
  end
@@ -34,4 +34,5 @@ genesis:
34
34
  bits: 0x207fffff
35
35
  version: 1
36
36
  prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
37
- bip44_coin_type: 1
37
+ bip44_coin_type: 1
38
+ dust_relay_fee: 3600
@@ -22,6 +22,9 @@ module Bitcoin
22
22
  LOCKTIME_VERIFY_SEQUENCE = (1 << 0)
23
23
  LOCKTIME_MEDIAN_TIME_PAST = (1 << 1)
24
24
 
25
+ # Min feerate for defining dust.
26
+ DUST_RELAY_TX_FEE = 3000
27
+
25
28
  # script verify flags
26
29
  SCRIPT_VERIFY_NONE = 0
27
30
  SCRIPT_VERIFY_P2SH = (1 << 0)
@@ -185,4 +188,8 @@ module Bitcoin
185
188
 
186
189
  # Size of set to pick median time from.
187
190
  MEDIAN_TIME_SPAN = 11
191
+
192
+ BIP32_EXTKEY_WITH_VERSION_SIZE = 78
193
+
194
+ HARDENED_THRESHOLD = 2147483648 # 2**31
188
195
  end
@@ -3,8 +3,6 @@ module Bitcoin
3
3
  # Integers modulo the order of the curve(secp256k1)
4
4
  CURVE_ORDER = ECDSA::Group::Secp256k1.order
5
5
 
6
- HARDENED_THRESHOLD = 2147483648 # 2**31
7
-
8
6
  # BIP32 Extended private key
9
7
  class ExtKey
10
8
 
@@ -85,7 +83,7 @@ module Bitcoin
85
83
 
86
84
  # whether hardened key.
87
85
  def hardened?
88
- number >= HARDENED_THRESHOLD
86
+ number >= Bitcoin::HARDENED_THRESHOLD
89
87
  end
90
88
 
91
89
  # derive new key
@@ -93,12 +91,12 @@ module Bitcoin
93
91
  # @param [Boolean] harden whether hardened key or not. If true, 2^31 is added to +number+.
94
92
  # @return [Bitcoin::ExtKey] derived new key.
95
93
  def derive(number, harden = false)
96
- number += HARDENED_THRESHOLD if harden
94
+ number += Bitcoin::HARDENED_THRESHOLD if harden
97
95
  new_key = ExtKey.new
98
96
  new_key.depth = depth + 1
99
97
  new_key.number = number
100
98
  new_key.parent_fingerprint = fingerprint
101
- if number > (HARDENED_THRESHOLD - 1)
99
+ if number > (Bitcoin::HARDENED_THRESHOLD - 1)
102
100
  data = [0x00].pack('C') << key.priv_key.htb << [number].pack('N')
103
101
  else
104
102
  data = key.pubkey.htb << [number].pack('N')
@@ -134,6 +132,10 @@ module Bitcoin
134
132
  end
135
133
  end
136
134
 
135
+ def ==(other)
136
+ to_payload == other.to_payload
137
+ end
138
+
137
139
  def self.parse_from_payload(payload)
138
140
  buf = StringIO.new(payload)
139
141
  ext_key = ExtKey.new
@@ -155,7 +157,7 @@ module Bitcoin
155
157
 
156
158
  # get version bytes from purpose' value.
157
159
  def self.version_from_purpose(purpose)
158
- v = purpose - HARDENED_THRESHOLD
160
+ v = purpose - Bitcoin::HARDENED_THRESHOLD
159
161
  case v
160
162
  when 49
161
163
  Bitcoin.chain_params.bip49_privkey_p2wpkh_p2sh_version
@@ -247,7 +249,7 @@ module Bitcoin
247
249
 
248
250
  # whether hardened key.
249
251
  def hardened?
250
- number >= HARDENED_THRESHOLD
252
+ number >= Bitcoin::HARDENED_THRESHOLD
251
253
  end
252
254
 
253
255
  # derive child key
@@ -256,7 +258,7 @@ module Bitcoin
256
258
  new_key.depth = depth + 1
257
259
  new_key.number = number
258
260
  new_key.parent_fingerprint = fingerprint
259
- raise 'hardened key is not support' if number > (HARDENED_THRESHOLD - 1)
261
+ raise 'hardened key is not support' if number > (Bitcoin::HARDENED_THRESHOLD - 1)
260
262
  data = pub.htb << [number].pack('N')
261
263
  l = Bitcoin.hmac_sha512(chain_code, data)
262
264
  left = l[0..31].bth.to_i(16)
@@ -288,6 +290,10 @@ module Bitcoin
288
290
  end
289
291
  end
290
292
 
293
+ def ==(other)
294
+ to_payload == other.to_payload
295
+ end
296
+
291
297
  def self.parse_from_payload(payload)
292
298
  buf = StringIO.new(payload)
293
299
  ext_pubkey = ExtPubkey.new
@@ -301,6 +307,7 @@ module Bitcoin
301
307
  ext_pubkey
302
308
  end
303
309
 
310
+
304
311
  # import pub key from Base58 private key address
305
312
  def self.from_base58(address)
306
313
  ExtPubkey.parse_from_payload(Base58.decode(address).htb)
@@ -308,7 +315,7 @@ module Bitcoin
308
315
 
309
316
  # get version bytes from purpose' value.
310
317
  def self.version_from_purpose(purpose)
311
- v = purpose - HARDENED_THRESHOLD
318
+ v = purpose - Bitcoin::HARDENED_THRESHOLD
312
319
  case v
313
320
  when 49
314
321
  Bitcoin.chain_params.bip49_pubkey_p2wpkh_p2sh_version
@@ -0,0 +1,26 @@
1
+ module Bitcoin
2
+ module KeyPath
3
+
4
+ # key path convert an array of derive number
5
+ # @param [String] path_string
6
+ # @return [Array[Integer]] key path numbers.
7
+ def parse_key_path(path_string)
8
+ path_string.split('/').map.with_index do|p, index|
9
+ if index == 0
10
+ raise ArgumentError.new("#{path_string} is invalid format.") unless p == 'm'
11
+ next
12
+ end
13
+ raise ArgumentError.new("#{path_string} is invalid format.") unless p.delete("'") =~ /^[0-9]+$/
14
+ (p[-1] == "'" ? p.delete("'").to_i + Bitcoin::HARDENED_THRESHOLD : p.to_i)
15
+ end[1..-1]
16
+ end
17
+
18
+ # key path numbers convert to path string.
19
+ # @param [Array[Integer]] key path numbers.
20
+ # @return [String] path string.
21
+ def to_key_path(numbers)
22
+ "m/#{numbers.map{|p| p >= Bitcoin::HARDENED_THRESHOLD ? "#{p - Bitcoin::HARDENED_THRESHOLD}'" : p.to_s}.join('/')}"
23
+ end
24
+
25
+ end
26
+ end
@@ -7,11 +7,12 @@ module Bitcoin
7
7
  autoload :Tx, 'bitcoin/psbt/tx'
8
8
  autoload :Input, 'bitcoin/psbt/input'
9
9
  autoload :Output, 'bitcoin/psbt/output'
10
+ autoload :KeyOriginInfo, 'bitcoin/psbt/key_origin_info'
10
11
  autoload :HDKeyPath, 'bitcoin/psbt/hd_key_path'
11
12
 
12
13
  # constants for PSBT
13
14
  PSBT_MAGIC_BYTES = 0x70736274
14
- PSBT_GLOBAL_TYPES = {unsigned_tx: 0x00}
15
+ PSBT_GLOBAL_TYPES = {unsigned_tx: 0x00, xpub: 0x01}
15
16
  PSBT_IN_TYPES = {non_witness_utxo: 0x00, witness_utxo: 0x01, partial_sig: 0x02,
16
17
  sighash: 0x03, redeem_script: 0x04, witness_script: 0x05,
17
18
  bip32_derivation: 0x06, script_sig: 0x07, script_witness: 0x08}
@@ -5,31 +5,30 @@ module Bitcoin
5
5
  # see https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Specification
6
6
  class HDKeyPath
7
7
 
8
- attr_reader :pubkey
9
- attr_accessor :fingerprint
10
- attr_reader :path
8
+ attr_reader :pubkey # String
9
+ attr_reader :info # KeyOriginInfo
11
10
 
12
- def initialize(pubkey, fingerprint: nil, path: [])
13
- @pubkey = pubkey
14
- @fingerprint = fingerprint
15
- @path = path
16
- end
17
-
18
- # parse hd key path from payload.
19
- # @param [String] pubkey a public key with binary format.
20
- # @param [String] payload hd key path value with binary format.
21
- # @return [Bitcoin::PSBT::HDKeyPath]
22
- def self.parse_from_payload(pubkey, payload)
11
+ def initialize(pubkey, info)
12
+ pubkey = pubkey.encoding == Encoding::ASCII_8BIT ? pubkey : pubkey.htb
23
13
  raise ArgumentError, 'Size of key was not the expected size for the type BIP32 keypath.' unless [Bitcoin::Key::PUBLIC_KEY_SIZE, Bitcoin::Key::COMPRESSED_PUBLIC_KEY_SIZE].include?(pubkey.bytesize)
24
14
  pubkey = Bitcoin::Key.new(pubkey: pubkey.bth)
25
15
  raise ArgumentError, 'Invalid pubkey' unless pubkey.fully_valid_pubkey?
26
- self.new(pubkey.pubkey, fingerprint: payload[0...4].bth, path: payload[4..-1].unpack('I*'))
16
+ @pubkey = pubkey.pubkey
17
+ @info = info
27
18
  end
28
19
 
29
20
  # generate payload which consist of pubkey and fingerprint, hd key path payload.
30
21
  # @return [String] a payload
31
22
  def to_payload(type = PSBT_IN_TYPES[:bip32_derivation])
32
- PSBT.serialize_to_vector(type, key: pubkey.htb, value: fingerprint.htb + path.pack('I*'))
23
+ PSBT.serialize_to_vector(type, key: pubkey.htb, value: info.to_payload)
24
+ end
25
+
26
+ def to_h
27
+ {pubkey: pubkey}.merge(info.to_h)
28
+ end
29
+
30
+ def to_s
31
+ to_h.to_s
33
32
  end
34
33
 
35
34
  end
@@ -72,7 +72,7 @@ module Bitcoin
72
72
  when PSBT_IN_TYPES[:bip32_derivation]
73
73
  raise ArgumentError, 'Invalid bip32 typed key.' unless key_len
74
74
  raise ArgumentError, 'Duplicate Key, pubkey derivation path already provided.' if input.hd_key_paths[key.bth]
75
- input.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.parse_from_payload(key, value)
75
+ input.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.new(key, Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value))
76
76
  when PSBT_IN_TYPES[:script_sig]
77
77
  raise ArgumentError, 'Invalid final scriptsig typed key.' unless key_len == 1
78
78
  raise ArgumentError, 'Duplicate Key, input final scriptSig already provided.' if input.final_script_sig
@@ -0,0 +1,34 @@
1
+ module Bitcoin
2
+ module PSBT
3
+
4
+ class KeyOriginInfo
5
+
6
+ include Bitcoin::KeyPath
7
+
8
+ attr_reader :fingerprint # String hex format
9
+ attr_reader :key_paths # Array[Integer]
10
+
11
+ def initialize(fingerprint: nil, key_paths: [])
12
+ @fingerprint = fingerprint
13
+ @key_paths = key_paths
14
+ end
15
+
16
+ def self.parse_from_payload(payload)
17
+ buf = StringIO.new(payload)
18
+ self.new(fingerprint: buf.read(4).bth, key_paths: buf.read.unpack('I*'))
19
+ end
20
+
21
+ def to_payload
22
+ fingerprint.htb + key_paths.pack('I*')
23
+ end
24
+
25
+ def to_h
26
+ {fingerprint: fingerprint, key_paths: to_key_path(key_paths)}
27
+ end
28
+
29
+ def to_s
30
+ to_h.to_s
31
+ end
32
+ end
33
+ end
34
+ end
@@ -40,7 +40,7 @@ module Bitcoin
40
40
  output.witness_script = value
41
41
  when PSBT_OUT_TYPES[:bip32_derivation]
42
42
  raise ArgumentError, 'Duplicate Key, pubkey derivation path already provided' if output.hd_key_paths[key.bth]
43
- output.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.parse_from_payload(key, value)
43
+ output.hd_key_paths[key.bth] = Bitcoin::PSBT::HDKeyPath.new(key, Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value))
44
44
  else
45
45
  unknown_key = ([key_type].pack('C') + key).bth
46
46
  raise ArgumentError, 'Duplicate Key, key for unknown value already provided' if output.unknowns[unknown_key]
@@ -1,14 +1,39 @@
1
1
  module Bitcoin
2
2
  module PSBT
3
3
 
4
+ class GlobalXpub
5
+
6
+ attr_reader :xpub # Bitcoin::ExtPubkey
7
+ attr_reader :info # Bitcoin::PSBT::KeyOriginInfo
8
+
9
+ def initialize(xpub, info)
10
+ @xpub = xpub
11
+ @info = info
12
+ end
13
+
14
+ def to_payload
15
+ PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:xpub], key: xpub.to_payload, value: info.to_payload)
16
+ end
17
+
18
+ def to_h
19
+ {xpub: xpub.to_payload.bth}.merge(info.to_h)
20
+ end
21
+
22
+ def to_s
23
+ to_h.to_s
24
+ end
25
+ end
26
+
4
27
  class Tx
5
28
  attr_accessor :tx
29
+ attr_accessor :xpubs
6
30
  attr_reader :inputs
7
31
  attr_reader :outputs
8
32
  attr_accessor :unknowns
9
33
 
10
34
  def initialize(tx = nil)
11
35
  @tx = tx
36
+ @xpubs = []
12
37
  @inputs = tx ? tx.in.map{Input.new}: []
13
38
  @outputs = tx ? tx.out.map{Output.new}: []
14
39
  @unknowns = {}
@@ -49,6 +74,14 @@ module Bitcoin
49
74
  partial_tx.tx.in.each do |tx_in|
50
75
  raise ArgumentError, 'Unsigned tx does not have empty scriptSigs and scriptWitnesses.' if !tx_in.script_sig.empty? || !tx_in.script_witness.empty?
51
76
  end
77
+ when PSBT_GLOBAL_TYPES[:xpub]
78
+ raise ArgumentError, 'Size of key was not the expected size for the type global xpub.' unless key.size == Bitcoin::BIP32_EXTKEY_WITH_VERSION_SIZE
79
+ xpub = Bitcoin::ExtPubkey.parse_from_payload(key)
80
+ raise ArgumentError, 'Invalid pubkey.' unless xpub.key.fully_valid_pubkey?
81
+ raise ArgumentError, 'Duplicate key, global xpub already provided' if partial_tx.xpubs.any?{|x|x.xpub == xpub}
82
+ info = Bitcoin::PSBT::KeyOriginInfo.parse_from_payload(value)
83
+ raise ArgumentError, "global xpub's depth and the number of indexes not matched." unless xpub.depth == info.key_paths.size
84
+ partial_tx.xpubs << Bitcoin::PSBT::GlobalXpub.new(xpub, info)
52
85
  else
53
86
  raise ArgumentError, 'Duplicate Key, key for unknown value already provided.' if partial_tx.unknowns[key]
54
87
  partial_tx.unknowns[([key_type].pack('C') + key).bth] = value
@@ -104,6 +137,7 @@ module Bitcoin
104
137
  payload = PSBT_MAGIC_BYTES.itb << 0xff.itb
105
138
 
106
139
  payload << PSBT.serialize_to_vector(PSBT_GLOBAL_TYPES[:unsigned_tx], value: tx.to_payload)
140
+ payload << xpubs.map(&:to_payload).join
107
141
 
108
142
  payload << unknowns.map {|k,v|Bitcoin.pack_var_int(k.htb.bytesize) << k.htb << Bitcoin.pack_var_int(v.bytesize) << v}.join
109
143
 
@@ -58,7 +58,6 @@ module Bitcoin
58
58
  new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
59
59
  end
60
60
 
61
-
62
61
  # generate p2wsh script for +redeem_script+
63
62
  # @param [Script] redeem_script target redeem script
64
63
  # @param [Script] p2wsh script
@@ -144,10 +143,6 @@ module Bitcoin
144
143
  chunks.join
145
144
  end
146
145
 
147
- def to_hex
148
- to_payload.bth
149
- end
150
-
151
146
  def empty?
152
147
  chunks.size == 0
153
148
  end
@@ -219,6 +214,12 @@ module Bitcoin
219
214
  true
220
215
  end
221
216
 
217
+ # get public keys in the stack.
218
+ # @return[Array[String]] an array of the pubkeys with hex format.
219
+ def get_pubkeys
220
+ chunks.select{|c|c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) && [2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))}.map{|c|c.pushed_data.bth}
221
+ end
222
+
222
223
  # A witness program is any valid Script that consists of a 1-byte push opcode followed by a data push between 2 and 40 bytes.
223
224
  def witness_program?
224
225
  return false if size < 4 || size > 42 || chunks.size < 2
@@ -495,6 +496,19 @@ module Bitcoin
495
496
  h
496
497
  end
497
498
 
499
+ # Returns whether the script is guaranteed to fail at execution, regardless of the initial stack.
500
+ # This allows outputs to be pruned instantly when entering the UTXO set.
501
+ # @return [Boolean] whether the script is guaranteed to fail at execution
502
+ def unspendable?
503
+ (size > 0 && op_return?) || size > Bitcoin::MAX_SCRIPT_SIZE
504
+ end
505
+
506
+ # convert payload to hex data.
507
+ # @return [String] script with hex format.
508
+ def to_hex
509
+ to_payload.bth
510
+ end
511
+
498
512
  private
499
513
 
500
514
  # generate p2pkh address. if script dose not p2pkh, return nil.
@@ -74,6 +74,8 @@ module Bitcoin
74
74
  time = []
75
75
  Bitcoin::MEDIAN_TIME_SPAN.times do
76
76
  entry = find_entry_by_hash(hash)
77
+ break unless entry
78
+
77
79
  time << entry.header.time
78
80
  hash = entry.header.prev_hash
79
81
  end
@@ -104,6 +104,12 @@ module Bitcoin
104
104
  witness? ? serialize_witness_format : serialize_old_format
105
105
  end
106
106
 
107
+ # convert tx to hex format.
108
+ # @return [String] tx with hex format.
109
+ def to_hex
110
+ to_payload.bth
111
+ end
112
+
107
113
  def coinbase_tx?
108
114
  inputs.length == 1 && inputs.first.coinbase?
109
115
  end
@@ -156,7 +162,7 @@ module Bitcoin
156
162
  return false unless o.script_pubkey.standard?
157
163
  data_count += 1 if o.script_pubkey.op_return?
158
164
  # TODO add non P2SH multisig relay(permitbaremultisig)
159
- # TODO add dust relay check
165
+ return false if o.dust?
160
166
  end
161
167
  return false if data_count > 1
162
168
  true
@@ -233,6 +239,14 @@ module Bitcoin
233
239
  }
234
240
  end
235
241
 
242
+ # Verify transaction validity.
243
+ # @return [Boolean] whether this tx is valid or not.
244
+ def valid?
245
+ state = Bitcoin::ValidationState.new
246
+ validation = Bitcoin::Validation.new
247
+ validation.check_tx(self, state) && state.valid?
248
+ end
249
+
236
250
  private
237
251
 
238
252
  # generate sighash with legacy format
@@ -45,6 +45,30 @@ module Bitcoin
45
45
  to_payload == other.to_payload
46
46
  end
47
47
 
48
+ # Returns this output bytesize
49
+ # @return [Integer] bytesize
50
+ def size
51
+ to_payload.bytesize
52
+ end
53
+
54
+ # Whether this output is dust or not
55
+ # @return [Boolean]
56
+ def dust?
57
+ value < dust_threshold
58
+ end
59
+
60
+ private
61
+
62
+ def dust_threshold
63
+ return 0 if script_pubkey.unspendable?
64
+ n_size = size
65
+ n_size += script_pubkey.witness_program? ? (32 + 4 + 1 + (107 / Bitcoin::WITNESS_SCALE_FACTOR) + 4) : (32 + 4 + 1 + 107 + 4)
66
+ fee = n_size * Bitcoin.chain_params.dust_relay_fee / 1000
67
+ if fee == 0 && n_size != 0
68
+ fee = Bitcoin.chain_params.dust_relay_fee > 0 ? 1 : -1
69
+ end
70
+ fee
71
+ end
48
72
  end
49
73
 
50
74
  end
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.2.8"
2
+ VERSION = "0.2.9"
3
3
  end
@@ -5,6 +5,7 @@ module Bitcoin
5
5
  class MasterKey
6
6
  extend Bitcoin::Util
7
7
  include Bitcoin::Util
8
+ include Bitcoin::KeyPath
8
9
 
9
10
  attr_reader :seed
10
11
  attr_accessor :salt
@@ -65,15 +66,7 @@ module Bitcoin
65
66
  # @return [Bitcoin::ExtKey]
66
67
  def derive(path)
67
68
  derived_key = key
68
- path.split('/').each_with_index do|p, index|
69
- if index == 0
70
- raise ArgumentError.new("#{path} is invalid format.") unless p == 'm'
71
- next
72
- end
73
- raise ArgumentError.new("#{path} is invalid format.") unless p.delete("'") =~ /^[0-9]+$/
74
- num = (p[-1] == "'" ? p.delete("'").to_i + Bitcoin::HARDENED_THRESHOLD : p.to_i)
75
- derived_key = derived_key.derive(num)
76
- end
69
+ parse_key_path(path).each{|num| derived_key = derived_key.derive(num)}
77
70
  derived_key
78
71
  end
79
72
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-04 00:00:00.000000000 Z
11
+ date: 2019-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
208
  version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: activesupport
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: 5.2.3
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: 5.2.3
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: leveldb-ruby
211
225
  requirement: !ruby/object:Gem::Requirement
@@ -316,6 +330,7 @@ files:
316
330
  - lib/bitcoin/ext_key.rb
317
331
  - lib/bitcoin/gcs_filter.rb
318
332
  - lib/bitcoin/key.rb
333
+ - lib/bitcoin/key_path.rb
319
334
  - lib/bitcoin/logger.rb
320
335
  - lib/bitcoin/merkle_tree.rb
321
336
  - lib/bitcoin/message.rb
@@ -385,6 +400,7 @@ files:
385
400
  - lib/bitcoin/psbt.rb
386
401
  - lib/bitcoin/psbt/hd_key_path.rb
387
402
  - lib/bitcoin/psbt/input.rb
403
+ - lib/bitcoin/psbt/key_origin_info.rb
388
404
  - lib/bitcoin/psbt/output.rb
389
405
  - lib/bitcoin/psbt/tx.rb
390
406
  - lib/bitcoin/rpc.rb