bitcoinrb 0.2.4 → 0.2.5

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.
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."