Commit 34db910b31e1c96a91b28f56338a0667b1552422

Authored by Marius Hanne
Exists in master and in 1 other branch benchmark

Merge remote-tracking branch 'comboy/validation_optimizations2'

Showing 2 changed files Side-by-side Diff

lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb
  1 +Sequel.migration do
  2 +
  3 + up do
  4 +
  5 + @log.info { "Running migration #{__FILE__}" }
  6 +
  7 + # Naming seems to be different on different adapters and sequel's
  8 + # "drop_index(:txin, :prev_out)" doesn't seem to be handling it correctly
  9 + execute "DROP INDEX IF EXISTS txin_prev_out_idx;"
  10 + execute "DROP INDEX IF EXISTS txin_prev_out_index;"
  11 +
  12 + add_index :txin, [:prev_out, :prev_out_index]
  13 +
  14 + end
  15 +
  16 +end
lib/bitcoin/validation.rb
... ... @@ -155,19 +155,28 @@
155 155 end
156 156  
157 157 def tx_validators
158   - @tx_validators ||= block.tx[1..-1].map {|tx| tx.validator(store, block, tx_cache: prev_txs_hash)}
  158 + @tx_validators ||= block.tx[1..-1].map {|tx| tx.validator(store, block, tx_cache: prev_txs_hash, spent_outs_txins: spent_outs_txins)}
159 159 end
160 160  
161 161 # Fetch all prev_txs that will be needed for validation
162 162 # Used for optimization in tx validators
163 163 def prev_txs_hash
164 164 @prev_tx_hash ||= (
165   - inputs = block.tx.map {|tx| tx.in }.flatten
  165 + inputs = block.tx[1..-1].map {|tx| tx.in }.flatten
166 166 txs = store.get_txs(inputs.map{|i| i.prev_out.reverse_hth })
167 167 Hash[*txs.map {|tx| [tx.hash, tx] }.flatten]
168 168 )
169 169 end
170 170  
  171 + def spent_outs_txins
  172 + @spent_outs_txins ||= (
  173 + next_ins = store.get_txins_for_txouts(block.tx[1..-1].map(&:in).flatten.map.with_index {|txin, idx| [txin.prev_out.reverse_hth, txin.prev_out_index] })
  174 + # OPTIMIZE normally next_ins is empty, but in case of some reorgs this could be pain, becouse get_tx is heavy
  175 + # and all we need is a few joins (but some general abstraction is needed for that in storage)
  176 + next_ins.select {|i| i.get_tx.blk_id }
  177 + )
  178 + end
  179 +
171 180 def next_bits_required
172 181 retarget = (Bitcoin.network[:retarget_interval] || Bitcoin::RETARGET_INTERVAL)
173 182 index = (prev_block.depth + 1) / retarget
174 183  
... ... @@ -234,10 +243,13 @@
234 243  
235 244 # setup new validator for given +tx+, validating context with +store+.
236 245 # also needs the +block+ to find prev_outs for chains of tx inside one block.
237   - # opts+ may include :tx_cache which should be hash with transactiotns including prev_txs
  246 + # opts+ may include:
  247 + # * :tx_cache which should be hash with transactiotns including prev_txs
  248 + # * :spent_outs_txins txins for txouts that were already spent
238 249 def initialize(tx, store, block = nil, opts = {})
239 250 @tx, @store, @block, @errors = tx, store, block, []
240 251 @tx_cache = opts[:tx_cache]
  252 + @spent_outs_txins = opts[:spent_outs_txins]
241 253 end
242 254  
243 255 # check that tx hash matches data
244 256  
... ... @@ -308,9 +320,11 @@
308 320  
309 321 # check that none of the prev_outs are already spent in the main chain or in the current block
310 322 def not_spent
  323 + # if we received cached spents, use it
  324 + return @spent_outs_txins.empty? if @spent_outs_txins
  325 +
311 326 # find all spent txouts
312   - # OPTIMIZE: these could be fetched in one query for all transactions and cached
313   - next_ins = store.get_txins_for_txouts(tx.in.map.with_index {|txin, idx| [prev_txs[idx].hash, txin.prev_out_index] })
  327 + next_ins = store.get_txins_for_txouts(tx.in.map.with_index {|txin, idx| [txin.prev_out.reverse_hth, txin.prev_out_index] })
314 328  
315 329 # no txouts found spending these txins, we can safely return true
316 330 return true if next_ins.empty?