bitcoinrb 0.2.8 → 0.2.9

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: 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