bitcoinrb 0.1.5 → 0.1.6

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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -0
  4. data/README.md +40 -1
  5. data/bitcoinrb.gemspec +1 -0
  6. data/lib/bitcoin/base58.rb +2 -4
  7. data/lib/bitcoin/bloom_filter.rb +71 -0
  8. data/lib/bitcoin/chain_params.rb +8 -0
  9. data/lib/bitcoin/constants.rb +8 -1
  10. data/lib/bitcoin/ext_key.rb +0 -4
  11. data/lib/bitcoin/key.rb +31 -7
  12. data/lib/bitcoin/message/block_transaction_request.rb +45 -0
  13. data/lib/bitcoin/message/block_transactions.rb +31 -0
  14. data/lib/bitcoin/message/block_txn.rb +27 -0
  15. data/lib/bitcoin/message/cmpct_block.rb +42 -0
  16. data/lib/bitcoin/message/get_block_txn.rb +27 -0
  17. data/lib/bitcoin/message/header_and_short_ids.rb +57 -0
  18. data/lib/bitcoin/message/prefilled_tx.rb +29 -0
  19. data/lib/bitcoin/message/send_cmpct.rb +1 -1
  20. data/lib/bitcoin/message/version.rb +1 -1
  21. data/lib/bitcoin/message.rb +7 -0
  22. data/lib/bitcoin/network/message_handler.rb +7 -1
  23. data/lib/bitcoin/network/peer.rb +26 -4
  24. data/lib/bitcoin/network/pool.rb +17 -1
  25. data/lib/bitcoin/node/cli.rb +5 -0
  26. data/lib/bitcoin/node/configuration.rb +1 -0
  27. data/lib/bitcoin/node/spv.rb +21 -1
  28. data/lib/bitcoin/rpc/request_handler.rb +6 -0
  29. data/lib/bitcoin/script/script.rb +4 -1
  30. data/lib/bitcoin/script/script_interpreter.rb +5 -18
  31. data/lib/bitcoin/secp256k1/native.rb +9 -3
  32. data/lib/bitcoin/secp256k1/ruby.rb +51 -23
  33. data/lib/bitcoin/secp256k1.rb +2 -0
  34. data/lib/bitcoin/store/chain_entry.rb +1 -2
  35. data/lib/bitcoin/store/db/level_db.rb +2 -3
  36. data/lib/bitcoin/store/spv_chain.rb +1 -1
  37. data/lib/bitcoin/tx.rb +8 -4
  38. data/lib/bitcoin/tx_in.rb +3 -0
  39. data/lib/bitcoin/tx_out.rb +3 -0
  40. data/lib/bitcoin/util.rb +3 -0
  41. data/lib/bitcoin/version.rb +1 -1
  42. data/lib/bitcoin/wallet/account.rb +3 -3
  43. data/lib/bitcoin/wallet/base.rb +15 -1
  44. data/lib/bitcoin/wallet/db.rb +9 -2
  45. data/lib/bitcoin.rb +23 -3
  46. metadata +25 -3
@@ -26,6 +26,7 @@ module Bitcoin
26
26
  @max_outbound = MAX_OUTBOUND_CONNECTIONS
27
27
  @chain = chain
28
28
  @logger = Bitcoin::Logger.create(:debug)
29
+ @configuration = configuration
29
30
  @peer_discovery = PeerDiscovery.new(configuration)
30
31
  @started = false
31
32
  end
@@ -39,7 +40,7 @@ module Bitcoin
39
40
  port = Bitcoin.chain_params.default_port
40
41
  EM::Iterator.new(addr_list, Bitcoin::PARALLEL_THREAD).each do |ip, iter|
41
42
  if pending_peers.size < MAX_OUTBOUND_CONNECTIONS
42
- peer = Peer.new(ip, port, self)
43
+ peer = Peer.new(ip, port, self, @configuration)
43
44
  pending_peers << peer
44
45
  peer.connect
45
46
  iter.next
@@ -73,6 +74,21 @@ module Bitcoin
73
74
  peers.each { |peer| peer.broadcast_tx(tx) }
74
75
  end
75
76
 
77
+ # new bloom filter.
78
+ def filter_load(bloom)
79
+ peers.each { |peer| peer.send_filter_load(bloom) }
80
+ end
81
+
82
+ # add element to bloom filter.
83
+ def filter_add(element)
84
+ peers.each { |peer| peer.send_filter_add(element) }
85
+ end
86
+
87
+ # clear bloom filter.
88
+ def filter_clear
89
+ peers.each { |peer| peer.send_filter_clear }
90
+ end
91
+
76
92
  def handle_error(e)
77
93
  terminate
78
94
  end
@@ -45,6 +45,11 @@ module Bitcoin
45
45
  request('listwallets')
46
46
  end
47
47
 
48
+ desc 'getwalletinfo', 'Returns an object containing various wallet state info.'
49
+ def getwalletinfo
50
+ request('getwalletinfo')
51
+ end
52
+
48
53
  private
49
54
 
50
55
  def config
@@ -9,6 +9,7 @@ module Bitcoin
9
9
  def initialize(opts = {})
10
10
  # TODO apply configuration file.
11
11
  opts[:network] = :mainnet unless opts[:network]
12
+ opts[:relay] = false unless opts[:relay]
12
13
  Bitcoin.chain_params = opts[:network]
13
14
 
14
15
  begin
@@ -10,6 +10,7 @@ module Bitcoin
10
10
  attr_accessor :running
11
11
  attr_reader :configuration
12
12
  attr_accessor :server
13
+ attr_accessor :wallet
13
14
 
14
15
  def initialize(configuration)
15
16
  @chain = Bitcoin::Store::SPVChain.new
@@ -17,6 +18,10 @@ module Bitcoin
17
18
  @pool = Bitcoin::Network::Pool.new(@chain, @configuration)
18
19
  @logger = Bitcoin::Logger.create(:debug)
19
20
  @running = false
21
+ @wallet = Bitcoin::Wallet::Base.current_wallet
22
+ # TODO : optimize bloom filter parameters
23
+ # TODO : load public keys in wallet.
24
+ @bloom = Bitcoin::BloomFilter.new(512, 0.01)
20
25
  end
21
26
 
22
27
  # open the node.
@@ -43,7 +48,22 @@ module Bitcoin
43
48
  logger.debug "broadcast tx: #{tx.to_payload.bth}"
44
49
  end
45
50
 
46
- end
51
+ # new bloom filter.
52
+ def filter_load
53
+ pool.filter_load(@bloom)
54
+ end
55
+
56
+ # add filter element to bloom filter.
57
+ # [String] element. the hex string of txid, public key, public key hash or outpoint.
58
+ def filter_add(element)
59
+ @bloom.add(element)
60
+ pool.filter_add(element)
61
+ end
47
62
 
63
+ # clear bloom filter.
64
+ def filter_clear
65
+ pool.filter_clear
66
+ end
67
+ end
48
68
  end
49
69
  end
@@ -92,6 +92,12 @@ module Bitcoin
92
92
  Bitcoin::Wallet::Base.wallet_paths(wallet_path_prefix)
93
93
  end
94
94
 
95
+ # get current wallet information.
96
+ def getwalletinfo
97
+ return {} unless node.wallet
98
+ {wallet_id: node.wallet.wallet_id, version: node.wallet.version}
99
+ end
100
+
95
101
  end
96
102
 
97
103
  end
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  module Bitcoin
2
5
 
3
6
  # bitcoin script
@@ -304,7 +307,7 @@ module Bitcoin
304
307
  return '' if i == 0
305
308
  negative = i < 0
306
309
 
307
- hex = i.abs.to_s(16)
310
+ hex = i.abs.to_even_length_hex
308
311
  hex = '0' + hex unless (hex.length % 2).zero?
309
312
  v = hex.htb.reverse # change endian
310
313
 
@@ -14,7 +14,7 @@ module Bitcoin
14
14
  DISABLE_OPCODES = [OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT, OP_AND, OP_OR, OP_XOR, OP_2MUL, OP_2DIV, OP_DIV, OP_MUL, OP_MOD, OP_LSHIFT, OP_RSHIFT]
15
15
 
16
16
  # initialize runner
17
- def initialize(flags: [], checker: TxChecker.new)
17
+ def initialize(flags: SCRIPT_VERIFY_NONE, checker: TxChecker.new)
18
18
  @stack, @debug = [], []
19
19
  @flags = flags
20
20
  @checker = checker
@@ -219,10 +219,7 @@ module Bitcoin
219
219
  when OP_NOP1, OP_NOP4..OP_NOP10
220
220
  return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
221
221
  when OP_CHECKLOCKTIMEVERIFY
222
- unless flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
223
- return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
224
- next
225
- end
222
+ next unless flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
226
223
  return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
227
224
  # Note that elsewhere numeric opcodes are limited to operands in the range -2**31+1 to 2**31-1,
228
225
  # however it is legal for opcodes to produce results exceeding that range.
@@ -235,12 +232,7 @@ module Bitcoin
235
232
  return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if locktime < 0
236
233
  return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME) unless checker.check_locktime(locktime)
237
234
  when OP_CHECKSEQUENCEVERIFY
238
- unless flag?(SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
239
- if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
240
- return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
241
- end
242
- next
243
- end
235
+ next unless flag?(SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
244
236
  return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
245
237
 
246
238
  # nSequence, like nLockTime, is a 32-bit unsigned integer field.
@@ -522,13 +514,7 @@ module Bitcoin
522
514
  private
523
515
 
524
516
  def flag?(flag)
525
- (all_flags & flag) != 0
526
- end
527
-
528
- def all_flags
529
- result = SCRIPT_VERIFY_NONE
530
- flags.each{ |f| result |= f }
531
- result
517
+ (flags & flag) != 0
532
518
  end
533
519
 
534
520
  # pop the item with the int value for the number specified by +count+ from the stack.
@@ -610,6 +596,7 @@ module Bitcoin
610
596
  return false if sig.empty?
611
597
  s = sig.unpack('C*')
612
598
  hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
599
+ hash_type &= (~(Bitcoin::SIGHASH_FORK_ID)) if Bitcoin.chain_params.fork_chain? # for fork coin.
613
600
  return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single]
614
601
  true
615
602
  end
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  module Bitcoin
2
5
  module Secp256k1
3
6
 
@@ -101,9 +104,13 @@ module Bitcoin
101
104
  Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
102
105
  end
103
106
 
104
- def sign_data(data, priv_key)
107
+ # sign data.
108
+ # @param [String] data a data to be signed with binary format
109
+ # @param [String] privkey a private key using sign
110
+ # @return [String] signature data with binary format
111
+ def sign_data(data, privkey)
105
112
  with_context do |context|
106
- secret = FFI::MemoryPointer.new(:uchar, priv_key.htb.bytesize).put_bytes(0, priv_key.htb)
113
+ secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
107
114
  raise 'priv_key invalid' unless secp256k1_ec_seckey_verify(context, secret)
108
115
 
109
116
  internal_signature = FFI::MemoryPointer.new(:uchar, 64)
@@ -137,7 +144,6 @@ module Bitcoin
137
144
  signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
138
145
  internal_signature = FFI::MemoryPointer.new(:uchar, 64)
139
146
  result = secp256k1_ecdsa_signature_parse_der(context, internal_signature, signature, signature.size)
140
- #result = ecdsa_signature_parse_der_lax(context, internal_signature, signature, signature.size)
141
147
  return false unless result
142
148
 
143
149
  # libsecp256k1's ECDSA verification requires lower-S signatures, which have not historically been enforced in Bitcoin, so normalize them first.
@@ -1,8 +1,6 @@
1
1
  module Bitcoin
2
2
  module Secp256k1
3
3
 
4
- GROUP = ECDSA::Group::Secp256k1
5
-
6
4
  # secp256 module using ecdsa gem
7
5
  # https://github.com/DavidEGrayson/ruby_ecdsa
8
6
  module Ruby
@@ -25,19 +23,35 @@ module Bitcoin
25
23
  end
26
24
 
27
25
  # sign data.
28
- # @param [String] data a data to be signed
26
+ # @param [String] data a data to be signed with binary format
29
27
  # @param [String] privkey a private key using sign
30
28
  # @return [String] signature data with binary format
31
29
  def sign_data(data, privkey)
32
- # digest = Digest::SHA2.digest(data)
33
- private_key = ECDSA::Format::IntegerOctetString.decode(privkey.htb)
34
- signature = nil
35
- while signature.nil?
36
- # TODO support rfc 6979 https://tools.ietf.org/html/rfc6979
37
- temp_key = 1 + SecureRandom.random_number(GROUP.order - 1)
38
- signature = ECDSA.sign(GROUP, private_key, data, temp_key)
30
+ privkey = privkey.htb
31
+ private_key = ECDSA::Format::IntegerOctetString.decode(privkey)
32
+ nonce = generate_rfc6979_nonce(data, privkey)
33
+
34
+ # port form ecdsa gem.
35
+ r_point = GROUP.new_point(nonce)
36
+
37
+ point_field = ECDSA::PrimeField.new(GROUP.order)
38
+ r = point_field.mod(r_point.x)
39
+ return nil if r.zero?
40
+
41
+ e = ECDSA.normalize_digest(data, GROUP.bit_length)
42
+ s = point_field.mod(point_field.inverse(nonce) * (e + r * private_key))
43
+
44
+ if s > (GROUP.order / 2) # convert low-s
45
+ s = GROUP.order - s
39
46
  end
40
- ECDSA::Format::SignatureDerString.encode(signature) # signature with DER format
47
+
48
+ return nil if s.zero?
49
+
50
+ signature = ECDSA::Signature.new(r, s)
51
+ public_key = Bitcoin::Key.new(priv_key: privkey.bth).pubkey
52
+ signature = ECDSA::Format::SignatureDerString.encode(signature) # signature with DER format
53
+ raise 'Creation of signature failed.' unless Bitcoin::Secp256k1::Ruby.verify_sig(data, signature, public_key)
54
+ signature
41
55
  end
42
56
 
43
57
  # verify signature using public key
@@ -48,24 +62,13 @@ module Bitcoin
48
62
  def verify_sig(digest, sig, pubkey)
49
63
  begin
50
64
  k = ECDSA::Format::PointOctetString.decode(repack_pubkey(pubkey), GROUP)
51
- signature = repack_sig(sig)
65
+ signature = ECDSA::Format::SignatureDerString.decode(sig)
52
66
  ECDSA.valid_signature?(k, digest, signature)
53
67
  rescue Exception
54
68
  false
55
69
  end
56
70
  end
57
71
 
58
- # repack signature for OpenSSL 1.0.1k handling of DER signatures
59
- # https://github.com/bitcoin/bitcoin/pull/5634/files
60
- def repack_sig(sig)
61
- sig_array = sig.unpack('C*')
62
- len_r = sig_array[3]
63
- r = sig_array[4...(len_r+4)].pack('C*').bth
64
- len_s = sig_array[len_r + 5]
65
- s = sig_array[(len_r + 6)...(len_r + 6 + len_s)].pack('C*').bth
66
- ECDSA::Signature.new(r.to_i(16), s.to_i(16))
67
- end
68
-
69
72
  # if +pubkey+ is hybrid public key format, it convert uncompressed format.
70
73
  # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
71
74
  def repack_pubkey(pubkey)
@@ -79,6 +82,31 @@ module Bitcoin
79
82
  end
80
83
  end
81
84
 
85
+ # generate temporary key k to be used when ECDSA sign.
86
+ # https://tools.ietf.org/html/rfc6979#section-3.2
87
+ def generate_rfc6979_nonce(data, privkey)
88
+ v = ('01' * 32).htb
89
+ k = ('00' * 32).htb
90
+ # 3.2.d
91
+ k = Bitcoin.hmac_sha256(k, v + '00'.htb + privkey + data)
92
+ # 3.2.e
93
+ v = Bitcoin.hmac_sha256(k, v)
94
+ # 3.2.f
95
+ k = Bitcoin.hmac_sha256(k, v + '01'.htb + privkey + data)
96
+ # 3.2.g
97
+ v = Bitcoin.hmac_sha256(k, v)
98
+ # 3.2.h
99
+ t = ''
100
+ 10000.times do
101
+ v = Bitcoin.hmac_sha256(k, v)
102
+ t = (t + v)
103
+ t_num = t.bth.to_i(16)
104
+ return t_num if 1 <= t_num && t_num < GROUP.order
105
+ k = Bitcoin.hmac_sha256(k, v + '00'.htb)
106
+ v = Bitcoin.hmac_sha256(v)
107
+ end
108
+ raise 'A valid nonce was not found.'
109
+ end
82
110
  end
83
111
 
84
112
  end
@@ -2,6 +2,8 @@ module Bitcoin
2
2
 
3
3
  module Secp256k1
4
4
 
5
+ GROUP = ECDSA::Group::Secp256k1
6
+
5
7
  autoload :Ruby, 'bitcoin/secp256k1/ruby'
6
8
  autoload :Native, 'bitcoin/secp256k1/native'
7
9
 
@@ -51,8 +51,7 @@ module Bitcoin
51
51
 
52
52
  # generate payload
53
53
  def to_payload
54
- height_value = height.to_s(16)
55
- height_value = '0' + height_value if height_value.length.odd?
54
+ height_value = height.to_even_length_hex
56
55
  height_value = height_value.htb.reverse
57
56
  Bitcoin.pack_var_int(height_value.bytesize) + height_value + header.to_payload
58
57
  end
@@ -74,8 +74,7 @@ module Bitcoin
74
74
 
75
75
  # generate height key
76
76
  def height_key(height)
77
- height = height.to_s(16)
78
- height = '0' + height if height.bytesize.odd?
77
+ height = height.to_even_length_hex
79
78
  KEY_PREFIX[:height] + height.htb.reverse.bth
80
79
  end
81
80
 
@@ -98,4 +97,4 @@ module Bitcoin
98
97
 
99
98
  end
100
99
  end
101
- end
100
+ end
@@ -43,7 +43,7 @@ module Bitcoin
43
43
  # @param [Bitcoin::BlockHeader] header a block header.
44
44
  # @return [Bitcoin::Store::ChainEntry] appended block header entry.
45
45
  def append_header(header)
46
- logger.debug("append header #{header.hash}")
46
+ logger.info("append header #{header.hash}")
47
47
  raise "this header is invalid. #{header.hash}" unless header.valid?
48
48
  best_block = latest_block
49
49
  current_height = best_block.height
data/lib/bitcoin/tx.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  module Bitcoin
2
5
 
3
6
  # Transaction class
@@ -178,7 +181,7 @@ module Bitcoin
178
181
  raise ArgumentError, 'script_pubkey must be specified.' unless output_script
179
182
  raise ArgumentError, 'unsupported sig version specified.' unless SIG_VERSION.include?(sig_version)
180
183
 
181
- if sig_version == :witness_v0
184
+ if sig_version == :witness_v0 || Bitcoin.chain_params.fork_chain?
182
185
  raise ArgumentError, 'amount must be specified.' unless amount
183
186
  sighash_for_witness(input_index, output_script, hash_type, amount, skip_separator_index)
184
187
  else
@@ -201,7 +204,7 @@ module Bitcoin
201
204
  script_pubkey = redeem_script if redeem_script.p2wpkh?
202
205
  end
203
206
 
204
- if has_witness
207
+ if has_witness || Bitcoin.chain_params.fork_chain?
205
208
  verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
206
209
  else
207
210
  verify_input_sig_for_legacy(input_index, script_pubkey, flags)
@@ -271,6 +274,7 @@ module Bitcoin
271
274
  if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
272
275
  hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
273
276
  end
277
+ hash_type |= (Bitcoin.chain_params.fork_id << 8) if Bitcoin.chain_params.fork_chain?
274
278
  buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint,
275
279
  script_code ,amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
276
280
  Bitcoin.double_sha256(buf)
@@ -287,8 +291,8 @@ module Bitcoin
287
291
 
288
292
  # verify input signature for witness tx.
289
293
  def verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
290
- flags << SCRIPT_VERIFY_WITNESS
291
- flags << SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
294
+ flags |= SCRIPT_VERIFY_WITNESS
295
+ flags |= SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
292
296
  checker = Bitcoin::TxChecker.new(tx: self, input_index: input_index, amount: amount)
293
297
  interpreter = Bitcoin::ScriptInterpreter.new(checker: checker, flags: flags)
294
298
  i = inputs[input_index]
data/lib/bitcoin/tx_in.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  module Bitcoin
2
5
 
3
6
  # transaction input
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  module Bitcoin
2
5
 
3
6
  # transaction output
data/lib/bitcoin/util.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  module Bitcoin
2
5
 
3
6
  # bitcoin utility.
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -54,12 +54,12 @@ module Bitcoin
54
54
 
55
55
  # derive receive key
56
56
  def derive_receive(address_index)
57
- derive_path(0, address_index)
57
+ derive_key(0, address_index)
58
58
  end
59
59
 
60
60
  # derive change key
61
61
  def derive_change(address_index)
62
- derive_path(1, address_index)
62
+ derive_key(1, address_index)
63
63
  end
64
64
 
65
65
  # save this account payload to database.
@@ -74,7 +74,7 @@ module Bitcoin
74
74
 
75
75
  # get the list of derived keys for change key.
76
76
  def derived_change_keys
77
- receive_depth.times.map{|i|derive_key(1,i)}
77
+ change_depth.times.map{|i|derive_key(1,i)}
78
78
  end
79
79
 
80
80
  private
@@ -9,6 +9,7 @@ module Bitcoin
9
9
  attr_reader :path
10
10
 
11
11
  DEFAULT_PATH_PREFIX = "#{Bitcoin.base_dir}/db/wallet/"
12
+ VERSION = 1
12
13
 
13
14
  # Create new wallet. If wallet already exist, throw error.
14
15
  # The wallet generates a seed using SecureRandom and store to db at initialization.
@@ -36,7 +37,15 @@ module Bitcoin
36
37
  # get wallets path
37
38
  # @return [Array] Array of paths for each wallet dir.
38
39
  def self.wallet_paths(path_prefix = DEFAULT_PATH_PREFIX)
39
- Dir.glob("#{path_prefix}wallet*/")
40
+ Dir.glob("#{path_prefix}wallet*/").sort
41
+ end
42
+
43
+ # get current wallet
44
+ def self.current_wallet(path_prefix = DEFAULT_PATH_PREFIX)
45
+ path = wallet_paths.first # TODO default wallet selection
46
+ return nil unless path
47
+ wallet_id = path.delete(path_prefix + '/wallet').delete('/').to_i
48
+ self.load(wallet_id, path_prefix)
40
49
  end
41
50
 
42
51
  # get account list based on BIP-44
@@ -55,6 +64,11 @@ module Bitcoin
55
64
  account
56
65
  end
57
66
 
67
+ # get wallet version.
68
+ def version
69
+ db.version
70
+ end
71
+
58
72
  # close database wallet
59
73
  def close
60
74
  db.close
@@ -5,7 +5,8 @@ module Bitcoin
5
5
 
6
6
  KEY_PREFIX = {
7
7
  account: 'a', # key: account index, value: Account raw data.
8
- master: 'm', # value : wallet seed.
8
+ master: 'm', # value: wallet seed.
9
+ version: 'v', # value: wallet version
9
10
  }
10
11
 
11
12
  attr_reader :level_db
@@ -44,6 +45,7 @@ module Bitcoin
44
45
  # @param [Bitcoin::Wallet::MasterKey] master a master key.
45
46
  def register_master_key(master)
46
47
  level_db.put(KEY_PREFIX[:master], master.to_payload)
48
+ level_db.put(KEY_PREFIX[:version], Bitcoin::Wallet::Base::VERSION.to_s)
47
49
  @master_key = master
48
50
  end
49
51
 
@@ -52,6 +54,11 @@ module Bitcoin
52
54
  !level_db.get(KEY_PREFIX[:master]).nil?
53
55
  end
54
56
 
57
+ # wallet version
58
+ def version
59
+ level_db.get(KEY_PREFIX[:version]).to_i
60
+ end
61
+
55
62
  end
56
63
  end
57
- end
64
+ end
data/lib/bitcoin.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # Porting part of the code from bitcoin-ruby. see the license.
2
+ # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
+
1
4
  require 'bitcoin/version'
2
5
  require 'eventmachine'
3
6
  require 'ecdsa'
@@ -39,6 +42,7 @@ module Bitcoin
39
42
  autoload :Store, 'bitcoin/store'
40
43
  autoload :RPC, 'bitcoin/rpc'
41
44
  autoload :Wallet, 'bitcoin/wallet'
45
+ autoload :BloomFilter, 'bitcoin/bloom_filter'
42
46
 
43
47
  require_relative 'bitcoin/constants'
44
48
 
@@ -58,12 +62,13 @@ module Bitcoin
58
62
  return @current_chain if @current_chain
59
63
  case @chain_param
60
64
  when :mainnet
61
- Bitcoin::ChainParams.mainnet
65
+ @current_chain = Bitcoin::ChainParams.mainnet
62
66
  when :testnet
63
- Bitcoin::ChainParams.testnet
67
+ @current_chain = Bitcoin::ChainParams.testnet
64
68
  when :regtest
65
- Bitcoin::ChainParams.regtest
69
+ @current_chain = Bitcoin::ChainParams.regtest
66
70
  end
71
+ @current_chain
67
72
  end
68
73
 
69
74
  # base dir path that store blockchain data and wallet data
@@ -77,6 +82,14 @@ module Bitcoin
77
82
  (path && File.exist?(path)) ? Bitcoin::Secp256k1::Native : Bitcoin::Secp256k1::Ruby
78
83
  end
79
84
 
85
+ def self.hmac_sha512(key, data)
86
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA512'), key, data)
87
+ end
88
+
89
+ def self.hmac_sha256(key, data)
90
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), key, data)
91
+ end
92
+
80
93
  class ::String
81
94
  # binary convert to hex string
82
95
  def bth
@@ -140,6 +153,7 @@ module Bitcoin
140
153
  end
141
154
 
142
155
  def to_h
156
+ return self if self.is_a?(String)
143
157
  instance_variables.inject({}) do |result, var|
144
158
  key = var.to_s
145
159
  key.slice!(0) if key.start_with?('@')
@@ -154,4 +168,10 @@ module Bitcoin
154
168
 
155
169
  end
156
170
 
171
+ class ::Integer
172
+ def to_even_length_hex
173
+ hex = to_s(16)
174
+ hex.rjust((hex.length / 2.0).ceil * 2, '0')
175
+ end
176
+ end
157
177
  end