bitcoinrb 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/README.md +31 -9
  4. data/bitcoinrb.conf.sample +0 -0
  5. data/bitcoinrb.gemspec +6 -2
  6. data/exe/bitcoinrb-cli +2 -2
  7. data/lib/bitcoin/block_header.rb +9 -2
  8. data/lib/bitcoin/chainparams/regtest.yml +1 -1
  9. data/lib/bitcoin/constants.rb +3 -0
  10. data/lib/bitcoin/key.rb +70 -5
  11. data/lib/bitcoin/logger.rb +25 -1
  12. data/lib/bitcoin/message/addr.rb +0 -39
  13. data/lib/bitcoin/message/headers.rb +1 -0
  14. data/lib/bitcoin/message/inventory.rb +4 -0
  15. data/lib/bitcoin/message/network_addr.rb +44 -0
  16. data/lib/bitcoin/message/version.rb +8 -3
  17. data/lib/bitcoin/message.rb +12 -0
  18. data/lib/bitcoin/mnemonic.rb +2 -2
  19. data/lib/bitcoin/network/connection.rb +14 -1
  20. data/lib/bitcoin/network/message_handler.rb +52 -19
  21. data/lib/bitcoin/network/peer.rb +142 -5
  22. data/lib/bitcoin/network/peer_discovery.rb +16 -8
  23. data/lib/bitcoin/network/pool.rb +39 -14
  24. data/lib/bitcoin/network.rb +0 -1
  25. data/lib/bitcoin/node/cli.rb +66 -0
  26. data/lib/bitcoin/node/configuration.rb +37 -0
  27. data/lib/bitcoin/node/spv.rb +13 -3
  28. data/lib/bitcoin/node.rb +2 -1
  29. data/lib/bitcoin/rpc/http_server.rb +56 -0
  30. data/lib/bitcoin/rpc/request_handler.rb +84 -0
  31. data/lib/bitcoin/rpc.rb +6 -0
  32. data/lib/bitcoin/script/multisig.rb +92 -0
  33. data/lib/bitcoin/script/script.rb +17 -7
  34. data/lib/bitcoin/script/script_interpreter.rb +2 -38
  35. data/lib/bitcoin/store/chain_entry.rb +64 -0
  36. data/lib/bitcoin/store/db/level_db.rb +101 -0
  37. data/lib/bitcoin/store/db.rb +9 -0
  38. data/lib/bitcoin/store/spv_chain.rb +96 -0
  39. data/lib/bitcoin/store.rb +5 -1
  40. data/lib/bitcoin/tx.rb +6 -2
  41. data/lib/bitcoin/version.rb +1 -1
  42. data/lib/bitcoin/wallet/account.rb +82 -0
  43. data/lib/bitcoin/wallet/base.rb +84 -0
  44. data/lib/bitcoin/wallet/db.rb +57 -0
  45. data/lib/bitcoin/wallet/master_key.rb +100 -0
  46. data/lib/bitcoin/wallet.rb +8 -0
  47. data/lib/bitcoin.rb +4 -0
  48. data/lib/openassets/payload.rb +6 -2
  49. metadata +70 -13
  50. data/lib/bitcoin/node/spv_block_chain.rb +0 -15
  51. data/lib/bitcoin/store/spv_block_store.rb +0 -11
@@ -0,0 +1,84 @@
1
+ require 'leveldb'
2
+ module Bitcoin
3
+ module Wallet
4
+
5
+ class Base
6
+
7
+ attr_accessor :wallet_id
8
+ attr_reader :db
9
+
10
+ DEFAULT_PATH_PREFIX = "#{Bitcoin.base_dir}/db/wallet"
11
+
12
+ # Create new wallet. If wallet already exist, throw error.
13
+ # The wallet generates a seed using SecureRandom and store to db at initialization.
14
+ # @param [String] wallet_id new wallet id.
15
+ # @param [String] path_prefix wallet file path prefix.
16
+ # @return [Bitcoin::Wallet::Base] the wallet
17
+ def self.create(wallet_id = 1, path_prefix = DEFAULT_PATH_PREFIX)
18
+ raise ArgumentError, "wallet_id : #{wallet_id} already exist." if self.exist?(wallet_id, path_prefix)
19
+ w = self.new(wallet_id, path_prefix)
20
+ # generate seed
21
+ raise RuntimeError, 'the seed already exist.' if w.db.registered_master?
22
+ master = Bitcoin::Wallet::MasterKey.generate
23
+ w.db.register_master_key(master)
24
+ w
25
+ end
26
+
27
+ # load wallet with specified +wallet_id+
28
+ # @return [Bitcoin::Wallet::Base] the wallet
29
+ def self.load(wallet_id, path_prefix = DEFAULT_PATH_PREFIX)
30
+ raise ArgumentError, "wallet_id : #{wallet_id} dose not exist." unless self.exist?(wallet_id, path_prefix)
31
+ self.new(wallet_id, path_prefix)
32
+ end
33
+
34
+ # get account list based on BIP-44
35
+ def accounts
36
+ db.accounts.map{|raw| Account.parse_from_payload(raw)}
37
+ end
38
+
39
+ def create_account(purpose = Account::PURPOSE_TYPE[:legacy], index = 0, name)
40
+ account = Account.new(purpose, index, name)
41
+ account.wallet = self
42
+ account.init
43
+ account
44
+ end
45
+
46
+ # close database wallet
47
+ def close
48
+ db.close
49
+ end
50
+
51
+ # get master key
52
+ # @return [Bitcoin::Wallet::MasterKey]
53
+ def master_key
54
+ db.master_key
55
+ end
56
+
57
+ # encrypt wallet
58
+ # @param [String] passphrase the wallet passphrase
59
+ def encrypt(passphrase)
60
+
61
+ end
62
+
63
+ # decrypt wallet
64
+ # @param [String] passphrase the wallet passphrase
65
+ def decrypt(passphrase)
66
+
67
+ end
68
+
69
+ private
70
+
71
+ def initialize(wallet_id, path_prefix)
72
+ @db = Bitcoin::Wallet::DB.new("#{path_prefix}_#{wallet_id}")
73
+ @wallet_id = wallet_id
74
+ end
75
+
76
+ def self.exist?(wallet_id, path_prefix)
77
+ path = "#{path_prefix}_#{wallet_id}"
78
+ Dir.exist?(path)
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,57 @@
1
+ module Bitcoin
2
+ module Wallet
3
+
4
+ class DB
5
+
6
+ KEY_PREFIX = {
7
+ account: 'a', # key: account index, value: Account raw data.
8
+ master: 'm', # value : wallet seed.
9
+ }
10
+
11
+ attr_reader :level_db
12
+ attr_accessor :master_key
13
+
14
+ def initialize(path = "#{Bitcoin.base_dir}/db/wallet")
15
+ FileUtils.mkdir_p(path)
16
+ @level_db = ::LevelDB::DB.new(path)
17
+ end
18
+
19
+ # close database
20
+ def close
21
+ level_db.close
22
+ end
23
+
24
+ # get accounts raw data.
25
+ def accounts
26
+ from = KEY_PREFIX[:account] + '00000000'
27
+ to = KEY_PREFIX[:account] + 'ffffffff'
28
+ level_db.each(from: from, to: to).map { |k, v| v}
29
+ end
30
+
31
+ def save_account(account)
32
+ level_db.batch do
33
+ key = KEY_PREFIX[:account] + account.index.to_s(16).rjust(8, '0')
34
+ level_db.put(key, account.to_payload)
35
+ end
36
+ end
37
+
38
+ # get master_key
39
+ def master_key
40
+ @master_key ||= Bitcoin::Wallet::MasterKey.parse_from_payload(level_db.get(KEY_PREFIX[:master]))
41
+ end
42
+
43
+ # save seed
44
+ # @param [Bitcoin::Wallet::MasterKey] master a master key.
45
+ def register_master_key(master)
46
+ level_db.put(KEY_PREFIX[:master], master.to_payload)
47
+ @master_key = master
48
+ end
49
+
50
+ # whether master key registered.
51
+ def registered_master?
52
+ !level_db.get(KEY_PREFIX[:master]).nil?
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,100 @@
1
+ module Bitcoin
2
+ module Wallet
3
+
4
+ # HD Wallet master seed
5
+ class MasterKey
6
+ extend Bitcoin::Util
7
+ include Bitcoin::Util
8
+
9
+ attr_reader :seed
10
+ attr_accessor :salt
11
+ attr_accessor :encrypted
12
+ attr_accessor :mnemonic # ephemeral data existing only at initialization
13
+
14
+ def initialize(seed, salt: '', encrypted: false, mnemonic: nil)
15
+ @mnemonic = mnemonic
16
+ @seed = seed
17
+ @encrypted = encrypted
18
+ @salt = salt
19
+ end
20
+
21
+ # generate new master key.
22
+ # @return Bitcoin::Wallet::MasterKey
23
+ def self.generate
24
+ entropy = SecureRandom.hex(16)
25
+ mnemonic = Bitcoin::Mnemonic.new('english')
26
+ self.recover_from_words(mnemonic.to_mnemonic(entropy))
27
+ end
28
+
29
+ # recover master key from mnemonic word list.
30
+ # @param [Array] words the mnemonic word list.
31
+ # @return Bitcoin::Wallet::MasterKey
32
+ def self.recover_from_words(words)
33
+ mnemonic = Bitcoin::Mnemonic.new('english')
34
+ seed = mnemonic.to_seed(words)
35
+ self.new(seed, mnemonic: words)
36
+ end
37
+
38
+ # parse master key raw data
39
+ # @param [String] payload raw data
40
+ # @return [Bitcoin::Wallet::MasterKey]
41
+ def self.parse_from_payload(payload)
42
+ flag, payload = unpack_var_int(payload)
43
+ raise 'encrypted flag is invalid.' unless [0, 1].include?(flag)
44
+ salt, payload = unpack_var_string(payload)
45
+ seed, payload = unpack_var_string(payload)
46
+ self.new(seed.bth, salt: salt.bth, encrypted: flag == 1)
47
+ end
48
+
49
+ # generate payload with following format
50
+ # [encrypted(false:0, true:1)][salt(var str)][seed(var str)]
51
+ def to_payload
52
+ flg = encrypted ? 1 : 0
53
+ pack_var_int(flg) << [salt, seed].map{|v|pack_var_string(v.htb)}.join
54
+ end
55
+
56
+ # get master key
57
+ # @return [Bitcoin::ExtKey] the master key
58
+ def key
59
+ raise 'seed is encrypted. please decrypt the seed.' if encrypted
60
+ Bitcoin::ExtKey.generate_master(seed)
61
+ end
62
+
63
+ # encrypt seed
64
+ def encrypt(passphrase)
65
+ raise 'seed already encrypted.' if encrypted
66
+ @salt = SecureRandom.hex(16)
67
+ enc = OpenSSL::Cipher.new('AES-256-CBC')
68
+ enc.encrypt
69
+ enc.key, enc.iv = key_iv(enc, passphrase)
70
+ encrypted_data = ''
71
+ encrypted_data << enc.update(seed)
72
+ encrypted_data << enc.final
73
+ @seed = encrypted_data
74
+ @encrypted = true
75
+ end
76
+
77
+ # decrypt seed
78
+ def decrypt(passphrase)
79
+ raise 'seed is not encrypted.' unless encrypted
80
+ dec = OpenSSL::Cipher.new('AES-256-CBC')
81
+ dec.decrypt
82
+ dec.key, dec.iv = key_iv(dec, passphrase)
83
+ decrypted_data = ''
84
+ decrypted_data << dec.update(seed)
85
+ decrypted_data << dec.final
86
+ @seed = decrypted_data
87
+ @encrypted = false
88
+ @salt = ''
89
+ end
90
+
91
+ private
92
+
93
+ def key_iv(enc, passphrase)
94
+ key_iv = OpenSSL::PKCS5.pbkdf2_hmac_sha1(passphrase, salt, 2000, enc.key_len + enc.iv_len)
95
+ [key_iv[0, enc.key_len], key_iv[enc.key_len, enc.iv_len]]
96
+ end
97
+
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,8 @@
1
+ module Bitcoin
2
+ module Wallet
3
+ autoload :Base, 'bitcoin/wallet/base'
4
+ autoload :Account, 'bitcoin/wallet/account'
5
+ autoload :DB, 'bitcoin/wallet/db'
6
+ autoload :MasterKey, 'bitcoin/wallet/master_key'
7
+ end
8
+ end
data/lib/bitcoin.rb CHANGED
@@ -5,6 +5,7 @@ require 'securerandom'
5
5
  require 'json'
6
6
  require 'bech32'
7
7
  require 'ffi'
8
+ require 'tmpdir'
8
9
  require_relative 'openassets'
9
10
 
10
11
  module Bitcoin
@@ -17,6 +18,7 @@ module Bitcoin
17
18
  autoload :BlockHeader, 'bitcoin/block_header'
18
19
  autoload :Tx, 'bitcoin/tx'
19
20
  autoload :Script, 'bitcoin/script/script'
21
+ autoload :Multisig, 'bitcoin/script/multisig'
20
22
  autoload :ScriptInterpreter, 'bitcoin/script/script_interpreter'
21
23
  autoload :ScriptError, 'bitcoin/script/script_error'
22
24
  autoload :TxChecker, 'bitcoin/script/tx_checker'
@@ -35,6 +37,8 @@ module Bitcoin
35
37
  autoload :ValidationState, 'bitcoin/validation'
36
38
  autoload :Network, 'bitcoin/network'
37
39
  autoload :Store, 'bitcoin/store'
40
+ autoload :RPC, 'bitcoin/rpc'
41
+ autoload :Wallet, 'bitcoin/wallet'
38
42
 
39
43
  require_relative 'bitcoin/constants'
40
44
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module OpenAssets
2
4
 
3
5
  MARKER = "\x4f\x41"
@@ -34,9 +36,11 @@ module OpenAssets
34
36
 
35
37
  # generate binary payload
36
38
  def to_payload
37
- payload = MARKER << VERSION
39
+ payload = String.new
40
+ payload << MARKER
41
+ payload << VERSION
38
42
  payload << Bitcoin.pack_var_int(quantities.size) << quantities.map{|q| LEB128.encode_unsigned(q).read }.join
39
- payload << Bitcoin.pack_var_int(metadata.length) << metadata.bytes.map{|b|b.to_s(16)}.join.htb
43
+ payload << Bitcoin.pack_var_int(metadata.length) << metadata.bytes.map{|b| sprintf("%02x", b)}.join.htb
40
44
  payload
41
45
  end
42
46
 
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.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-03 00:00:00.000000000 Z
11
+ date: 2017-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.0.1
61
+ version: 1.0.3
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.1
68
+ version: 1.0.3
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: daemon-spawn
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +109,21 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: parallel
112
+ name: leb128
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.0.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.0.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: leveldb-ruby
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -123,19 +137,47 @@ dependencies:
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
- name: leb128
140
+ name: eventmachine_httpserver
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - "~>"
143
+ - - ">="
130
144
  - !ruby/object:Gem::Version
131
- version: 1.0.0
145
+ version: '0'
132
146
  type: :runtime
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - "~>"
150
+ - - ">="
137
151
  - !ruby/object:Gem::Version
138
- version: 1.0.0
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rest-client
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: iniparse
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
139
181
  - !ruby/object:Gem::Dependency
140
182
  name: bundler
141
183
  requirement: !ruby/object:Gem::Requirement
@@ -213,6 +255,7 @@ files:
213
255
  - Rakefile
214
256
  - bin/console
215
257
  - bin/setup
258
+ - bitcoinrb.conf.sample
216
259
  - bitcoinrb.gemspec
217
260
  - exe/bitcoinrb-cli
218
261
  - exe/bitcoinrbd
@@ -249,6 +292,7 @@ files:
249
292
  - lib/bitcoin/message/inventory.rb
250
293
  - lib/bitcoin/message/mem_pool.rb
251
294
  - lib/bitcoin/message/merkle_block.rb
295
+ - lib/bitcoin/message/network_addr.rb
252
296
  - lib/bitcoin/message/not_found.rb
253
297
  - lib/bitcoin/message/ping.rb
254
298
  - lib/bitcoin/message/pong.rb
@@ -273,10 +317,15 @@ files:
273
317
  - lib/bitcoin/network/peer_discovery.rb
274
318
  - lib/bitcoin/network/pool.rb
275
319
  - lib/bitcoin/node.rb
320
+ - lib/bitcoin/node/cli.rb
321
+ - lib/bitcoin/node/configuration.rb
276
322
  - lib/bitcoin/node/spv.rb
277
- - lib/bitcoin/node/spv_block_chain.rb
278
323
  - lib/bitcoin/opcodes.rb
279
324
  - lib/bitcoin/out_point.rb
325
+ - lib/bitcoin/rpc.rb
326
+ - lib/bitcoin/rpc/http_server.rb
327
+ - lib/bitcoin/rpc/request_handler.rb
328
+ - lib/bitcoin/script/multisig.rb
280
329
  - lib/bitcoin/script/script.rb
281
330
  - lib/bitcoin/script/script_error.rb
282
331
  - lib/bitcoin/script/script_interpreter.rb
@@ -286,13 +335,21 @@ files:
286
335
  - lib/bitcoin/secp256k1/native.rb
287
336
  - lib/bitcoin/secp256k1/ruby.rb
288
337
  - lib/bitcoin/store.rb
289
- - lib/bitcoin/store/spv_block_store.rb
338
+ - lib/bitcoin/store/chain_entry.rb
339
+ - lib/bitcoin/store/db.rb
340
+ - lib/bitcoin/store/db/level_db.rb
341
+ - lib/bitcoin/store/spv_chain.rb
290
342
  - lib/bitcoin/tx.rb
291
343
  - lib/bitcoin/tx_in.rb
292
344
  - lib/bitcoin/tx_out.rb
293
345
  - lib/bitcoin/util.rb
294
346
  - lib/bitcoin/validation.rb
295
347
  - lib/bitcoin/version.rb
348
+ - lib/bitcoin/wallet.rb
349
+ - lib/bitcoin/wallet/account.rb
350
+ - lib/bitcoin/wallet/base.rb
351
+ - lib/bitcoin/wallet/db.rb
352
+ - lib/bitcoin/wallet/master_key.rb
296
353
  - lib/openassets.rb
297
354
  - lib/openassets/marker_output.rb
298
355
  - lib/openassets/payload.rb
@@ -316,7 +373,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
316
373
  version: '0'
317
374
  requirements: []
318
375
  rubyforge_project:
319
- rubygems_version: 2.6.13
376
+ rubygems_version: 2.6.12
320
377
  signing_key:
321
378
  specification_version: 4
322
379
  summary: "[WIP]The implementation of Bitcoin Protocol for Ruby."
@@ -1,15 +0,0 @@
1
- module Bitcoin
2
- module Node
3
-
4
- class SPVBlockChain
5
-
6
- attr_reader :block_store
7
-
8
- def initialize(block_store = Bitcoin::Store::SPVBlockStore.new)
9
- @block_store = block_store
10
- end
11
-
12
- end
13
-
14
- end
15
- end
@@ -1,11 +0,0 @@
1
- module Bitcoin
2
-
3
- module Store
4
-
5
- class SPVBlockStore
6
-
7
- end
8
-
9
- end
10
-
11
- end