Packages
elixium_core
0.3.16
0.6.3
0.6.2
0.6.1
0.6.0
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.1
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.4
0.1.3
The core package for the Elixium blockchain, containing all the modules needed to run the chain
Current section
Files
Jump to
Current section
Files
lib/store/ledger.ex
defmodule Elixium.Store.Ledger do
alias Elixium.Block
alias Elixium.BlockEncoder
alias Elixium.Utilities
use Elixium.Store
@moduledoc """
Provides an interface for interacting with the blockchain stored within LevelDB. This
is where blocks are stored and fetched
"""
@store_dir ".chaindata"
@ets_name :chaindata
def initialize do
initialize(@store_dir)
:ets.new(@ets_name, [:ordered_set, :public, :named_table])
end
@doc """
Add a block to leveldb, indexing it by its hash (this is the most likely piece of data to be unique)
"""
def append_block(block) do
transact @store_dir do
&Exleveldb.put(&1, String.to_atom(block.hash), BlockEncoder.encode(block))
end
:ets.insert(@ets_name, {block.index, block.hash, block})
end
@spec drop_block(Block) :: none
def drop_block(block) do
transact @store_dir do
&Exleveldb.delete(&1, String.to_atom(block.hash))
end
:ets.delete(@ets_name, block.index)
end
@doc """
Given a block hash, return its contents
"""
@spec retrieve_block(String.t()) :: Block
def retrieve_block(hash) do
# Only check the store if we don't have this hash in our ETS cache
case :ets.match(@ets_name, {'_', hash, '$1'}) do
[] -> do_retrieve_block_from_store(hash)
[block] -> block
end
end
defp do_retrieve_block_from_store(hash) do
transact @store_dir do
fn ref ->
case Exleveldb.get(ref, hash) do
{:ok, block} -> BlockEncoder.decode(block)
err -> err
end
end
end
end
@doc """
Return the whole chain from leveldb
"""
def retrieve_chain do
chain =
transact @store_dir do
fn ref ->
ref
|> Exleveldb.map(fn {_, block} -> BlockEncoder.decode(block) end)
|> Enum.sort_by(& &1.index, &>=/2)
end
end
chain
end
@doc """
Hydrate ETS with our chain data
"""
def hydrate do
ets_hydrate = Enum.map(retrieve_chain(), &({&1.index, String.to_atom(&1.hash), &1}))
:ets.insert(@ets_name, ets_hydrate)
end
@doc """
Returns the most recent block on the chain
"""
@spec last_block :: Block
def last_block do
case :ets.last(@ets_name) do
:"$end_of_table" -> :err
key ->
[{_index, _key, block}] = :ets.lookup(@ets_name, key)
block
end
end
@doc """
Returns the block at a given index
"""
@spec block_at_height(integer) :: Block
def block_at_height(height) when is_integer(height) do
height =
height
|> :binary.encode_unsigned()
|> Utilities.zero_pad(4)
block_at_height(height)
end
def block_at_height(height) when is_binary(height) do
case :ets.lookup(@ets_name, height) do
[] -> :none
[{_index, _key, block}] -> block
end
end
@doc """
Returns the last N blocks in the chain
"""
@spec last_n_blocks(integer) :: list
def last_n_blocks(n, starting_at \\ :binary.decode_unsigned(last_block().index)) do
starting_at - (n - 1)
|> max(0)
|> Range.new(starting_at)
|> Enum.map(&block_at_height/1)
end
@doc """
Returns the number of blocks in the chain
"""
@spec count_blocks :: integer
def count_blocks, do: :ets.info(@ets_name, :size)
def empty? do
empty?(@store_dir)
end
end