bitcoinrb 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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