class Tapyrus::Store::SPVChain

Attributes

db[R]
logger[R]

Public Class Methods

new(db = Tapyrus::Store::DB::LevelDB.new, genesis: nil) click to toggle source

initialize spv chain @param db @param genesis genesis block

# File lib/tapyrus/store/spv_chain.rb, line 19
def initialize(db = Tapyrus::Store::DB::LevelDB.new, genesis: nil)
  raise ArgumentError, 'genesis block should be specified.' unless genesis
  @db = db # TODO multiple db switch
  @logger = Tapyrus::Logger.create(:debug)
  initialize_block(genesis)
end

Public Instance Methods

add_agg_pubkey(active_height, agg_pubkey) click to toggle source

Add aggregated public key. @param [Integer] active_height @param [String] agg_pubkey aggregated public key with hex format.

# File lib/tapyrus/store/spv_chain.rb, line 94
def add_agg_pubkey(active_height, agg_pubkey)
  db.add_agg_pubkey(active_height, agg_pubkey)
end
agg_pubkeys() click to toggle source

get aggregated public key keys. @return [Array[Array(height, agg_pubkey)]] the list of public keys.

# File lib/tapyrus/store/spv_chain.rb, line 100
def agg_pubkeys
  db.agg_pubkeys
end
append_header(header) click to toggle source

append block header to chain. @param [Tapyrus::BlockHeader] header a block header. @return [Tapyrus::Store::ChainEntry] appended block header entry.

# File lib/tapyrus/store/spv_chain.rb, line 49
def append_header(header)
  logger.info("append header #{header.block_id}")
  best_block = latest_block
  current_height = best_block.height
  unless header.valid?(db.agg_pubkey_with_height(current_height + 1))
    raise "this header is invalid. #{header.block_hash}"
  end
  if best_block.block_hash == header.prev_hash
    entry = Tapyrus::Store::ChainEntry.new(header, current_height + 1)
    db.save_entry(entry)
    entry
  else
    unless find_entry_by_hash(header.block_hash)
      # TODO implements recovery process
      raise "header's previous hash(#{header.prev_hash}) does not match current best block's(#{best_block.block_hash})."
    end
  end
end
find_entry_by_hash(hash) click to toggle source

find block entry with the specified hash

# File lib/tapyrus/store/spv_chain.rb, line 40
def find_entry_by_hash(hash)
  payload = db.get_entry_payload_from_hash(hash)
  return nil unless payload
  ChainEntry.parse_from_payload(payload)
end
find_entry_by_height(height) click to toggle source

find block entry with the specified height.

# File lib/tapyrus/store/spv_chain.rb, line 35
def find_entry_by_height(height)
  find_entry_by_hash(db.get_hash_from_height(height))
end
latest_block() click to toggle source

get latest block in the store. @return

# File lib/tapyrus/store/spv_chain.rb, line 28
def latest_block
  hash = db.best_hash
  return nil unless hash
  find_entry_by_hash(hash)
end
mtp(hash) click to toggle source

get median time past for specified block hash @param [String] hash the block hash. @return [Integer] the median time past value.

# File lib/tapyrus/store/spv_chain.rb, line 78
def mtp(hash)
  time = []
  Tapyrus::MEDIAN_TIME_SPAN.times do
    entry = find_entry_by_hash(hash)
    break unless entry

    time << entry.header.time
    hash = entry.header.prev_hash
  end
  time.sort!
  time[time.size / 2]
end
next_hash(hash) click to toggle source

get next block hash for specified hash @param [String] hash the block hash(little endian) @return [String] the next block hash. If it does not exist yet, return nil.

# File lib/tapyrus/store/spv_chain.rb, line 71
def next_hash(hash)
  db.next_hash(hash)
end

Private Instance Methods

initialize_block(genesis) click to toggle source

if database is empty, put genesis block. @param [Tapyrus::Block] genesis genesis block

# File lib/tapyrus/store/spv_chain.rb, line 108
def initialize_block(genesis)
  db.save_entry(ChainEntry.new(genesis.header, 0)) unless latest_block
end