bitcoinrb 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f552597dfd50a5df7cee6e6c29c10789afbbe5e78ad316af9833f25e99f15e76
4
- data.tar.gz: 9044c1daafb7c4badee574cdd326a922354b64376b9b871da3b2f5ad6a8aa4e6
3
+ metadata.gz: 895222030c0d0673f1524e112cf42d8cbb8e248558ba356e156a92b7482d4344
4
+ data.tar.gz: cca9f6a8777925fcf39c47a835549b1894554e2d3c67400ba330487731677a89
5
5
  SHA512:
6
- metadata.gz: '09d270868f51f4a08af65dbedce68d87d1863bb8f82dbf32c8999eb40bad432910325d5d17efc7c8d23f2f0398614cca8d4852dd3a52bd232ab0576bb0e7ee61'
7
- data.tar.gz: 0b958912e698973c4e1b4af4f9737a046b3d07566064bdd759ec7fa4c336a33861ec060a90f7bc9181c5a8a7f6fe91240359232ee0cefbcfc799580f59e861e5
6
+ metadata.gz: cbb089fdfa71ac6a9c60591789c18b78af3fac81e6ba82ac264a50cd1d464b814f8989ca62fdde78910640e241f2b7e8c516b20b9e67eb979efae86c43b9b0ab
7
+ data.tar.gz: bf0b6f27f6e4819569f3d814194b202f18d829bf315591ce8fa75db0846b2ef372ca45294537918ff7d161b37947f91be05562200d7028bc39b598ec83f01b26
@@ -1 +1 @@
1
- 2.5.3
1
+ 2.6.0
@@ -3,6 +3,7 @@ rvm:
3
3
  - 2.3.8
4
4
  - 2.4.5
5
5
  - 2.5.3
6
+ - 2.6.0
6
7
  addons:
7
8
  apt:
8
9
  packages:
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017-2018 HAW International, Inc. / chaintope, Inc.
3
+ Copyright (c) 2017-2019 HAW International, Inc. / chaintope, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -13,7 +13,9 @@ Bitcoinrb supports following feature:
13
13
  * De/serialization of Bitcoin protocol network messages
14
14
  * De/serialization of blocks and transactions
15
15
  * Key generation and verification for ECDSA, including [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) supports.
16
+ * ECDSA signature(RFC6979 -Deterministic ECDSA, LOW-S, LOW-R support)
16
17
  * Segwit support (parsing segwit payload, Bech32 address, sign for segwit tx, etc..)
18
+ * PSBT(Partially Signed Bitcoin Transaction) support
17
19
  * [WIP] SPV node
18
20
  * [WIP] 0ff-chain protocol
19
21
 
@@ -47,6 +47,10 @@ module Bitcoin
47
47
  autoload :BloomFilter, 'bitcoin/bloom_filter'
48
48
  autoload :Payments, 'bitcoin/payments'
49
49
  autoload :PSBT, 'bitcoin/psbt'
50
+ autoload :GCSFilter, 'bitcoin/gcs_filter'
51
+ autoload :BlockFilter, 'bitcoin/block_filter'
52
+ autoload :BitStreamWriter, 'bitcoin/bit_stream'
53
+ autoload :BitStreamReader, 'bitcoin/bit_stream'
50
54
 
51
55
  require_relative 'bitcoin/constants'
52
56
 
@@ -0,0 +1,71 @@
1
+ module Bitcoin
2
+
3
+ class BitStreamWriter
4
+
5
+ MAX_BIT = 2**64
6
+
7
+ attr_reader :stream
8
+ attr_accessor :buffer
9
+ attr_accessor :offset
10
+
11
+ def initialize
12
+ @stream = ''
13
+ @buffer = 0
14
+ @offset = 0
15
+ end
16
+
17
+ def write(data, nbits)
18
+ raise "nbits must be between 0 and 64" if nbits < 0 || nbits > 64
19
+ while nbits > 0
20
+ bits = [8 - offset, nbits].min
21
+ tmp = (data << (64 - nbits)) & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111
22
+ self.buffer |= (tmp >> (64 - 8 + offset))
23
+ self.offset += bits
24
+ nbits -= bits
25
+ flush if offset == 8
26
+ end
27
+ end
28
+
29
+ def flush
30
+ return if offset == 0
31
+ self.stream << [buffer.to_even_length_hex].pack('H*')
32
+ self.offset = 0
33
+ self.buffer = 0
34
+ end
35
+
36
+ end
37
+
38
+ class BitStreamReader
39
+
40
+ attr_reader :stream
41
+ attr_accessor :buffer
42
+ attr_accessor :offset
43
+
44
+ def initialize(payload)
45
+ @offset = 8
46
+ @buffer = 0
47
+ @stream = StringIO.new(payload)
48
+ end
49
+
50
+ # offset
51
+ def read(nbits)
52
+ raise 'nbits must be between 0 and 64' if nbits < 0 || nbits > 64
53
+ data = 0
54
+ while nbits > 0
55
+ if offset == 8
56
+ self.buffer = stream.read(1).bth.to_i(16)
57
+ self.offset = 0
58
+ end
59
+ bits = [8 - offset, nbits].min
60
+ data <<= bits
61
+ tmp = (buffer << offset) & 255
62
+ data = data | (tmp >> (8 - bits))
63
+ self.offset += bits
64
+ nbits -= bits
65
+ end
66
+ data
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,83 @@
1
+ module Bitcoin
2
+
3
+ # Compact Block Filter
4
+ # https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki
5
+ # This implementation ported the implementation of Bitcoin Core's blockfilter.cpp.
6
+ # https://github.com/bitcoin/bitcoin/blob/master/src/blockfilter.cpp
7
+ class BlockFilter
8
+
9
+ TYPE = {basic: 0}
10
+
11
+ # basic filter params
12
+ BASIC_FILTER_P = 19
13
+ BASIC_FILTER_M = 784931
14
+
15
+ attr_accessor :filter_type
16
+ attr_accessor :filter
17
+ attr_accessor :block_hash
18
+
19
+ # Constructor
20
+ # @param [Integer] filter_type
21
+ # @param [Bitcoin::GCSFilter] filter a GCS filter.
22
+ # @param [String] block_hash a block hash with hex format.
23
+ # @return [Bitcoin::BlockFilter]
24
+ def initialize(filter_type, filter, block_hash)
25
+ @filter_type = filter_type
26
+ @filter = filter
27
+ @block_hash = block_hash
28
+ end
29
+
30
+ # Build BlockFilter from the block data.
31
+ # @param [Integer] filter_type a filter type(basic or extended).
32
+ # @param [Bitcoin::Block] block target block object.
33
+ # @param [Array[Bitcoin::Script]] prev_out_scripts The previous output script (the script being spent) for each input, except for the coinbase transaction.
34
+ # @return [Bitcoin::BlockFilter] block filter object.
35
+ def self.build_from_block(filter_type, block, prev_out_scripts)
36
+ block_hash = block.block_hash.htb[0...16]
37
+ filter = case filter_type
38
+ when TYPE[:basic]
39
+ GCSFilter.new(block_hash, BASIC_FILTER_P, BASIC_FILTER_M, elements: build_basic_filter_elements(block, prev_out_scripts))
40
+ else
41
+ raise "unknown filter type: #{filter_type}."
42
+ end
43
+ BlockFilter.new(filter_type, filter, block.block_hash)
44
+ end
45
+
46
+
47
+ # calculate filter hash.
48
+ # @return [String] this filter hash with hex format.
49
+ def filter_hash
50
+ Bitcoin.double_sha256(encoded_filter.htb).bth
51
+ end
52
+
53
+ # calculate filter header which calculates from previous filter header and current filter hash.
54
+ # @param [String] prev_header a previous header with hex format.
55
+ # @return [String] header of this filter with hex format.
56
+ def header(prev_header)
57
+ Bitcoin.double_sha256(filter_hash.htb + prev_header.htb).bth
58
+ end
59
+
60
+ # get encoded filter.
61
+ def encoded_filter
62
+ filter.encoded
63
+ end
64
+
65
+ # build basic filter elements
66
+ # @param [Bitcoin::Block] block current block
67
+ # @param [Array[Bitcoin::Script]] prev_out_scripts The previous output script (the script being spent) for each input, except for the coinbase transaction.
68
+ # @return [Array[String]] basic filter elements
69
+ def self.build_basic_filter_elements(block, prev_out_scripts)
70
+ elements = []
71
+ block.transactions.each do |tx|
72
+ elements += tx.outputs.select{|o|
73
+ !o.script_pubkey.empty? && !o.script_pubkey.op_return?}.map{|o| o.script_pubkey.to_payload}
74
+ end
75
+ elements += prev_out_scripts.select{|s|!s.empty? && !s.op_return?}.map(&:to_payload)
76
+ elements.uniq
77
+ end
78
+
79
+ private_class_method :build_basic_filter_elements
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,135 @@
1
+ require 'siphash'
2
+
3
+ module Bitcoin
4
+
5
+ # Golomb-coded set filter
6
+ # see https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki
7
+ class GCSFilter
8
+
9
+ attr_reader :p # Golomb-Rice coding parameter
10
+ attr_reader :m # Inverse false positive rate
11
+ attr_reader :n # Number of elements in the filter
12
+ attr_reader :key # SipHash key
13
+ attr_reader :encoded # encoded filter with hex format.
14
+
15
+ # initialize Filter object.
16
+ # @param [String] key the 128-bit key used to randomize the SipHash outputs.
17
+ # @param [Integer] p the bit parameter of the Golomb-Rice coding.
18
+ # @param [Integer] m which determines the false positive rate.
19
+ # @param [Array] elements the filter elements.
20
+ # @param [String] encoded_filter encoded filter with hex format.
21
+ # @return [Bitcoin::GCSFilter]
22
+ def initialize(key, p, m, elements: nil, encoded_filter: nil)
23
+ raise 'specify either elements or encoded_filter.' if elements.nil? && encoded_filter.nil?
24
+ raise 'p must be <= 32' if p > 32
25
+ @key = key
26
+ @p = p
27
+ @m = m
28
+ if elements
29
+ raise 'elements size must be < 2**32.' if elements.size >= (2**32)
30
+ @n = elements.size
31
+ encoded = Bitcoin.pack_var_int(@n)
32
+ bit_writer = Bitcoin::BitStreamWriter.new
33
+ unless elements.empty?
34
+ last_value = 0
35
+ hashed_set = elements.map{|e| hash_to_range(e) }.sort
36
+ hashed_set.each do |v|
37
+ delta = v - last_value
38
+ golomb_rice_encode(bit_writer, p, delta)
39
+ last_value = v
40
+ end
41
+ end
42
+ bit_writer.flush
43
+ encoded << bit_writer.stream
44
+ @encoded = encoded.bth
45
+ else
46
+ @encoded = encoded_filter
47
+ @n, payload = Bitcoin.unpack_var_int(encoded_filter.htb)
48
+ end
49
+ end
50
+
51
+ # Range of element hashes, F = N * M
52
+ def f
53
+ n * m
54
+ end
55
+
56
+ # Hash a data element to an integer in the range [0, F).
57
+ # @param [String] element with binary format.
58
+ # @return [Integer]
59
+ def hash_to_range(element)
60
+ hash = SipHash.digest(key, element)
61
+ map_into_range(hash, f)
62
+ end
63
+
64
+ # Checks if the element may be in the set. False positives are possible with probability 1/M.
65
+ # @param [String] element with binary format
66
+ # @return [Boolean] whether element in set.
67
+ def match?(element)
68
+ query = hash_to_range(element)
69
+ match_internal?([query], 1)
70
+ end
71
+
72
+ # Checks if any of the given elements may be in the set. False positives are possible with probability 1/M per element checked.
73
+ # This is more efficient that checking Match on multiple elements separately.
74
+ # @param [Array] elements list of elements with binary format.
75
+ # @return [Boolean] whether element in set.
76
+ def match_any?(elements)
77
+ queries = elements.map{|e| hash_to_range(e) }.sort
78
+ match_internal?(queries, queries.size)
79
+ end
80
+
81
+ private
82
+
83
+ # hash are then mapped uniformly over the desired range by multiplying with F and taking the top 64 bits of the 128-bit result.
84
+ # https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
85
+ # https://stackoverflow.com/a/26855440
86
+ def map_into_range(x, y)
87
+ (x * y) >> 64
88
+ end
89
+
90
+ # Checks if the elements may be in the set.
91
+ # @param [Array[Integer]] hashes the query hash list.
92
+ # @param [Integer] size query size.
93
+ # @return [Boolean] whether elements in set.
94
+ def match_internal?(hashes, size)
95
+ n, payload = Bitcoin.unpack_var_int(encoded.htb)
96
+ bit_reader = Bitcoin::BitStreamReader.new(payload)
97
+ value = 0
98
+ hashes_index = 0
99
+ n.times do
100
+ delta = golomb_rice_decode(bit_reader, p)
101
+ value += delta
102
+ loop do
103
+ return false if hashes_index == size
104
+ return true if hashes[hashes_index] == value
105
+ break if hashes[hashes_index] > value
106
+ hashes_index += 1
107
+ end
108
+ end
109
+ false
110
+ end
111
+
112
+ # encode golomb rice
113
+ def golomb_rice_encode(bit_writer, p, x)
114
+ q = x >> p
115
+ while q > 0
116
+ nbits = q <= 64 ? q : 64
117
+ bit_writer.write(-1, nbits) # 18446744073709551615 is 2**64 - 1 = ~0ULL in cpp.
118
+ q -= nbits
119
+ end
120
+ bit_writer.write(0, 1)
121
+ bit_writer.write(x, p)
122
+ end
123
+
124
+ # decode golomb rice
125
+ def golomb_rice_decode(bit_reader, p)
126
+ q = 0
127
+ while bit_reader.read(1) == 1
128
+ q +=1
129
+ end
130
+ r = bit_reader.read(p)
131
+ (q << p) + r
132
+ end
133
+
134
+ end
135
+ end
@@ -105,14 +105,14 @@ module Bitcoin
105
105
  # wallet api
106
106
 
107
107
  # create wallet
108
- def createwallet(wallet_id = 1, wallet_path_prefix = Bitcoin::Wallet::Base::DEFAULT_PATH_PREFIX)
108
+ def createwallet(wallet_id = 1, wallet_path_prefix = Bitcoin::Wallet::Base.default_path_prefix)
109
109
  wallet = Bitcoin::Wallet::Base.create(wallet_id, wallet_path_prefix)
110
110
  node.wallet = wallet unless node.wallet
111
111
  {wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic}
112
112
  end
113
113
 
114
114
  # get wallet list.
115
- def listwallets(wallet_path_prefix = Bitcoin::Wallet::Base::DEFAULT_PATH_PREFIX)
115
+ def listwallets(wallet_path_prefix = Bitcoin::Wallet::Base.default_path_prefix)
116
116
  Bitcoin::Wallet::Base.wallet_paths(wallet_path_prefix)
117
117
  end
118
118
 
@@ -130,7 +130,11 @@ module Bitcoin
130
130
  end
131
131
  end
132
132
  else
133
- s << opcode.ord
133
+ if Opcodes.defined?(opcode.ord)
134
+ s << opcode.ord
135
+ else
136
+ s.chunks << (opcode + buf.read) # If opcode is invalid, put all remaining data in last chunk.
137
+ end
134
138
  end
135
139
  end
136
140
  s
@@ -331,7 +335,8 @@ module Bitcoin
331
335
  end
332
336
  end
333
337
  else
334
- Opcodes.opcode_to_name(c.ord)
338
+ opcode = Opcodes.opcode_to_name(c.ord)
339
+ opcode ? opcode : 'OP_UNKNOWN [error]'
335
340
  end
336
341
  end
337
342
  }.join(' ')
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
@@ -8,15 +8,19 @@ module Bitcoin
8
8
  attr_reader :db
9
9
  attr_reader :path
10
10
 
11
- DEFAULT_PATH_PREFIX = "#{Bitcoin.base_dir}/db/wallet/"
12
11
  VERSION = 1
13
12
 
13
+ # get wallet dir path
14
+ def self.default_path_prefix
15
+ "#{Bitcoin.base_dir}/db/wallet/"
16
+ end
17
+
14
18
  # Create new wallet. If wallet already exist, throw error.
15
19
  # The wallet generates a seed using SecureRandom and store to db at initialization.
16
20
  # @param [String] wallet_id new wallet id.
17
21
  # @param [String] path_prefix wallet file path prefix.
18
22
  # @return [Bitcoin::Wallet::Base] the wallet
19
- def self.create(wallet_id = 1, path_prefix = DEFAULT_PATH_PREFIX)
23
+ def self.create(wallet_id = 1, path_prefix = default_path_prefix)
20
24
  raise ArgumentError, "wallet_id : #{wallet_id} already exist." if self.exist?(wallet_id, path_prefix)
21
25
  w = self.new(wallet_id, path_prefix)
22
26
  # generate seed
@@ -29,23 +33,24 @@ module Bitcoin
29
33
 
30
34
  # load wallet with specified +wallet_id+
31
35
  # @return [Bitcoin::Wallet::Base] the wallet
32
- def self.load(wallet_id, path_prefix = DEFAULT_PATH_PREFIX)
36
+ def self.load(wallet_id, path_prefix = default_path_prefix)
33
37
  raise ArgumentError, "wallet_id : #{wallet_id} dose not exist." unless self.exist?(wallet_id, path_prefix)
34
38
  self.new(wallet_id, path_prefix)
35
39
  end
36
40
 
37
41
  # get wallets path
38
42
  # @return [Array] Array of paths for each wallet dir.
39
- def self.wallet_paths(path_prefix = DEFAULT_PATH_PREFIX)
43
+ def self.wallet_paths(path_prefix = default_path_prefix)
40
44
  Dir.glob("#{path_prefix}wallet*/").sort
41
45
  end
42
46
 
43
47
  # get current wallet
44
- def self.current_wallet(path_prefix = DEFAULT_PATH_PREFIX)
45
- path = wallet_paths.first # TODO default wallet selection
48
+ def self.current_wallet(path_prefix = default_path_prefix)
49
+ path = wallet_paths(path_prefix).first # TODO default wallet selection
46
50
  return nil unless path
47
- wallet_id = path.delete(path_prefix + '/wallet').delete('/').to_i
48
- self.load(wallet_id, path_prefix)
51
+ path.slice!(path_prefix + 'wallet')
52
+ path.slice!('/')
53
+ self.load(path.to_i, path_prefix)
49
54
  end
50
55
 
51
56
  # get account list based on BIP-44
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.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-13 00:00:00.000000000 Z
11
+ date: 2019-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -303,7 +303,9 @@ files:
303
303
  - exe/bitcoinrbd
304
304
  - lib/bitcoin.rb
305
305
  - lib/bitcoin/base58.rb
306
+ - lib/bitcoin/bit_stream.rb
306
307
  - lib/bitcoin/block.rb
308
+ - lib/bitcoin/block_filter.rb
307
309
  - lib/bitcoin/block_header.rb
308
310
  - lib/bitcoin/bloom_filter.rb
309
311
  - lib/bitcoin/chain_params.rb
@@ -312,6 +314,7 @@ files:
312
314
  - lib/bitcoin/chainparams/testnet.yml
313
315
  - lib/bitcoin/constants.rb
314
316
  - lib/bitcoin/ext_key.rb
317
+ - lib/bitcoin/gcs_filter.rb
315
318
  - lib/bitcoin/key.rb
316
319
  - lib/bitcoin/logger.rb
317
320
  - lib/bitcoin/merkle_tree.rb
@@ -436,8 +439,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
436
439
  - !ruby/object:Gem::Version
437
440
  version: '0'
438
441
  requirements: []
439
- rubyforge_project:
440
- rubygems_version: 2.7.8
442
+ rubygems_version: 3.0.1
441
443
  signing_key:
442
444
  specification_version: 4
443
445
  summary: "[WIP]The implementation of Bitcoin Protocol for Ruby."