bitcoinrb 0.4.0 → 0.5.0

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -0
  4. data/README.md +3 -2
  5. data/bitcoinrb.gemspec +2 -2
  6. data/exe/bitcoinrbd +5 -0
  7. data/lib/bitcoin.rb +10 -1
  8. data/lib/bitcoin/bip85_entropy.rb +111 -0
  9. data/lib/bitcoin/block_header.rb +2 -0
  10. data/lib/bitcoin/ext.rb +5 -0
  11. data/lib/bitcoin/ext/json_parser.rb +46 -0
  12. data/lib/bitcoin/ext_key.rb +7 -3
  13. data/lib/bitcoin/key_path.rb +12 -5
  14. data/lib/bitcoin/message.rb +7 -0
  15. data/lib/bitcoin/message/base.rb +1 -0
  16. data/lib/bitcoin/message/cf_parser.rb +16 -0
  17. data/lib/bitcoin/message/cfcheckpt.rb +36 -0
  18. data/lib/bitcoin/message/cfheaders.rb +40 -0
  19. data/lib/bitcoin/message/cfilter.rb +35 -0
  20. data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
  21. data/lib/bitcoin/message/get_cfheaders.rb +24 -0
  22. data/lib/bitcoin/message/get_cfilters.rb +25 -0
  23. data/lib/bitcoin/message/version.rb +7 -0
  24. data/lib/bitcoin/mnemonic.rb +5 -5
  25. data/lib/bitcoin/network/peer.rb +9 -4
  26. data/lib/bitcoin/network/peer_discovery.rb +3 -1
  27. data/lib/bitcoin/node/cli.rb +14 -10
  28. data/lib/bitcoin/node/spv.rb +1 -1
  29. data/lib/bitcoin/out_point.rb +2 -0
  30. data/lib/bitcoin/payment_code.rb +92 -0
  31. data/lib/bitcoin/psbt/input.rb +5 -14
  32. data/lib/bitcoin/psbt/tx.rb +7 -12
  33. data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
  34. data/lib/bitcoin/rpc/request_handler.rb +1 -1
  35. data/lib/bitcoin/script/script.rb +5 -9
  36. data/lib/bitcoin/script/script_interpreter.rb +2 -3
  37. data/lib/bitcoin/secp256k1.rb +1 -0
  38. data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
  39. data/lib/bitcoin/secp256k1/ruby.rb +4 -35
  40. data/lib/bitcoin/store.rb +1 -0
  41. data/lib/bitcoin/store/chain_entry.rb +1 -0
  42. data/lib/bitcoin/store/utxo_db.rb +226 -0
  43. data/lib/bitcoin/tx.rb +3 -7
  44. data/lib/bitcoin/tx_in.rb +3 -4
  45. data/lib/bitcoin/util.rb +7 -0
  46. data/lib/bitcoin/version.rb +1 -1
  47. data/lib/bitcoin/wallet.rb +1 -0
  48. data/lib/bitcoin/wallet/account.rb +1 -0
  49. data/lib/bitcoin/wallet/base.rb +2 -2
  50. data/lib/bitcoin/wallet/master_key.rb +1 -0
  51. data/lib/bitcoin/wallet/utxo.rb +37 -0
  52. metadata +34 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34ec1d9b1b20094213a29e9df2a47b901bdef1d537323446230b6ff2f3420a50
4
- data.tar.gz: 5591f08d5909d1b18b60b091a41e4f6638d424ea0512dbd270001d8e9555e633
3
+ metadata.gz: a5da63d0663778eba1816d008ebf369348bc75d214965b68c2ff7ce564e95ac3
4
+ data.tar.gz: 2a7e31c9f5b29b72b14e93103f1b69111a2283e390fb61fdad517a5aa764f9f4
5
5
  SHA512:
6
- metadata.gz: 8e1a4df59cb46fa6d35dab5dc3d83dac58aaf6b8f39ae2ccc782c10ce06821000a1e31732887b0019b2ac62f0f4d7e831d71a0a9ad8964868b24c168862b963a
7
- data.tar.gz: 9f374d157b4c8814c771697365ffded665a17b36579130604fd8c85375e05fbaa41f8212006ef037f5d2442af67a6130a1d5400d5019b9685404b433de1dff77
6
+ metadata.gz: b062f7c6e944cac7787fdca7be284224961b9a675467bd6f55fa0d49ed502157507e098aee9b28998f600826d677ddc3f8b26947134591240b5d8ada5e004686
7
+ data.tar.gz: b32d9705400ac5bfb1cc252e4a79c66ff9fb2d8dcba1f530060b5c130e14726e412a5434d5769dd6824588374cbae99c797ae65a2478a92d53cc5fa664ab5a4e
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.7.0
@@ -3,6 +3,7 @@ rvm:
3
3
  - 2.4.6
4
4
  - 2.5.5
5
5
  - 2.6.3
6
+ - 2.7.0
6
7
  addons:
7
8
  apt:
8
9
  packages:
data/README.md CHANGED
@@ -17,6 +17,7 @@ Bitcoinrb supports following feature:
17
17
  * Segwit support (parsing segwit payload, Bech32 address, sign for segwit tx, [BIP-141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki), [BIP-143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki), [BIP-144](https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki))
18
18
  * [BIP-173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) Bech32 address support
19
19
  * [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) PSBT(Partially Signed Bitcoin Transaction) support
20
+ * [BIP-85](https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki) Deterministic Entropy From BIP32 Keychains support by `Bitcoin::BIP85Entropy` class.
20
21
  * [WIP] SPV node
21
22
  * [WIP] 0ff-chain protocol
22
23
 
@@ -38,8 +39,8 @@ If you use node features, please install level DB as follows.
38
39
 
39
40
  and put `leveldb-native` in your Gemfile and run bundle install.
40
41
 
41
- ```
42
- gem leveldb-native
42
+ ```ruby
43
+ gem 'leveldb-native'
43
44
  ```
44
45
 
45
46
  ## Installation
@@ -29,17 +29,17 @@ Gem::Specification.new do |spec|
29
29
  spec.add_runtime_dependency 'ffi'
30
30
  spec.add_runtime_dependency 'leb128', '~> 1.0.0'
31
31
  spec.add_runtime_dependency 'eventmachine_httpserver'
32
- spec.add_runtime_dependency 'rest-client'
33
32
  spec.add_runtime_dependency 'iniparse'
34
33
  spec.add_runtime_dependency 'siphash'
35
34
  spec.add_runtime_dependency 'protobuf', '3.8.5'
36
35
  spec.add_runtime_dependency 'scrypt'
36
+ spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
37
37
 
38
38
  # for options
39
39
  spec.add_development_dependency 'leveldb-native'
40
40
 
41
41
  spec.add_development_dependency 'bundler'
42
- spec.add_development_dependency 'rake', '~> 10.0'
42
+ spec.add_development_dependency 'rake', '>= 12.3.3'
43
43
  spec.add_development_dependency 'rspec', '~> 3.0'
44
44
  spec.add_development_dependency 'timecop'
45
45
  spec.add_development_dependency 'webmock', '~> 3.0'
@@ -12,6 +12,11 @@ class BitcoinDaemon < DaemonSpawn::Base
12
12
  node.run
13
13
  end
14
14
 
15
+ def stop
16
+ puts "Stopping Bitcoinrb daemon : #{Time.now}"
17
+ node.shutdown
18
+ end
19
+
15
20
  end
16
21
 
17
22
  class Bitcoinrbd < Thor
@@ -14,6 +14,7 @@ require_relative 'openassets'
14
14
 
15
15
  module Bitcoin
16
16
 
17
+ autoload :Ext, 'bitcoin/ext'
17
18
  autoload :Util, 'bitcoin/util'
18
19
  autoload :ChainParams, 'bitcoin/chain_params'
19
20
  autoload :Message, 'bitcoin/message'
@@ -55,6 +56,8 @@ module Bitcoin
55
56
  autoload :Descriptor, 'bitcoin/descriptor'
56
57
  autoload :SLIP39, 'bitcoin/slip39'
57
58
  autoload :Aezeed, 'bitcoin/aezeed'
59
+ autoload :PaymentCode, 'bitcoin/payment_code'
60
+ autoload :BIP85Entropy, 'bitcoin/bip85_entropy'
58
61
 
59
62
  require_relative 'bitcoin/constants'
60
63
 
@@ -189,7 +192,7 @@ module Bitcoin
189
192
  if value.is_a?(Array)
190
193
  result.update(key => value.map{|v|v.to_h})
191
194
  else
192
- result.update(key => value)
195
+ result.update(key => value.class.to_s.start_with?("Bitcoin::") ? value.to_h : value)
193
196
  end
194
197
  end
195
198
  end
@@ -223,4 +226,10 @@ module Bitcoin
223
226
  end
224
227
  end
225
228
 
229
+ class ::ECDSA::Point
230
+ def to_hex(compression = true)
231
+ ECDSA::Format::PointOctetString.encode(self, compression: compression).bth
232
+ end
233
+ end
234
+
226
235
  end
@@ -0,0 +1,111 @@
1
+ module Bitcoin
2
+
3
+ # Deterministic Entropy From BIP32 Keychains
4
+ # https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki
5
+ class BIP85Entropy
6
+
7
+ BIP85_PATH = 83696968 + HARDENED_THRESHOLD
8
+
9
+ include Bitcoin::KeyPath
10
+
11
+ attr_reader :root_key #hex format
12
+
13
+ # Import root key.
14
+ # @param [String] base58 master bip32 root key.
15
+ # @return [Bitcoin::BIP85Entropy]
16
+ def self.from_base58(base58)
17
+ key = Bitcoin::ExtKey.from_base58(base58)
18
+ self.new(key)
19
+ end
20
+
21
+ # derive entropy
22
+ # @param [String] path derive path.
23
+ # @return [Tuple(String, Object)] a tuple of entropy with hex format and results depending the application.
24
+ def derive(path)
25
+ raise ArgumentError, "Invalid BIP85 path format." unless path.start_with?("m/83696968'")
26
+ derived_key = root_key
27
+ parse_key_path(path).each{|num| derived_key = derived_key.derive(num)}
28
+ derived_key = derived_key.priv
29
+ entropy = Bitcoin.hmac_sha512("bip-entropy-from-k", derived_key.htb).bth
30
+ app_no = path.split('/')[2]
31
+ case app_no
32
+ when "39'"
33
+ bip39_entropy(path, entropy)
34
+ when "2'"
35
+ hd_seed_entropy(entropy)
36
+ when "32'"
37
+ xprv_entropy(entropy)
38
+ else
39
+ [entropy, entropy]
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def initialize(root_key)
46
+ @root_key = root_key
47
+ end
48
+
49
+ # derive BIP39 entropy.
50
+ def bip39_entropy(path, entropy)
51
+ params = path.split('/')
52
+ word_len = params[4]
53
+ language = code_to_language(params[3])
54
+ entropy = case word_len
55
+ when "12'"
56
+ entropy[0...32]
57
+ when "18'"
58
+ entropy[0...48]
59
+ when "24'"
60
+ entropy[0...64]
61
+ else
62
+ raise ArgumentError, "Word length #{word_len} does not supported."
63
+ end
64
+ mnemonic = Bitcoin::Mnemonic.new(language)
65
+ [entropy, mnemonic.to_mnemonic(entropy)]
66
+ end
67
+
68
+ # derive HD-Seed WIF entropy.
69
+ def hd_seed_entropy(entropy)
70
+ result = entropy[0...64]
71
+ [result, Bitcoin::Key.new(priv_key: result).to_wif]
72
+ end
73
+
74
+ # derive xprv entropy
75
+ def xprv_entropy(entropy)
76
+ chaincode = entropy[0...64]
77
+ private_key = Bitcoin::Key.new(priv_key: entropy[64..-1])
78
+ ext_key = Bitcoin::ExtKey.new
79
+ ext_key.key = private_key
80
+ ext_key.chain_code = chaincode.htb
81
+ ext_key.depth = 0
82
+ ext_key.number = 0
83
+ ext_key.parent_fingerprint = Bitcoin::ExtKey::MASTER_FINGERPRINT
84
+ [entropy, ext_key.to_base58]
85
+ end
86
+
87
+ # convert language code to language string.
88
+ def code_to_language(code)
89
+ case code
90
+ when "0'"
91
+ "english"
92
+ when "1'"
93
+ "japanese"
94
+ when "3'"
95
+ "spanish"
96
+ when "4'"
97
+ "chinese_simplified"
98
+ when "5'"
99
+ "chinese_traditional"
100
+ when "6'"
101
+ "french"
102
+ when "7'"
103
+ "italian"
104
+ else
105
+ raise ArgumentError, "bitcoinrb does not support language: #{code}"
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -3,6 +3,8 @@ module Bitcoin
3
3
  # Block Header
4
4
  class BlockHeader
5
5
 
6
+ include Bitcoin::HexConverter
7
+
6
8
  attr_accessor :version
7
9
  attr_accessor :prev_hash
8
10
  attr_accessor :merkle_root
@@ -0,0 +1,5 @@
1
+ module Bitcoin
2
+ module Ext
3
+ autoload :JsonParser, 'bitcoin/ext/json_parser'
4
+ end
5
+ end
@@ -0,0 +1,46 @@
1
+ require 'json/pure'
2
+
3
+ module Bitcoin
4
+ module Ext
5
+ # Extension of JSON::Pure::Parser.
6
+ # This class convert Float value to String value.
7
+ class JsonParser < JSON::Pure::Parser
8
+
9
+ def parse_value
10
+ case
11
+ when scan(FLOAT)
12
+ self[1].to_s
13
+ when scan(INTEGER)
14
+ Integer(self[1])
15
+ when scan(TRUE)
16
+ true
17
+ when scan(FALSE)
18
+ false
19
+ when scan(NULL)
20
+ nil
21
+ when !UNPARSED.equal?(string = parse_string)
22
+ string
23
+ when scan(ARRAY_OPEN)
24
+ @current_nesting += 1
25
+ ary = parse_array
26
+ @current_nesting -= 1
27
+ ary
28
+ when scan(OBJECT_OPEN)
29
+ @current_nesting += 1
30
+ obj = parse_object
31
+ @current_nesting -= 1
32
+ obj
33
+ when @allow_nan && scan(NAN)
34
+ NaN
35
+ when @allow_nan && scan(INFINITY)
36
+ Infinity
37
+ when @allow_nan && scan(MINUS_INFINITY)
38
+ MinusInfinity
39
+ else
40
+ UNPARSED
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -6,6 +6,8 @@ module Bitcoin
6
6
  # BIP32 Extended private key
7
7
  class ExtKey
8
8
 
9
+ include Bitcoin::HexConverter
10
+
9
11
  MAX_DEPTH = 255
10
12
  MASTER_FINGERPRINT = '00000000'
11
13
 
@@ -50,7 +52,7 @@ module Bitcoin
50
52
 
51
53
  # Base58 encoded extended private key
52
54
  def to_base58
53
- h = to_payload.bth
55
+ h = to_hex
54
56
  hex = h + Bitcoin.calc_checksum(h)
55
57
  Base58.encode(hex)
56
58
  end
@@ -198,6 +200,8 @@ module Bitcoin
198
200
  # BIP-32 Extended public key
199
201
  class ExtPubkey
200
202
 
203
+ include Bitcoin::HexConverter
204
+
201
205
  attr_accessor :ver
202
206
  attr_accessor :depth
203
207
  attr_accessor :number
@@ -249,7 +253,7 @@ module Bitcoin
249
253
 
250
254
  # Base58 encoded extended pubkey
251
255
  def to_base58
252
- h = to_payload.bth
256
+ h = to_hex
253
257
  hex = h + Bitcoin.calc_checksum(h)
254
258
  Base58.encode(hex)
255
259
  end
@@ -273,7 +277,7 @@ module Bitcoin
273
277
  raise 'invalid key' if left >= CURVE_ORDER
274
278
  p1 = Bitcoin::Secp256k1::GROUP.generator.multiply_by_scalar(left)
275
279
  p2 = Bitcoin::Key.new(pubkey: pubkey, key_type: key_type).to_point
276
- new_key.pubkey = ECDSA::Format::PointOctetString.encode(p1 + p2, compression: true).bth
280
+ new_key.pubkey = (p1 + p2).to_hex
277
281
  new_key.chain_code = l[32..-1]
278
282
  new_key.ver = version
279
283
  new_key
@@ -4,14 +4,21 @@ module Bitcoin
4
4
  # key path convert an array of derive number
5
5
  # @param [String] path_string
6
6
  # @return [Array[Integer]] key path numbers.
7
+ # @raise [ArgumentError] if invalid +path_string+.
7
8
  def parse_key_path(path_string)
8
- path_string.split('/').map.with_index do|p, index|
9
+ paths = path_string.split('/')
10
+ raise ArgumentError, "Invalid path." if path_string.include?(" ")
11
+ raise ArgumentError, "Invalid path." unless path_string.count("/") <= paths.size
12
+ paths.map.with_index do|p, index|
9
13
  if index == 0
10
- raise ArgumentError.new("#{path_string} is invalid format.") unless p == 'm'
11
- next
14
+ next if p == 'm'
15
+ raise ArgumentError, "Invalid path." unless p == 'm'
12
16
  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)
17
+ raise ArgumentError, "Invalid path." if p.count("'") > 1 || (p.count("'") == 1 && p[-1] != "'")
18
+ raise ArgumentError, "Invalid path." unless p.delete("'") =~ /^[0-9]+$/
19
+ value = (p[-1] == "'" ? p.delete("'").to_i + Bitcoin::HARDENED_THRESHOLD : p.to_i)
20
+ raise ArgumentError, "Invalid path." if value > 4294967295 # 4294967295 = 0xFFFFFFFF (uint32 max)
21
+ value
15
22
  end[1..-1]
16
23
  end
17
24
 
@@ -37,6 +37,13 @@ module Bitcoin
37
37
  autoload :BlockTransactionRequest, 'bitcoin/message/block_transaction_request'
38
38
  autoload :BlockTxn, 'bitcoin/message/block_txn'
39
39
  autoload :BlockTransactions, 'bitcoin/message/block_transactions'
40
+ autoload :GetCFilters, 'bitcoin/message/get_cfilters'
41
+ autoload :GetCFHeaders, 'bitcoin/message/get_cfheaders'
42
+ autoload :CFParser, 'bitcoin/message/cf_parser'
43
+ autoload :GetCFCheckpt, 'bitcoin/message/get_cfcheckpt'
44
+ autoload :CFCheckpt, 'bitcoin/message/cfcheckpt'
45
+ autoload :CFilter, 'bitcoin/message/cfilter'
46
+ autoload :CFHeaders, 'bitcoin/message/cfheaders'
40
47
 
41
48
  USER_AGENT = "/bitcoinrb:#{Bitcoin::VERSION}/"
42
49
 
@@ -3,6 +3,7 @@ module Bitcoin
3
3
 
4
4
  # Base message class
5
5
  class Base
6
+ include Bitcoin::HexConverter
6
7
  include Bitcoin::Util
7
8
  extend Bitcoin::Util
8
9
 
@@ -0,0 +1,16 @@
1
+ module Bitcoin
2
+ module Message
3
+ module CFParser
4
+
5
+ def parse_from_payload(payload)
6
+ type, start, hash = payload.unpack('CLH*')
7
+ self.new(type, start, hash)
8
+ end
9
+
10
+ def to_payload
11
+ [filter_type, start_height, stop_hash].pack('CLH*')
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # cfcheckpt message for BIP-157
5
+ # https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki#cfcheckpt
6
+ class CFCheckpt < Base
7
+
8
+ COMMAND = 'cfcheckpt'
9
+
10
+ attr_accessor :filter_type
11
+ attr_accessor :stop_hash # little endian
12
+ attr_accessor :filter_headers # little endian
13
+
14
+ def initialize(filter_type, stop_hash, filter_headers)
15
+ @filter_type = filter_type
16
+ @stop_hash = stop_hash
17
+ @filter_headers = filter_headers
18
+ end
19
+
20
+ def self.parse_from_payload(payload)
21
+ buf = StringIO.new(payload)
22
+ type = buf.read(1).unpack('C').first
23
+ hash = buf.read(32).unpack('H*').first
24
+ count = Bitcoin.unpack_var_int_from_io(buf)
25
+ headers = count.times.map{buf.read(32).bth}
26
+ self.new(type, hash, headers)
27
+ end
28
+
29
+ def to_payload
30
+ [filter_type, stop_hash].pack('CH*') +
31
+ Bitcoin.pack_var_int(filter_headers.size) + filter_headers.map(&:htb).join
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ module Bitcoin
2
+ module Message
3
+
4
+ # cfheaders message for BIP-157
5
+ # https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki#cfheaders
6
+ class CFHeaders < Base
7
+
8
+ COMMAND = 'cfheaders'
9
+
10
+ attr_accessor :filter_type
11
+ attr_accessor :stop_hash # little endian
12
+ attr_accessor :prev_filter_header # little endian
13
+ attr_accessor :filter_hashes # little endian
14
+
15
+ def initialize(filter_type, stop_hash, prev_filter_header, filter_hashes)
16
+ @filter_type = filter_type
17
+ @stop_hash = stop_hash
18
+ @prev_filter_header = prev_filter_header
19
+ @filter_hashes = filter_hashes
20
+ end
21
+
22
+ def self.parse_from_payload(payload)
23
+ buf = StringIO.new(payload)
24
+ type = buf.read(1).unpack("C").first
25
+ hash = buf.read(32).bth
26
+ header = buf.read(32).bth
27
+ count = Bitcoin.unpack_var_int_from_io(buf)
28
+ hashes = count.times.map{buf.read(32).bth}
29
+ self.new(type, hash, header, hashes)
30
+ end
31
+
32
+ def to_payload
33
+ [filter_type].pack('C') + stop_hash.htb + prev_filter_header.htb +
34
+ Bitcoin.pack_var_int(filter_hashes.size) + filter_hashes.map(&:htb).join
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end