tapyrus 0.2.7 → 0.2.8

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.prettierignore +3 -0
  4. data/.prettierrc.yaml +3 -0
  5. data/CODE_OF_CONDUCT.md +7 -7
  6. data/README.md +14 -17
  7. data/Rakefile +3 -3
  8. data/lib/openassets.rb +0 -2
  9. data/lib/openassets/marker_output.rb +0 -4
  10. data/lib/openassets/payload.rb +4 -10
  11. data/lib/schnorr.rb +2 -3
  12. data/lib/schnorr/signature.rb +3 -6
  13. data/lib/tapyrus.rb +6 -22
  14. data/lib/tapyrus/base58.rb +7 -6
  15. data/lib/tapyrus/block.rb +1 -2
  16. data/lib/tapyrus/block_header.rb +15 -9
  17. data/lib/tapyrus/bloom_filter.rb +5 -3
  18. data/lib/tapyrus/chain_params.rb +1 -4
  19. data/lib/tapyrus/chainparams/dev.yml +3 -2
  20. data/lib/tapyrus/chainparams/prod.yml +3 -2
  21. data/lib/tapyrus/constants.rb +29 -23
  22. data/lib/tapyrus/errors.rb +1 -3
  23. data/lib/tapyrus/ext.rb +1 -1
  24. data/lib/tapyrus/ext/ecdsa.rb +4 -4
  25. data/lib/tapyrus/ext/json_parser.rb +1 -4
  26. data/lib/tapyrus/ext_key.rb +38 -34
  27. data/lib/tapyrus/key.rb +31 -35
  28. data/lib/tapyrus/key_path.rb +15 -12
  29. data/lib/tapyrus/logger.rb +20 -16
  30. data/lib/tapyrus/merkle_tree.rb +19 -20
  31. data/lib/tapyrus/message.rb +14 -16
  32. data/lib/tapyrus/message/addr.rb +1 -7
  33. data/lib/tapyrus/message/base.rb +0 -3
  34. data/lib/tapyrus/message/block.rb +2 -9
  35. data/lib/tapyrus/message/block_transaction_request.rb +3 -6
  36. data/lib/tapyrus/message/block_transactions.rb +2 -6
  37. data/lib/tapyrus/message/block_txn.rb +0 -4
  38. data/lib/tapyrus/message/cmpct_block.rb +1 -7
  39. data/lib/tapyrus/message/error.rb +1 -4
  40. data/lib/tapyrus/message/fee_filter.rb +1 -4
  41. data/lib/tapyrus/message/filter_add.rb +0 -4
  42. data/lib/tapyrus/message/filter_clear.rb +0 -4
  43. data/lib/tapyrus/message/filter_load.rb +2 -5
  44. data/lib/tapyrus/message/get_addr.rb +0 -4
  45. data/lib/tapyrus/message/get_block_txn.rb +0 -4
  46. data/lib/tapyrus/message/get_blocks.rb +0 -3
  47. data/lib/tapyrus/message/get_data.rb +1 -4
  48. data/lib/tapyrus/message/get_headers.rb +1 -3
  49. data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
  50. data/lib/tapyrus/message/headers.rb +0 -4
  51. data/lib/tapyrus/message/headers_parser.rb +3 -8
  52. data/lib/tapyrus/message/inv.rb +1 -4
  53. data/lib/tapyrus/message/inventories_parser.rb +2 -7
  54. data/lib/tapyrus/message/inventory.rb +12 -5
  55. data/lib/tapyrus/message/mem_pool.rb +0 -4
  56. data/lib/tapyrus/message/merkle_block.rb +4 -9
  57. data/lib/tapyrus/message/network_addr.rb +7 -6
  58. data/lib/tapyrus/message/not_found.rb +0 -3
  59. data/lib/tapyrus/message/ping.rb +0 -3
  60. data/lib/tapyrus/message/pong.rb +0 -3
  61. data/lib/tapyrus/message/prefilled_tx.rb +0 -4
  62. data/lib/tapyrus/message/reject.rb +0 -3
  63. data/lib/tapyrus/message/send_cmpct.rb +1 -3
  64. data/lib/tapyrus/message/send_headers.rb +0 -3
  65. data/lib/tapyrus/message/tx.rb +0 -4
  66. data/lib/tapyrus/message/ver_ack.rb +1 -5
  67. data/lib/tapyrus/message/version.rb +2 -5
  68. data/lib/tapyrus/mnemonic.rb +17 -15
  69. data/lib/tapyrus/network.rb +0 -2
  70. data/lib/tapyrus/network/connection.rb +0 -3
  71. data/lib/tapyrus/network/message_handler.rb +61 -60
  72. data/lib/tapyrus/network/peer.rb +13 -12
  73. data/lib/tapyrus/network/peer_discovery.rb +3 -5
  74. data/lib/tapyrus/network/pool.rb +12 -12
  75. data/lib/tapyrus/node.rb +1 -1
  76. data/lib/tapyrus/node/cli.rb +12 -14
  77. data/lib/tapyrus/node/configuration.rb +1 -3
  78. data/lib/tapyrus/node/spv.rb +2 -3
  79. data/lib/tapyrus/opcodes.rb +9 -7
  80. data/lib/tapyrus/out_point.rb +5 -5
  81. data/lib/tapyrus/rpc/http_server.rb +21 -22
  82. data/lib/tapyrus/rpc/request_handler.rb +42 -44
  83. data/lib/tapyrus/rpc/tapyrus_core_client.rb +53 -25
  84. data/lib/tapyrus/script/color.rb +10 -0
  85. data/lib/tapyrus/script/multisig.rb +13 -12
  86. data/lib/tapyrus/script/script.rb +72 -71
  87. data/lib/tapyrus/script/script_error.rb +1 -4
  88. data/lib/tapyrus/script/script_interpreter.rb +439 -399
  89. data/lib/tapyrus/script/tx_checker.rb +20 -10
  90. data/lib/tapyrus/secp256k1.rb +0 -4
  91. data/lib/tapyrus/secp256k1/native.rb +14 -15
  92. data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
  93. data/lib/tapyrus/secp256k1/ruby.rb +10 -12
  94. data/lib/tapyrus/slip39.rb +20 -5
  95. data/lib/tapyrus/slip39/share.rb +41 -29
  96. data/lib/tapyrus/slip39/sss.rb +101 -57
  97. data/lib/tapyrus/store.rb +1 -3
  98. data/lib/tapyrus/store/chain_entry.rb +0 -4
  99. data/lib/tapyrus/store/db.rb +0 -2
  100. data/lib/tapyrus/store/db/level_db.rb +5 -9
  101. data/lib/tapyrus/store/spv_chain.rb +11 -17
  102. data/lib/tapyrus/tx.rb +45 -37
  103. data/lib/tapyrus/tx_builder.rb +158 -0
  104. data/lib/tapyrus/tx_in.rb +1 -6
  105. data/lib/tapyrus/tx_out.rb +2 -7
  106. data/lib/tapyrus/util.rb +7 -9
  107. data/lib/tapyrus/validation.rb +12 -11
  108. data/lib/tapyrus/version.rb +1 -1
  109. data/lib/tapyrus/wallet/account.rb +22 -18
  110. data/lib/tapyrus/wallet/base.rb +12 -9
  111. data/lib/tapyrus/wallet/db.rb +6 -9
  112. data/lib/tapyrus/wallet/master_key.rb +2 -4
  113. data/tapyrusrb.gemspec +13 -14
  114. metadata +21 -4
  115. data/.travis.yml +0 -14
@@ -2,10 +2,8 @@ require 'securerandom'
2
2
 
3
3
  module Tapyrus
4
4
  module SLIP39
5
-
6
5
  # Shamir's Secret Sharing
7
6
  class SSS
8
-
9
7
  include Tapyrus::Util
10
8
  extend Tapyrus::Util
11
9
 
@@ -37,39 +35,56 @@ module Tapyrus
37
35
  raise ArgumentError, 'Groups is empty.' if groups.empty?
38
36
  raise ArgumentError, 'Group threshold must be greater than 0.' if group_threshold.nil? || group_threshold < 1
39
37
  raise ArgumentError, 'Master secret does not specified.' unless secret
40
- raise ArgumentError, "The length of the master secret (#{secret.htb.bytesize} bytes) must be at least #{MIN_STRENGTH_BITS / 8} bytes." if (secret.htb.bytesize * 8) < MIN_STRENGTH_BITS
41
- raise ArgumentError, 'The length of the master secret in bytes must be an even number.' unless secret.bytesize.even?
42
- raise ArgumentError, 'The passphrase must contain only printable ASCII characters (code points 32-126).' unless passphrase.ascii_only?
43
- raise ArgumentError, "The requested group threshold (#{group_threshold}) must not exceed the number of groups (#{groups.length})." if group_threshold > groups.length
38
+ if (secret.htb.bytesize * 8) < MIN_STRENGTH_BITS
39
+ raise ArgumentError,
40
+ "The length of the master secret (#{secret.htb.bytesize} bytes) must be at least #{MIN_STRENGTH_BITS / 8} bytes."
41
+ end
42
+ unless secret.bytesize.even?
43
+ raise ArgumentError, 'The length of the master secret in bytes must be an even number.'
44
+ end
45
+ unless passphrase.ascii_only?
46
+ raise ArgumentError, 'The passphrase must contain only printable ASCII characters (code points 32-126).'
47
+ end
48
+ if group_threshold > groups.length
49
+ raise ArgumentError,
50
+ "The requested group threshold (#{group_threshold}) must not exceed the number of groups (#{groups.length})."
51
+ end
44
52
  groups.each do |threshold, count|
45
53
  raise ArgumentError, 'Group threshold must be greater than 0.' if threshold.nil? || threshold < 1
46
- raise ArgumentError, "The requested member threshold (#{threshold}) must not exceed the number of share (#{count})." if threshold > count
47
- raise ArgumentError, "Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead." if threshold == 1 && count > 1
54
+ if threshold > count
55
+ raise ArgumentError,
56
+ "The requested member threshold (#{threshold}) must not exceed the number of share (#{count})."
57
+ end
58
+ if threshold == 1 && count > 1
59
+ raise ArgumentError,
60
+ 'Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead.'
61
+ end
48
62
  end
49
63
 
50
- id = SecureRandom.random_number(32767) # 32767 is max number for 15 bits.
64
+ id = SecureRandom.random_number(32_767) # 32767 is max number for 15 bits.
51
65
  ems = encrypt(secret, passphrase, exp, id)
52
66
 
53
67
  group_shares = split_secret(group_threshold, groups.length, ems)
54
68
 
55
- shares = group_shares.map.with_index do |s, i|
56
- group_index, group_share = s[0], s[1]
57
- member_threshold, member_count = groups[i][0], groups[i][1]
58
- shares = split_secret(member_threshold, member_count, group_share)
59
- shares.map do |member_index, member_share|
60
- share = Tapyrus::SLIP39::Share.new
61
- share.id = id
62
- share.iteration_exp = exp
63
- share.group_index = group_index
64
- share.group_threshold = group_threshold
65
- share.group_count = groups.length
66
- share.member_index = member_index
67
- share.member_threshold = member_threshold
68
- share.value = member_share
69
- share.checksum = share.calculate_checksum
70
- share
69
+ shares =
70
+ group_shares.map.with_index do |s, i|
71
+ group_index, group_share = s[0], s[1]
72
+ member_threshold, member_count = groups[i][0], groups[i][1]
73
+ shares = split_secret(member_threshold, member_count, group_share)
74
+ shares.map do |member_index, member_share|
75
+ share = Tapyrus::SLIP39::Share.new
76
+ share.id = id
77
+ share.iteration_exp = exp
78
+ share.group_index = group_index
79
+ share.group_threshold = group_threshold
80
+ share.group_count = groups.length
81
+ share.member_index = member_index
82
+ share.member_threshold = member_threshold
83
+ share.value = member_share
84
+ share.checksum = share.calculate_checksum
85
+ share
86
+ end
71
87
  end
72
- end
73
88
  shares
74
89
  end
75
90
 
@@ -92,9 +107,15 @@ module Tapyrus
92
107
 
93
108
  shares.each do |share|
94
109
  raise ArgumentError, 'Invalid set of shares. All shares must have the same id.' unless id == share.id
95
- raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.' unless group_threshold == share.group_threshold
96
- raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.' unless group_count == share.group_count
97
- raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.' unless exp == share.iteration_exp
110
+ unless group_threshold == share.group_threshold
111
+ raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.'
112
+ end
113
+ unless group_count == share.group_count
114
+ raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.'
115
+ end
116
+ unless exp == share.iteration_exp
117
+ raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.'
118
+ end
98
119
  groups[share.group_index] ||= []
99
120
  groups[share.group_index] << share
100
121
  end
@@ -102,20 +123,29 @@ module Tapyrus
102
123
  group_shares = {}
103
124
  groups.each do |group_index, shares|
104
125
  member_threshold = shares.first.member_threshold
105
- raise ArgumentError, "Wrong number of mnemonics. Threshold is #{member_threshold}, but share count is #{shares.length}" if shares.length < member_threshold
126
+ if shares.length < member_threshold
127
+ raise ArgumentError,
128
+ "Wrong number of mnemonics. Threshold is #{member_threshold}, but share count is #{shares.length}"
129
+ end
106
130
  if shares.length == 1 && member_threshold == 1
107
131
  group_shares[group_index] = shares.first.value
108
132
  else
109
133
  value_length = shares.first.value.length
110
134
  x_coordinates = []
111
135
  shares.each do |share|
112
- raise ArgumentError, 'Invalid set of shares. All shares in a group must have the same member threshold.' unless member_threshold == share.member_threshold
113
- raise ArgumentError, 'Invalid set of shares. All share values must have the same length.' unless value_length == share.value.length
136
+ unless member_threshold == share.member_threshold
137
+ raise ArgumentError, 'Invalid set of shares. All shares in a group must have the same member threshold.'
138
+ end
139
+ unless value_length == share.value.length
140
+ raise ArgumentError, 'Invalid set of shares. All share values must have the same length.'
141
+ end
114
142
  x_coordinates << share.member_index
115
143
  end
116
144
  x_coordinates.uniq!
117
- raise ArgumentError, 'Invalid set of shares. Share indices must be unique.' unless x_coordinates.size == shares.size
118
- interpolate_shares = shares.map{|s|[s.member_index, s.value]}
145
+ unless x_coordinates.size == shares.size
146
+ raise ArgumentError, 'Invalid set of shares. Share indices must be unique.'
147
+ end
148
+ interpolate_shares = shares.map { |s| [s.member_index, s.value] }
119
149
 
120
150
  secret = interpolate(interpolate_shares, SECRET_INDEX)
121
151
  digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb
@@ -129,9 +159,12 @@ module Tapyrus
129
159
 
130
160
  return decrypt(group_shares.values.first, passphrase, exp, id) if group_threshold == 1
131
161
 
132
- raise ArgumentError, "Wrong number of mnemonics. Group threshold is #{group_threshold}, but share count is #{group_shares.length}" if group_shares.length < group_threshold
162
+ if group_shares.length < group_threshold
163
+ raise ArgumentError,
164
+ "Wrong number of mnemonics. Group threshold is #{group_threshold}, but share count is #{group_shares.length}"
165
+ end
133
166
 
134
- interpolate_shares = group_shares.map{|k, v|[k, v]}
167
+ interpolate_shares = group_shares.map { |k, v| [k, v] }
135
168
  secret = interpolate(interpolate_shares, SECRET_INDEX)
136
169
  digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb
137
170
  digest, random_value = digest_value[0...DIGEST_LENGTH_BYTES].bth, digest_value[DIGEST_LENGTH_BYTES..-1].bth
@@ -148,17 +181,18 @@ module Tapyrus
148
181
  # @param [Integer] x the x coordinate of the result.
149
182
  # @return [String] f(x) value with hex format.
150
183
  def self.interpolate(shares, x)
151
- s = shares.find{|s|s[0] == x}
184
+ s = shares.find { |s| s[0] == x }
152
185
  return s[1] if s
153
186
 
154
- log_prod = shares.sum{|s|LOG_TABLE[s[0] ^ x]}
187
+ log_prod = shares.sum { |s| LOG_TABLE[s[0] ^ x] }
155
188
 
156
189
  result = ('00' * shares.first[1].length).htb
157
190
  shares.each do |share|
158
- log_basis_eval = (log_prod - LOG_TABLE[share[0] ^ x] - shares.sum{|s|LOG_TABLE[share[0] ^ s[0]]}) % 255
159
- result = share[1].htb.bytes.each.map.with_index do |v, i|
160
- (result[i].bti ^ (v == 0 ? 0 : (EXP_TABLE[(LOG_TABLE[v] + log_basis_eval) % 255]))).itb
161
- end.join
191
+ log_basis_eval = (log_prod - LOG_TABLE[share[0] ^ x] - shares.sum { |s| LOG_TABLE[share[0] ^ s[0]] }) % 255
192
+ result =
193
+ share[1].htb.bytes.each.map.with_index do |v, i|
194
+ (result[i].bti ^ (v == 0 ? 0 : (EXP_TABLE[(LOG_TABLE[v] + log_basis_eval) % 255]))).itb
195
+ end.join
162
196
  end
163
197
  result.bth
164
198
  end
@@ -172,10 +206,14 @@ module Tapyrus
172
206
  l, r = ems[0...(ems.length / 2)].htb, ems[(ems.length / 2)..-1].htb
173
207
  salt = get_salt(id)
174
208
  e = (Tapyrus::SLIP39::BASE_ITERATION_COUNT << exp) / Tapyrus::SLIP39::ROUND_COUNT
175
- Tapyrus::SLIP39::ROUND_COUNT.times.to_a.reverse.each do |i|
176
- f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
177
- l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize)
178
- end
209
+ Tapyrus::SLIP39::ROUND_COUNT
210
+ .times
211
+ .to_a
212
+ .reverse
213
+ .each do |i|
214
+ f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
215
+ l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize)
216
+ end
179
217
  (r + l).bth
180
218
  end
181
219
 
@@ -190,10 +228,13 @@ module Tapyrus
190
228
  l, r = s[0...(s.bytesize / 2)], s[(s.bytesize / 2)..-1]
191
229
  salt = get_salt(id)
192
230
  e = (Tapyrus::SLIP39::BASE_ITERATION_COUNT << exp) / Tapyrus::SLIP39::ROUND_COUNT
193
- Tapyrus::SLIP39::ROUND_COUNT.times.to_a.each do |i|
194
- f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
195
- l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize)
196
- end
231
+ Tapyrus::SLIP39::ROUND_COUNT
232
+ .times
233
+ .to_a
234
+ .each do |i|
235
+ f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
236
+ l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize)
237
+ end
197
238
  (r + l).bth
198
239
  end
199
240
 
@@ -220,26 +261,29 @@ module Tapyrus
220
261
  # @return [Array[Integer, String]] the array of split secret.
221
262
  def self.split_secret(threshold, count, secret)
222
263
  raise ArgumentError, "The requested threshold (#{threshold}) must be a positive integer." if threshold < 1
223
- raise ArgumentError, "The requested threshold (#{threshold}) must not exceed the number of shares (#{count})." if threshold > count
224
- raise ArgumentError, "The requested number of shares (#{count}) must not exceed #{MAX_SHARE_COUNT}." if count > MAX_SHARE_COUNT
264
+ if threshold > count
265
+ raise ArgumentError, "The requested threshold (#{threshold}) must not exceed the number of shares (#{count})."
266
+ end
267
+ if count > MAX_SHARE_COUNT
268
+ raise ArgumentError, "The requested number of shares (#{count}) must not exceed #{MAX_SHARE_COUNT}."
269
+ end
225
270
 
226
- return count.times.map{|i|[i, secret]} if threshold == 1 # if the threshold is 1, digest of the share is not used.
271
+ return count.times.map { |i| [i, secret] } if threshold == 1 # if the threshold is 1, digest of the share is not used.
227
272
 
228
273
  random_share_count = threshold - 2
229
274
 
230
- shares = random_share_count.times.map{|i|[i, SecureRandom.hex(secret.htb.bytesize)]}
275
+ shares = random_share_count.times.map { |i| [i, SecureRandom.hex(secret.htb.bytesize)] }
231
276
  random_part = SecureRandom.hex(secret.htb.bytesize - DIGEST_LENGTH_BYTES)
232
277
  digest = create_digest(secret, random_part)
233
278
 
234
279
  base_shares = shares + [[DIGEST_INDEX, digest + random_part], [SECRET_INDEX, secret]]
235
280
 
236
- (random_share_count...count).each { |i| shares << [i, interpolate(base_shares, i)]}
281
+ (random_share_count...count).each { |i| shares << [i, interpolate(base_shares, i)] }
237
282
 
238
283
  shares
239
284
  end
240
285
 
241
286
  private_class_method :split_secret, :get_salt, :interpolate, :encrypt, :decrypt, :create_digest
242
-
243
287
  end
244
288
  end
245
- end
289
+ end
data/lib/tapyrus/store.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  module Tapyrus
2
2
  module Store
3
-
4
3
  autoload :DB, 'tapyrus/store/db'
5
4
  autoload :SPVChain, 'tapyrus/store/spv_chain'
6
5
  autoload :ChainEntry, 'tapyrus/store/chain_entry'
7
-
8
6
  end
9
- end
7
+ end
@@ -1,6 +1,5 @@
1
1
  module Tapyrus
2
2
  module Store
3
-
4
3
  # wrap a block header object with extra data.
5
4
  class ChainEntry
6
5
  include Tapyrus::HexConverter
@@ -60,9 +59,6 @@ module Tapyrus
60
59
  height_value = height_value.htb.reverse
61
60
  Tapyrus.pack_var_int(height_value.bytesize) + height_value + header.to_payload
62
61
  end
63
-
64
62
  end
65
-
66
63
  end
67
-
68
64
  end
@@ -1,9 +1,7 @@
1
1
  module Tapyrus
2
2
  module Store
3
-
4
3
  module DB
5
4
  autoload :LevelDB, 'tapyrus/store/db/level_db'
6
5
  end
7
-
8
6
  end
9
7
  end
@@ -3,9 +3,7 @@ require 'leveldb-native'
3
3
  module Tapyrus
4
4
  module Store
5
5
  module DB
6
-
7
6
  class LevelDB
8
-
9
7
  attr_reader :db
10
8
  attr_reader :logger
11
9
 
@@ -62,9 +60,11 @@ module Tapyrus
62
60
  # @param [Tapyrus::Store::ChainEntry]
63
61
  def save_entry(entry)
64
62
  db.batch do
65
- db.put(entry.key ,entry.to_payload)
63
+ db.put(entry.key, entry.to_payload)
66
64
  db.put(height_key(entry.height), entry.block_hash)
67
- add_agg_pubkey(entry.height == 0 ? 0 : entry.height + 1, entry.header.x_field) if entry.header.upgrade_agg_pubkey?
65
+ if entry.header.upgrade_agg_pubkey?
66
+ add_agg_pubkey(entry.height == 0 ? 0 : entry.height + 1, entry.header.x_field)
67
+ end
68
68
  connect_entry(entry)
69
69
  end
70
70
  end
@@ -134,9 +134,7 @@ module Tapyrus
134
134
  unless tip_block.block_hash == entry.prev_hash
135
135
  raise "entry(#{entry.block_hash}) does not reference current best block hash(#{tip_block.block_hash})"
136
136
  end
137
- unless tip_block.height + 1 == entry.height
138
- raise "block height is small than current best block."
139
- end
137
+ raise 'block height is small than current best block.' unless tip_block.height + 1 == entry.height
140
138
  end
141
139
  db.put(KEY_PREFIX[:best], entry.block_hash)
142
140
  db.put(KEY_PREFIX[:next] + entry.prev_hash, entry.block_hash)
@@ -148,9 +146,7 @@ module Tapyrus
148
146
  index = db.get(KEY_PREFIX[:latest_agg_pubkey])
149
147
  index&.to_i(16)
150
148
  end
151
-
152
149
  end
153
-
154
150
  end
155
151
  end
156
152
  end
@@ -1,18 +1,15 @@
1
1
  module Tapyrus
2
-
3
2
  module Store
4
-
5
3
  KEY_PREFIX = {
6
- entry: 'e', # key: block hash, value: Tapyrus::Store::ChainEntry payload
7
- height: 'h', # key: block height, value: block hash.
8
- best: 'B', # value: best block hash.
9
- next: 'n', # key: block hash, value: A hash of the next block of the specified hash
10
- agg_pubkey: 'a', # key: index, value: Activated block height | aggregated public key.
11
- latest_agg_pubkey: 'g' # value: latest agg pubkey index.
4
+ entry: 'e', # key: block hash, value: Tapyrus::Store::ChainEntry payload
5
+ height: 'h', # key: block height, value: block hash.
6
+ best: 'B', # value: best block hash.
7
+ next: 'n', # key: block hash, value: A hash of the next block of the specified hash
8
+ agg_pubkey: 'a', # key: index, value: Activated block height | aggregated public key.
9
+ latest_agg_pubkey: 'g' # value: latest agg pubkey index.
12
10
  }
13
11
 
14
12
  class SPVChain
15
-
16
13
  attr_reader :db
17
14
  attr_reader :logger
18
15
 
@@ -53,7 +50,9 @@ module Tapyrus
53
50
  logger.info("append header #{header.block_id}")
54
51
  best_block = latest_block
55
52
  current_height = best_block.height
56
- raise "this header is invalid. #{header.block_hash}" unless header.valid?(db.agg_pubkey_with_height(current_height + 1))
53
+ unless header.valid?(db.agg_pubkey_with_height(current_height + 1))
54
+ raise "this header is invalid. #{header.block_hash}"
55
+ end
57
56
  if best_block.block_hash == header.prev_hash
58
57
  entry = Tapyrus::Store::ChainEntry.new(header, current_height + 1)
59
58
  db.save_entry(entry)
@@ -107,13 +106,8 @@ module Tapyrus
107
106
  # if database is empty, put genesis block.
108
107
  # @param [Tapyrus::Block] genesis genesis block
109
108
  def initialize_block(genesis)
110
- unless latest_block
111
- db.save_entry(ChainEntry.new(genesis.header, 0))
112
- end
109
+ db.save_entry(ChainEntry.new(genesis.header, 0)) unless latest_block
113
110
  end
114
-
115
111
  end
116
-
117
112
  end
118
-
119
- end
113
+ end
data/lib/tapyrus/tx.rb CHANGED
@@ -2,7 +2,6 @@
2
2
  # https://github.com/lian/bitcoin-ruby/blob/master/COPYING
3
3
 
4
4
  module Tapyrus
5
-
6
5
  # Transaction class
7
6
  class Tx
8
7
  include Tapyrus::HexConverter
@@ -10,7 +9,7 @@ module Tapyrus
10
9
  MAX_STANDARD_VERSION = 2
11
10
 
12
11
  # The maximum weight for transactions we're willing to relay/mine
13
- MAX_STANDARD_TX_WEIGHT = 400000
12
+ MAX_STANDARD_TX_WEIGHT = 400_000
14
13
 
15
14
  attr_accessor :features
16
15
  attr_reader :inputs
@@ -34,14 +33,10 @@ module Tapyrus
34
33
 
35
34
  in_count = Tapyrus.unpack_var_int_from_io(buf)
36
35
 
37
- in_count.times do
38
- tx.inputs << TxIn.parse_from_payload(buf)
39
- end
36
+ in_count.times { tx.inputs << TxIn.parse_from_payload(buf) }
40
37
 
41
38
  out_count = Tapyrus.unpack_var_int_from_io(buf)
42
- out_count.times do
43
- tx.outputs << TxOut.parse_from_payload(buf)
44
- end
39
+ out_count.times { tx.outputs << TxOut.parse_from_payload(buf) }
45
40
 
46
41
  tx.lock_time = buf.read(4).unpack('V').first
47
42
 
@@ -58,7 +53,7 @@ module Tapyrus
58
53
 
59
54
  def txid
60
55
  buf = [features].pack('V')
61
- buf << Tapyrus.pack_var_int(inputs.length) << inputs.map{|i|i.to_payload(use_malfix: true)}.join
56
+ buf << Tapyrus.pack_var_int(inputs.length) << inputs.map { |i| i.to_payload(use_malfix: true) }.join
62
57
  buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
63
58
  buf << [lock_time].pack('V')
64
59
  Tapyrus.double_sha256(buf).reverse.bth
@@ -95,6 +90,7 @@ module Tapyrus
95
90
  outputs.each do |o|
96
91
  return false unless o.script_pubkey.standard?
97
92
  data_count += 1 if o.script_pubkey.op_return?
93
+
98
94
  # TODO add non P2SH multisig relay(permitbaremultisig)
99
95
  return false if o.dust?
100
96
  end
@@ -114,8 +110,14 @@ module Tapyrus
114
110
  # @param [Integer] amount tapyrus amount locked in input. required for witness input only.
115
111
  # @param [Integer] skip_separator_index If output_script is P2WSH and output_script contains any OP_CODESEPARATOR,
116
112
  # the script code needs is the witnessScript but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed.
117
- def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all],
118
- sig_version: :base, amount: nil, skip_separator_index: 0)
113
+ def sighash_for_input(
114
+ input_index,
115
+ output_script,
116
+ hash_type: SIGHASH_TYPE[:all],
117
+ sig_version: :base,
118
+ amount: nil,
119
+ skip_separator_index: 0
120
+ )
119
121
  raise ArgumentError, 'input_index must be specified.' unless input_index
120
122
  raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
121
123
  raise ArgumentError, 'script_pubkey must be specified.' unless output_script
@@ -129,16 +131,19 @@ module Tapyrus
129
131
  # @param [Integer] amount the amount of tapyrus, require for witness program only.
130
132
  # @param [Array] flags the flags used when execute script interpreter.
131
133
  def verify_input_sig(input_index, script_pubkey, amount: nil, flags: STANDARD_SCRIPT_VERIFY_FLAGS)
132
- if script_pubkey.p2sh?
133
- flags << SCRIPT_VERIFY_P2SH
134
- end
134
+ flags << SCRIPT_VERIFY_P2SH if script_pubkey.p2sh?
135
135
  verify_input_sig_for_legacy(input_index, script_pubkey, flags)
136
136
  end
137
137
 
138
138
  def to_h
139
139
  {
140
- txid: txid, hash: tx_hash, features: features, size: size, locktime: lock_time,
141
- vin: inputs.map(&:to_h), vout: outputs.map.with_index{|tx_out, index| tx_out.to_h.merge({n: index})}
140
+ txid: txid,
141
+ hash: tx_hash,
142
+ features: features,
143
+ size: size,
144
+ locktime: lock_time,
145
+ vin: inputs.map(&:to_h),
146
+ vout: outputs.map.with_index { |tx_out, index| tx_out.to_h.merge({ n: index }) }
142
147
  }
143
148
  end
144
149
 
@@ -154,43 +159,48 @@ module Tapyrus
154
159
 
155
160
  # generate sighash with legacy format
156
161
  def sighash_for_legacy(index, script_code, hash_type)
157
- ins = inputs.map.with_index do |i, idx|
158
- if idx == index
159
- i.to_payload(script_code.delete_opcode(Tapyrus::Opcodes::OP_CODESEPARATOR))
160
- else
161
- case hash_type & 0x1f
162
+ ins =
163
+ inputs.map.with_index do |i, idx|
164
+ if idx == index
165
+ i.to_payload(script_code.delete_opcode(Tapyrus::Opcodes::OP_CODESEPARATOR))
166
+ else
167
+ case hash_type & 0x1f
162
168
  when SIGHASH_TYPE[:none], SIGHASH_TYPE[:single]
163
169
  i.to_payload(Tapyrus::Script.new, 0)
164
170
  else
165
171
  i.to_payload(Tapyrus::Script.new)
172
+ end
166
173
  end
167
174
  end
168
- end
169
175
 
170
176
  outs = outputs.map(&:to_payload)
171
177
  out_size = Tapyrus.pack_var_int(outputs.size)
172
178
 
173
179
  case hash_type & 0x1f
174
- when SIGHASH_TYPE[:none]
175
- outs = ''
176
- out_size = Tapyrus.pack_var_int(0)
177
- when SIGHASH_TYPE[:single]
178
- return "\x01".ljust(32, "\x00") if index >= outputs.size
179
- outs = outputs[0...(index + 1)].map.with_index { |o, idx| (idx == index) ? o.to_payload : o.to_empty_payload }.join
180
- out_size = Tapyrus.pack_var_int(index + 1)
180
+ when SIGHASH_TYPE[:none]
181
+ outs = ''
182
+ out_size = Tapyrus.pack_var_int(0)
183
+ when SIGHASH_TYPE[:single]
184
+ return "\x01".ljust(32, "\x00") if index >= outputs.size
185
+ outs =
186
+ outputs[0...(index + 1)].map.with_index { |o, idx| (idx == index) ? o.to_payload : o.to_empty_payload }.join
187
+ out_size = Tapyrus.pack_var_int(index + 1)
181
188
  end
182
189
 
183
- if hash_type & SIGHASH_TYPE[:anyonecanpay] != 0
184
- ins = [ins[index]]
185
- end
190
+ ins = [ins[index]] if hash_type & SIGHASH_TYPE[:anyonecanpay] != 0
186
191
 
187
- buf = [[features].pack('V'), Tapyrus.pack_var_int(ins.size),
188
- ins, out_size, outs, [lock_time, hash_type].pack('VV')].join
192
+ buf = [
193
+ [features].pack('V'),
194
+ Tapyrus.pack_var_int(ins.size),
195
+ ins,
196
+ out_size,
197
+ outs,
198
+ [lock_time, hash_type].pack('VV')
199
+ ].join
189
200
 
190
201
  Tapyrus.double_sha256(buf)
191
202
  end
192
203
 
193
-
194
204
  # verify input signature for legacy tx.
195
205
  def verify_input_sig_for_legacy(input_index, script_pubkey, flags)
196
206
  script_sig = inputs[input_index].script_sig
@@ -199,7 +209,5 @@ module Tapyrus
199
209
 
200
210
  interpreter.verify_script(script_sig, script_pubkey)
201
211
  end
202
-
203
212
  end
204
-
205
213
  end