Class: Bitcoin::Storage::Backends::SequelStore
- Inherits:
-
SequelStoreBase
- Object
- StoreBase
- SequelStoreBase
- Bitcoin::Storage::Backends::SequelStore
- Defined in:
- lib/bitcoin/storage/sequel/sequel_store.rb
Overview
Storage backend using Sequel to connect to arbitrary SQL databases. Inherits from StoreBase and implements its interface.
Constant Summary collapse
- DEFAULT_CONFIG =
{ # TODO mode: :full, # cache head block. only the instance that is updating the head should do this. cache_head: false, # store an index of tx.nhash values index_nhash: false }
Constants inherited from SequelStoreBase
Bitcoin::Storage::Backends::SequelStoreBase::SEQUEL_ADAPTERS
Constants inherited from StoreBase
Bitcoin::Storage::Backends::StoreBase::ADDRESS_TYPES, Bitcoin::Storage::Backends::StoreBase::MAIN, Bitcoin::Storage::Backends::StoreBase::ORPHAN, Bitcoin::Storage::Backends::StoreBase::SCRIPT_TYPES, Bitcoin::Storage::Backends::StoreBase::SIDE
Instance Attribute Summary collapse
-
#db ⇒ Object
sequel database connection.
Attributes inherited from StoreBase
Instance Method Summary collapse
-
#check_consistency(count = 1000) ⇒ Object
check data consistency of the top
countblocks. -
#connect ⇒ Object
connect to database.
-
#delete_tx(hash) ⇒ Object
delete transaction TODO: also delete blk_tx mapping.
-
#get_block(blk_hash) ⇒ Object
get block for given
blk_hash. -
#get_block_by_depth(depth) ⇒ Object
get block by given
depth. -
#get_block_by_id(block_id) ⇒ Object
get block by given
id. -
#get_block_by_prev_hash(prev_hash) ⇒ Object
get block by given
prev_hash. -
#get_block_by_tx(tx_hash) ⇒ Object
get block by given
tx_hash. -
#get_block_id_for_tx_id(tx_id) ⇒ Object
get block id in the main chain by given
tx_id. -
#get_depth ⇒ Object
get depth of MAIN chain.
-
#get_head ⇒ Object
get head block (highest block from the MAIN chain).
- #get_head_hash ⇒ Object
-
#get_idx_from_tx_hash(tx_hash) ⇒ Object
Grab the position of a tx in a given block.
-
#get_received(address) ⇒ Object
get total received of
addressaddress. -
#get_tx(tx_hash) ⇒ Object
get transaction for given
tx_hash. -
#get_tx_by_id(tx_id) ⇒ Object
get transaction by given
tx_id. -
#get_txin_for_txout(tx_hash, txout_idx) ⇒ Object
get corresponding Models::TxIn for the txout in transaction
tx_hashwith indextxout_idx. -
#get_txins_for_txouts(txouts) ⇒ Object
optimized version of Storage#get_txins_for_txouts.
- #get_txout_by_id(txout_id) ⇒ Object
-
#get_txout_for_txin(txin) ⇒ Object
get corresponding Models::TxOut for
txin. -
#get_txouts_for_hash160(hash160, type = :hash160, unconfirmed = false) ⇒ Object
get all Models::TxOut matching given
hash160. - #get_txouts_for_name_hash(hash) ⇒ Object
-
#get_txouts_for_pk_script(script) ⇒ Object
get all Models::TxOut matching given
script. -
#get_txs(tx_hashes) ⇒ Object
get array of txes with given
tx_hashes. -
#get_unconfirmed_tx ⇒ Object
get all unconfirmed Models::TxOut.
-
#has_block(blk_hash) ⇒ Object
check if block
blk_hashexists in the main chain. -
#has_tx(tx_hash) ⇒ Object
check if transaction
tx_hashexists. -
#initialize(config, *args) ⇒ SequelStore
constructor
create sequel store with given
config. -
#persist_addrs(addrs) ⇒ Object
bulk-store addresses and txout mappings.
-
#persist_block(blk, chain, depth, prev_work = 0) ⇒ Object
persist given block
blkto storage. - #reorg(new_side, new_main) ⇒ Object
-
#reset ⇒ Object
reset database; delete all data.
-
#store_tx(tx, validate = true) ⇒ Object
store transaction
tx. -
#store_txin(tx_id, txin, idx, p2sh_type = nil) ⇒ Object
store input
txin. -
#store_txout(tx_id, txout, idx, tx_hash = "") ⇒ Object
store output
txout. -
#tx_data(tx) ⇒ Object
prepare transaction data for storage.
-
#txin_data(tx_id, txin, idx, p2sh_type = nil) ⇒ Object
prepare txin data for storage.
-
#txout_data(tx_id, txout, idx, script_type) ⇒ Object
prepare txout data for storage.
-
#wrap_block(block) ⇒ Object
wrap given
blockinto Models::Block. -
#wrap_tx(transaction, block_id = nil, prefetched = {}) ⇒ Object
wrap given
transactioninto Models::Transaction. -
#wrap_txin(input) ⇒ Object
wrap given
inputinto Models::TxIn. -
#wrap_txout(output) ⇒ Object
wrap given
outputinto Models::TxOut.
Methods inherited from SequelStoreBase
#check_metadata, #init_store_connection, #migrate, #sqlite_pragmas
Methods inherited from StoreBase
#add_watched_address, #backend_name, #get_balance, #get_locator, #get_txouts_for_address, #get_unspent_txouts_for_address, #import, #in_sync?, #init_store_connection, #new_block, #new_tx, #parse_script, #push_notification, #rescan, #store_block, #subscribe, #update_block
Constructor Details
#initialize(config, *args) ⇒ SequelStore
create sequel store with given config
27 28 29 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 27 def initialize config, *args super config, *args end |
Instance Attribute Details
#db ⇒ Object
sequel database connection
13 14 15 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 13 def db @db end |
Instance Method Details
#check_consistency(count = 1000) ⇒ Object
check data consistency of the top count blocks. validates that
-
the block hash computed from the stored data is the same
-
the prev_hash is the same as the previous blocks’ hash
-
the merkle root computed from all transactions is correct
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 471 def check_consistency count = 1000 return if get_depth < 1 || count <= 0 depth = get_depth count = depth - 1 if count == -1 count = depth - 1 if count >= depth log.info { "Checking consistency of last #{count} blocks..." } prev_blk = get_block_by_depth(depth - count - 1) (depth - count).upto(depth).each do |depth| blk = get_block_by_depth(depth) raise "Block hash #{blk.depth} invalid!" unless blk.hash == blk.recalc_block_hash raise "Prev hash #{blk.depth} invalid!" unless blk.prev_block.reverse.hth == prev_blk.hash raise "Merkle root #{blk.depth} invalid!" unless blk.verify_mrkl_root print "#{blk.hash} #{blk.depth} OK\r" prev_blk = blk end log.info { "Last #{count} blocks are consistent." } end |
#connect ⇒ Object
connect to database
32 33 34 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 32 def connect super end |
#delete_tx(hash) ⇒ Object
delete transaction TODO: also delete blk_tx mapping
234 235 236 237 238 239 240 241 242 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 234 def delete_tx(hash) log.debug { "Deleting tx #{hash} since all its outputs are spent" } @db.transaction do tx = get_tx(hash) tx.in.each {|i| @db[:txin].where(:id => i.id).delete } tx.out.each {|o| @db[:txout].where(:id => o.id).delete } @db[:tx].where(:id => tx.id).delete end end |
#get_block(blk_hash) ⇒ Object
get block for given blk_hash
275 276 277 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 275 def get_block(blk_hash) wrap_block(@db[:blk][:hash => blk_hash.htb.blob]) end |
#get_block_by_depth(depth) ⇒ Object
get block by given depth
280 281 282 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 280 def get_block_by_depth(depth) wrap_block(@db[:blk][:depth => depth, :chain => MAIN]) end |
#get_block_by_id(block_id) ⇒ Object
get block by given id
299 300 301 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 299 def get_block_by_id(block_id) wrap_block(@db[:blk][:id => block_id]) end |
#get_block_by_prev_hash(prev_hash) ⇒ Object
get block by given prev_hash
285 286 287 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 285 def get_block_by_prev_hash(prev_hash) wrap_block(@db[:blk][:prev_hash => prev_hash.htb.blob, :chain => MAIN]) end |
#get_block_by_tx(tx_hash) ⇒ Object
get block by given tx_hash
290 291 292 293 294 295 296 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 290 def get_block_by_tx(tx_hash) tx = @db[:tx][:hash => tx_hash.htb.blob] return nil unless tx parent = @db[:blk_tx][:tx_id => tx[:id]] return nil unless parent wrap_block(@db[:blk][:id => parent[:blk_id]]) end |
#get_block_id_for_tx_id(tx_id) ⇒ Object
get block id in the main chain by given tx_id
304 305 306 307 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 304 def get_block_id_for_tx_id(tx_id) @db[:blk_tx].join(:blk, id: :blk_id) .where(tx_id: tx_id, chain: MAIN).first[:blk_id] rescue nil end |
#get_depth ⇒ Object
get depth of MAIN chain
266 267 268 269 270 271 272 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 266 def get_depth depth = (@config[:cache_head] && @head) ? @head.depth : @depth = @db[:blk].filter(:chain => MAIN).order(:depth).last[:depth] rescue nil return -1 unless depth depth end |
#get_head ⇒ Object
get head block (highest block from the MAIN chain)
255 256 257 258 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 255 def get_head (@config[:cache_head] && @head) ? @head : @head = wrap_block(@db[:blk].filter(:chain => MAIN).order(:depth).last) end |
#get_head_hash ⇒ Object
260 261 262 263 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 260 def get_head_hash (@config[:cache_head] && @head) ? @head.hash : @head = @db[:blk].filter(:chain => MAIN).order(:depth).last[:hash].hth end |
#get_idx_from_tx_hash(tx_hash) ⇒ Object
Grab the position of a tx in a given block
385 386 387 388 389 390 391 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 385 def get_idx_from_tx_hash(tx_hash) tx = @db[:tx][:hash => tx_hash.htb.blob] return nil unless tx parent = @db[:blk_tx][:tx_id => tx[:id]] return nil unless parent return parent[:idx] end |
#get_received(address) ⇒ Object
get total received of address address
490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 490 def get_received(address) return 0 unless Bitcoin.valid_address?(address) txouts = get_txouts_for_address(address) return 0 unless txouts.any? txouts.inject(0){ |m, out| m + out.value } # total = 0 # txouts.each do |txout| # tx = txout.get_tx # total += txout.value # end end |
#get_tx(tx_hash) ⇒ Object
get transaction for given tx_hash
310 311 312 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 310 def get_tx(tx_hash) wrap_tx(@db[:tx][:hash => tx_hash.htb.blob]) end |
#get_tx_by_id(tx_id) ⇒ Object
get transaction by given tx_id
329 330 331 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 329 def get_tx_by_id(tx_id) wrap_tx(@db[:tx][:id => tx_id]) end |
#get_txin_for_txout(tx_hash, txout_idx) ⇒ Object
get corresponding Models::TxIn for the txout in transaction tx_hash with index txout_idx
335 336 337 338 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 335 def get_txin_for_txout(tx_hash, txout_idx) tx_hash = tx_hash.htb_reverse.blob wrap_txin(@db[:txin][:prev_out => tx_hash, :prev_out_index => txout_idx]) end |
#get_txins_for_txouts(txouts) ⇒ Object
optimized version of Storage#get_txins_for_txouts
341 342 343 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 341 def get_txins_for_txouts(txouts) @db[:txin].filter([:prev_out, :prev_out_index] => txouts.map{|tx_hash, tx_idx| [tx_hash.htb_reverse.blob, tx_idx]}).map{|i| wrap_txin(i)} end |
#get_txout_by_id(txout_id) ⇒ Object
345 346 347 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 345 def get_txout_by_id(txout_id) wrap_txout(@db[:txout][:id => txout_id]) end |
#get_txout_for_txin(txin) ⇒ Object
get corresponding Models::TxOut for txin
350 351 352 353 354 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 350 def get_txout_for_txin(txin) tx = @db[:tx][:hash => txin.prev_out.reverse.blob] return nil unless tx wrap_txout(@db[:txout][:tx_idx => txin.prev_out_index, :tx_id => tx[:id]]) end |
#get_txouts_for_hash160(hash160, type = :hash160, unconfirmed = false) ⇒ Object
get all Models::TxOut matching given hash160
363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 363 def get_txouts_for_hash160(hash160, type = :hash160, unconfirmed = false) addr = @db[:addr][hash160: hash160, type: ADDRESS_TYPES.index(type)] return [] unless addr txouts = @db[:addr_txout].where(addr_id: addr[:id]) .map{|t| @db[:txout][id: t[:txout_id]] } .map{|o| wrap_txout(o) } unless unconfirmed txouts.select!{|o| @db[:blk][id: o.get_tx.blk_id][:chain] == MAIN rescue false } end txouts end |
#get_txouts_for_name_hash(hash) ⇒ Object
375 376 377 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 375 def get_txouts_for_name_hash(hash) @db[:names].filter(hash: hash).map {|n| get_txout_by_id(n[:txout_id]) } end |
#get_txouts_for_pk_script(script) ⇒ Object
get all Models::TxOut matching given script
357 358 359 360 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 357 def get_txouts_for_pk_script(script) txouts = @db[:txout].filter(:pk_script => script.blob).order(:id) txouts.map{|txout| wrap_txout(txout)} end |
#get_txs(tx_hashes) ⇒ Object
get array of txes with given tx_hashes
315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 315 def get_txs(tx_hashes) txs = db[:tx].filter(hash: tx_hashes.map{|h| h.htb.blob}) txs_ids = txs.map {|tx| tx[:id]} return [] if txs_ids.empty? # we fetch all needed block ids, inputs and outputs to avoid doing number of queries propertional to number of transactions block_ids = Hash[*db[:blk_tx].join(:blk, id: :blk_id).filter(tx_id: txs_ids, chain: 0).map {|b| [b[:tx_id], b[:blk_id]] }.flatten] inputs = db[:txin].filter(:tx_id => txs_ids).order(:tx_idx).map.group_by{ |txin| txin[:tx_id] } outputs = db[:txout].filter(:tx_id => txs_ids).order(:tx_idx).map.group_by{ |txout| txout[:tx_id] } txs.map {|tx| wrap_tx(tx, block_ids[tx[:id]], inputs: inputs[tx[:id]], outputs: outputs[tx[:id]]) } end |
#get_unconfirmed_tx ⇒ Object
get all unconfirmed Models::TxOut
380 381 382 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 380 def get_unconfirmed_tx @db[:unconfirmed].map{|t| wrap_tx(t)} end |
#has_block(blk_hash) ⇒ Object
check if block blk_hash exists in the main chain
245 246 247 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 245 def has_block(blk_hash) !!@db[:blk].where(:hash => blk_hash.htb.blob, :chain => 0).get(1) end |
#has_tx(tx_hash) ⇒ Object
check if transaction tx_hash exists
250 251 252 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 250 def has_tx(tx_hash) !!@db[:tx].where(:hash => tx_hash.htb.blob).get(1) end |
#persist_addrs(addrs) ⇒ Object
bulk-store addresses and txout mappings
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 128 def persist_addrs addrs addr_txouts, new_addrs = [], [] # find addresses that are already there existing_addr = {} addrs.each do |i, addr| hash160 = Bitcoin.hash160_from_address(addr) type = Bitcoin.address_type(addr) if existing = @db[:addr][hash160: hash160, type: ADDRESS_TYPES.index(type)] existing_addr[[hash160, type]] = existing[:id] end end # iterate over all txouts, grouped by hash160 addrs.group_by {|_, a| a }.each do |addr, txouts| hash160 = Bitcoin.hash160_from_address(addr) type = Bitcoin.address_type(addr) if existing_id = existing_addr[[hash160, type]] # link each txout to existing address txouts.each {|id, _| addr_txouts << [existing_id, id] } else # collect new address/txout mapping new_addrs << [[hash160, type], txouts.map {|id, _| id }] end end # insert all new addresses new_addr_ids = fast_insert(:addr, new_addrs.map {|hash160_and_type, txout_id| hash160, type = *hash160_and_type { hash160: hash160, type: ADDRESS_TYPES.index(type) } }, return_ids: true) # link each new txout to the new addresses new_addr_ids.each.with_index do |addr_id, idx| new_addrs[idx][1].each do |txout_id| addr_txouts << [addr_id, txout_id] end end # insert addr/txout links fast_insert(:addr_txout, addr_txouts.map {|addr_id, txout_id| { addr_id: addr_id, txout_id: txout_id }}) end |
#persist_block(blk, chain, depth, prev_work = 0) ⇒ Object
persist given block blk to storage.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 43 def persist_block blk, chain, depth, prev_work = 0 @db.transaction do attrs = { :hash => blk.hash.htb.blob, :depth => depth, :chain => chain, :version => blk.ver, :prev_hash => blk.prev_block.reverse.blob, :mrkl_root => blk.mrkl_root.reverse.blob, :time => blk.time, :bits => blk.bits, :nonce => blk.nonce, :blk_size => blk.to_payload.bytesize, :work => (prev_work + blk.block_work).to_s } attrs[:aux_pow] = blk.aux_pow.to_payload.blob if blk.aux_pow existing = @db[:blk].filter(:hash => blk.hash.htb.blob) if existing.any? existing.update attrs block_id = existing.first[:id] else block_id = @db[:blk].insert(attrs) blk_tx, new_tx, addrs, names = [], [], [], [] # store tx existing_tx = Hash[*@db[:tx].filter(hash: blk.tx.map {|tx| tx.hash.htb.blob }).map { |tx| [tx[:hash].hth, tx[:id]] }.flatten] blk.tx.each.with_index do |tx, idx| existing = existing_tx[tx.hash] existing ? blk_tx[idx] = existing : new_tx << [tx, idx] end new_tx_ids = fast_insert(:tx, new_tx.map {|tx, _| tx_data(tx) }, return_ids: true) new_tx_ids.each.with_index {|tx_id, idx| blk_tx[new_tx[idx][1]] = tx_id } fast_insert(:blk_tx, blk_tx.map.with_index {|id, idx| { blk_id: block_id, tx_id: id, idx: idx } }) # store txins fast_insert(:txin, new_tx.map.with_index {|tx, tx_idx| tx, _ = *tx tx.in.map.with_index {|txin, txin_idx| p2sh_type = nil if @config[:index_p2sh_type] && !txin.coinbase? && (script = tx.scripts[txin_idx]) && script.is_p2sh? p2sh_type = Bitcoin::Script.new(script.inner_p2sh_script).type end txin_data(new_tx_ids[tx_idx], txin, txin_idx, p2sh_type) } }.flatten) # store txouts txout_i = 0 txout_ids = fast_insert(:txout, new_tx.map.with_index {|tx, tx_idx| tx, _ = *tx tx.out.map.with_index {|txout, txout_idx| script_type, a, n = *parse_script(txout, txout_i, tx.hash, txout_idx) addrs += a; names += n; txout_i += 1 txout_data(new_tx_ids[tx_idx], txout, txout_idx, script_type) } }.flatten, return_ids: true) # store addrs persist_addrs addrs.map {|i, addr| [txout_ids[i], addr]} names.each {|i, script| store_name(script, txout_ids[i]) } end @head = wrap_block(attrs.merge(id: block_id)) if chain == MAIN @db[:blk].where(:prev_hash => blk.hash.htb.blob, :chain => ORPHAN).each do |b| log.debug { "connecting orphan #{b[:hash].hth}" } begin store_block(get_block(b[:hash].hth)) rescue SystemStackError EM.defer { store_block(get_block(b[:hash].hth)) } if EM.reactor_running? end end return depth, chain end end |
#reorg(new_side, new_main) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 115 def reorg new_side, new_main @db.transaction do @db[:blk].where(hash: new_side.map {|h| h.htb.blob }).update(chain: SIDE) new_main.each do |block_hash| unless @config[:skip_validation] get_block(block_hash).validator(self).validate(raise_errors: true) end @db[:blk].where(hash: block_hash.htb.blob).update(chain: MAIN) end end end |
#reset ⇒ Object
reset database; delete all data
37 38 39 40 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 37 def reset [:blk, :blk_tx, :tx, :txin, :txout, :addr, :addr_txout, :names].each {|table| @db[table].delete } @head = nil end |
#store_tx(tx, validate = true) ⇒ Object
store transaction tx
185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 185 def store_tx(tx, validate = true) @log.debug { "Storing tx #{tx.hash} (#{tx.to_payload.bytesize} bytes)" } tx.validator(self).validate(raise_errors: true) if validate @db.transaction do transaction = @db[:tx][:hash => tx.hash.htb.blob] return transaction[:id] if transaction tx_id = @db[:tx].insert(tx_data(tx)) tx.in.each_with_index {|i, idx| store_txin(tx_id, i, idx)} tx.out.each_with_index {|o, idx| store_txout(tx_id, o, idx, tx.hash)} tx_id end end |
#store_txin(tx_id, txin, idx, p2sh_type = nil) ⇒ Object
store input txin
212 213 214 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 212 def store_txin(tx_id, txin, idx, p2sh_type = nil) @db[:txin].insert(txin_data(tx_id, txin, idx, p2sh_type)) end |
#store_txout(tx_id, txout, idx, tx_hash = "") ⇒ Object
store output txout
224 225 226 227 228 229 230 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 224 def store_txout(tx_id, txout, idx, tx_hash = "") script_type, addrs, names = *parse_script(txout, idx, tx_hash, idx) txout_id = @db[:txout].insert(txout_data(tx_id, txout, idx, script_type)) persist_addrs addrs.map {|i, h| [txout_id, h] } names.each {|i, script| store_name(script, txout_id) } txout_id end |
#tx_data(tx) ⇒ Object
prepare transaction data for storage
174 175 176 177 178 179 180 181 182 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 174 def tx_data tx data = { hash: tx.hash.htb.blob, version: tx.ver, lock_time: tx.lock_time, coinbase: tx.in.size == 1 && tx.in[0].coinbase?, tx_size: tx.payload.bytesize } data[:nhash] = tx.nhash.htb.blob if @config[:index_nhash] data end |
#txin_data(tx_id, txin, idx, p2sh_type = nil) ⇒ Object
prepare txin data for storage
199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 199 def txin_data tx_id, txin, idx, p2sh_type = nil data = { tx_id: tx_id, tx_idx: idx, script_sig: txin.script_sig.blob, prev_out: txin.prev_out.blob, prev_out_index: txin.prev_out_index, sequence: txin.sequence.unpack("V")[0], } data[:p2sh_type] = SCRIPT_TYPES.index(p2sh_type) if @config[:index_p2sh_type] data end |
#txout_data(tx_id, txout, idx, script_type) ⇒ Object
prepare txout data for storage
217 218 219 220 221 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 217 def txout_data tx_id, txout, idx, script_type { tx_id: tx_id, tx_idx: idx, pk_script: txout.pk_script.blob, value: txout.value, type: script_type } end |
#wrap_block(block) ⇒ Object
wrap given block into Models::Block
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 394 def wrap_block(block) return nil unless block data = {:id => block[:id], :depth => block[:depth], :chain => block[:chain], :work => block[:work].to_i, :hash => block[:hash].hth, :size => block[:blk_size]} blk = Bitcoin::Storage::Models::Block.new(self, data) blk.ver = block[:version] blk.prev_block = block[:prev_hash].reverse blk.mrkl_root = block[:mrkl_root].reverse blk.time = block[:time].to_i blk.bits = block[:bits] blk.nonce = block[:nonce] blk.aux_pow = Bitcoin::P::AuxPow.new(block[:aux_pow]) if block[:aux_pow] blk_tx = db[:blk_tx].filter(blk_id: block[:id]).join(:tx, id: :tx_id).order(:idx) # fetch inputs and outputs for all transactions in the block to avoid additional queries for each transaction inputs = db[:txin].filter(:tx_id => blk_tx.map{ |tx| tx[:id] }).order(:tx_idx).map.group_by{ |txin| txin[:tx_id] } outputs = db[:txout].filter(:tx_id => blk_tx.map{ |tx| tx[:id] }).order(:tx_idx).map.group_by{ |txout| txout[:tx_id] } blk.tx = blk_tx.map { |tx| wrap_tx(tx, block[:id], inputs: inputs[tx[:id]], outputs: outputs[tx[:id]]) } blk.hash = block[:hash].hth blk end |
#wrap_tx(transaction, block_id = nil, prefetched = {}) ⇒ Object
wrap given transaction into Models::Transaction
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 422 def wrap_tx(transaction, block_id = nil, prefetched = {}) return nil unless transaction block_id ||= @db[:blk_tx].join(:blk, id: :blk_id) .where(tx_id: transaction[:id], chain: 0).first[:blk_id] rescue nil data = {id: transaction[:id], blk_id: block_id, size: transaction[:tx_size], idx: transaction[:idx]} tx = Bitcoin::Storage::Models::Tx.new(self, data) inputs = prefetched[:inputs] || db[:txin].filter(:tx_id => transaction[:id]).order(:tx_idx) inputs.each { |i| tx.add_in(wrap_txin(i)) } outputs = prefetched[:outputs] || db[:txout].filter(:tx_id => transaction[:id]).order(:tx_idx) outputs.each { |o| tx.add_out(wrap_txout(o)) } tx.ver = transaction[:version] tx.lock_time = transaction[:lock_time] tx.hash = transaction[:hash].hth tx end |
#wrap_txin(input) ⇒ Object
wrap given input into Models::TxIn
443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 443 def wrap_txin(input) return nil unless input data = { :id => input[:id], :tx_id => input[:tx_id], :tx_idx => input[:tx_idx], :p2sh_type => input[:p2sh_type] ? SCRIPT_TYPES[input[:p2sh_type]] : nil } txin = Bitcoin::Storage::Models::TxIn.new(self, data) txin.prev_out = input[:prev_out] txin.prev_out_index = input[:prev_out_index] txin.script_sig_length = input[:script_sig].bytesize txin.script_sig = input[:script_sig] txin.sequence = [input[:sequence]].pack("V") txin end |
#wrap_txout(output) ⇒ Object
wrap given output into Models::TxOut
457 458 459 460 461 462 463 464 465 |
# File 'lib/bitcoin/storage/sequel/sequel_store.rb', line 457 def wrap_txout(output) return nil unless output data = {:id => output[:id], :tx_id => output[:tx_id], :tx_idx => output[:tx_idx], :hash160 => output[:hash160], :type => SCRIPT_TYPES[output[:type]]} txout = Bitcoin::Storage::Models::TxOut.new(self, data) txout.value = output[:value] txout.pk_script = output[:pk_script] txout end |