diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index a4e6938a..de0b407e 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -7,82 +7,78 @@ defmodule Aecore.Chain.Worker do alias Aecore.Structures.Block alias Aecore.Chain.ChainState - alias Aecore.Utils.Blockchain.BlockValidation - alias Aecore.Utils.Blockchain.Difficulty alias Aecore.Txs.Pool.Worker, as: Pool + alias Aecore.Utils.Blockchain.BlockValidation alias Aecore.Peers.Worker, as: Peers use GenServer def start_link do - GenServer.start_link( - __MODULE__, - {[Block.genesis_block()], ChainState.calculate_block_state(Block.genesis_block().txs)}, - name: __MODULE__ - ) + GenServer.start_link(__MODULE__, {}, name: __MODULE__) end - def init(initial_state) do + def init(_) do + genesis_block_hash = BlockValidation.block_header_hash(Block.genesis_block().header) + + genesis_block_map = %{genesis_block_hash => Block.genesis_block()} + genesis_chain_state = ChainState.calculate_block_state(Block.genesis_block().txs) + latest_block_chain_state = %{genesis_block_hash => genesis_chain_state} + + initial_state = {genesis_block_map, latest_block_chain_state} + {:ok, initial_state} end @spec latest_block() :: %Block{} def latest_block() do - GenServer.call(__MODULE__, :latest_block) - end + latest_block_hashes = get_latest_block_chain_state() |> Map.keys() + latest_block_hash = case(length(latest_block_hashes)) do + 1 -> List.first(latest_block_hashes) + _ -> throw({:error, "multiple or none latest block hashes"}) + end - @spec get_prior_blocks_for_validity_check() :: tuple() - def get_prior_blocks_for_validity_check() do - GenServer.call(__MODULE__, :get_prior_blocks_for_validity_check) + get_block(latest_block_hash) end - @spec get_block_by_hash(term()) :: %Block{} - def get_block_by_hash(hash) do - GenServer.call(__MODULE__, {:get_block_by_hash, hash}) + @spec get_latest_block_chain_state() :: tuple() + def get_latest_block_chain_state() do + GenServer.call(__MODULE__, :get_latest_block_chain_state) end - @spec all_blocks() :: list() - def all_blocks() do - GenServer.call(__MODULE__, :all_blocks) + @spec get_block_by_hex_hash(term()) :: %Block{} + def get_block_by_hex_hash(hash) do + GenServer.call(__MODULE__, {:get_block_by_hex_hash, hash}) end - @spec add_block(%Block{}) :: :ok - def add_block(%Block{} = b) do - GenServer.call(__MODULE__, {:add_block, b}) + @spec get_block(term()) :: %Block{} + def get_block(hash) do + GenServer.call(__MODULE__, {:get_block, hash}) end - @spec chain_state() :: map() - def chain_state() do - GenServer.call(__MODULE__, :chain_state) + @spec get_blocks(binary(), integer()) :: :ok + def get_blocks(start_block_hash, size) do + Enum.reverse(get_blocks([], start_block_hash, size)) end - @spec get_blocks_for_difficulty_calculation() :: list() - def get_blocks_for_difficulty_calculation() do - GenServer.call(__MODULE__, :get_blocks_for_difficulty_calculation) + @spec add_block(%Block{}) :: :ok + def add_block(%Block{} = block) do + GenServer.call(__MODULE__, {:add_block, block}) end - def handle_call(:latest_block, _from, state) do - [lb | _] = elem(state, 0) - {:reply, lb, state} + @spec chain_state(binary()) :: map() + def chain_state(latest_block_hash) do + GenServer.call(__MODULE__, {:chain_state, latest_block_hash}) end - def handle_call(:get_prior_blocks_for_validity_check, _from, state) do - chain = elem(state, 0) - - if length(chain) == 1 do - [lb | _] = chain - {:reply, {lb, nil}, state} - else - [lb, prev | _] = chain - {:reply, {lb, prev}, state} - end + def handle_call(:get_latest_block_chain_state, _from, state) do + {_, latest_block_chain_state} = state + {:reply, latest_block_chain_state, state} end - def handle_call({:get_block_by_hash, hash}, _from, state) do - block = Enum.find(elem(state, 0), fn(block) -> - block.header - |> BlockValidation.block_header_hash() - |> Base.encode16() == hash end) + def handle_call({:get_block, block_hash}, _from, state) do + {block_map, _} = state + block = block_map[block_hash] + if(block != nil) do {:reply, block, state} else @@ -90,31 +86,58 @@ defmodule Aecore.Chain.Worker do end end - def handle_call(:all_blocks, _from, state) do - chain = elem(state, 0) - {:reply, chain, state} + def handle_call({:get_block_by_hex_hash, hash}, _from, state) do + {chain, _} = state + case(Enum.find(chain, fn{block_hash, _block} -> + block_hash |> Base.encode16() == hash end)) do + {_, block} -> + {:reply, block, state} + nil -> + {:reply, {:error, "Block not found"}, state} + end end - def handle_call({:add_block, %Block{} = b}, _from, state) do - {chain, prev_chain_state} = state - [prior_block | _] = chain - new_block_state = ChainState.calculate_block_state(b.txs) - new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_chain_state) + def handle_call({:add_block, %Block{} = block}, _from, state) do + {chain, chain_state} = state + prev_block_chain_state = chain_state[block.header.prev_hash] + new_block_state = ChainState.calculate_block_state(block.txs) + new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_block_chain_state) try do - BlockValidation.validate_block!(b, prior_block, new_chain_state) - Enum.each(b.txs, fn(tx) -> Pool.remove_transaction(tx) end) + BlockValidation.validate_block!(block, chain[block.header.prev_hash], new_chain_state) + + Enum.each(block.txs, fn(tx) -> Pool.remove_transaction(tx) end) + + {block_map, latest_block_chain_state} = state + block_hash = BlockValidation.block_header_hash(block.header) + updated_block_map = Map.put(block_map, block_hash, block) + has_prev_block = Map.has_key?(latest_block_chain_state, block.header.prev_hash) + + {deleted_latest_chain_state, prev_chain_state} = case has_prev_block do + true -> + prev_chain_state = Map.get(latest_block_chain_state, block.header.prev_hash) + {Map.delete(latest_block_chain_state, block.header.prev_hash), prev_chain_state} + false -> + {latest_block_chain_state, %{}} + end + + new_block_state = ChainState.calculate_block_state(block.txs) + new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_chain_state) + + updated_latest_block_chainstate = Map.put(deleted_latest_chain_state, block_hash, new_chain_state) + total_tokens = ChainState.calculate_total_tokens(new_chain_state) + Logger.info(fn -> - "Added block ##{b.header.height} with hash #{b.header + "Added block ##{block.header.height} with hash #{block.header |> BlockValidation.block_header_hash() |> Base.encode16()}, total tokens: #{total_tokens}" end) ## Block was validated, now we can send it to other peers - Peers.broadcast_to_all({:new_block, b}) + Peers.broadcast_to_all({:new_block, block}) - {:reply, :ok, {[b | chain], new_chain_state}} + {:reply, :ok, {updated_block_map, updated_latest_block_chainstate}} catch {:error, message} -> Logger.error(fn -> @@ -124,15 +147,37 @@ defmodule Aecore.Chain.Worker do end end - def handle_call(:chain_state, _from, state) do - chain_state = elem(state, 1) - {:reply, chain_state, state} + def handle_call({:chain_state, latest_block_hash}, _from, state) do + {_, chain_state} = state + {:reply, chain_state[latest_block_hash], state} end - def handle_call(:get_blocks_for_difficulty_calculation, _from, state) do - chain = elem(state, 0) - number_of_blocks = Difficulty.get_number_of_blocks() - blocks_for_difficulty_calculation = Enum.take(chain, number_of_blocks) - {:reply, blocks_for_difficulty_calculation, state} + def chain_state() do + latest_block = latest_block() + latest_block_hash = BlockValidation.block_header_hash(latest_block.header) + chain_state(latest_block_hash) + end + + def all_blocks() do + latest_block_obj = latest_block() + latest_block_hash = BlockValidation.block_header_hash(latest_block_obj.header) + get_blocks(latest_block_hash, latest_block_obj.header.height) + end + + defp get_blocks(blocks_acc, next_block_hash, size) do + cond do + size > 0 -> + case(GenServer.call(__MODULE__, {:get_block, next_block_hash})) do + {:error, _} -> blocks_acc + block -> + updated_block_acc = [block | blocks_acc] + prev_block_hash = block.header.prev_hash + next_size = size - 1 + + get_blocks(updated_block_acc, prev_block_hash, next_size) + end + true -> + blocks_acc + end end end diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index 4ec3175d..106293be 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -41,7 +41,7 @@ defmodule Aecore.Miner.Worker do ## Idle ## def idle({:call, from}, :start, _data) do - IO.puts("Mining resuming by user") + IO.puts("Mining resumed by user") GenStateMachine.cast(__MODULE__, :mine) {:next_state, :running, 0, [{:reply, from, :ok}]} end @@ -78,7 +78,7 @@ defmodule Aecore.Miner.Worker do end def running({:call, from}, :suspend, data) do - IO.puts("Mined stop by user") + IO.puts("Mining stopped by user") {:next_state, :idle, data, [{:reply, from, :ok}]} end @@ -106,52 +106,65 @@ defmodule Aecore.Miner.Worker do ## Internal @spec mine_next_block(integer()) :: :ok | :error defp mine_next_block(start_nonce) do - chain_state = Chain.chain_state() + latest_block = Chain.latest_block() + latest_block_hash = BlockValidation.block_header_hash(latest_block.header) + chain_state = Chain.chain_state(latest_block_hash) txs_list = Map.values(Pool.get_pool()) ordered_txs_list = Enum.sort(txs_list, fn(tx1, tx2) -> tx1.data.nonce < tx2.data.nonce end) - blocks_for_difficulty_calculation = Chain.get_blocks_for_difficulty_calculation() - {latest_block, previous_block} = Chain.get_prior_blocks_for_validity_check() - - BlockValidation.validate_block!(latest_block, previous_block, chain_state) - - valid_txs = BlockValidation.filter_invalid_transactions_chainstate(ordered_txs_list, chain_state) - {_, pubkey} = Keys.pubkey() - valid_txs = [get_coinbase_transaction(pubkey) | valid_txs] - root_hash = BlockValidation.calculate_root_hash(valid_txs) - - new_block_state = ChainState.calculate_block_state(valid_txs) - new_chain_state = ChainState.calculate_chain_state(new_block_state, chain_state) - chain_state_hash = ChainState.calculate_chain_state_hash(new_chain_state) - - latest_block_hash = BlockValidation.block_header_hash(latest_block.header) - - difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) - - unmined_header = - Header.create( - latest_block.header.height + 1, - latest_block_hash, - root_hash, - chain_state_hash, - difficulty, - 0, #start from nonce 0, will be incremented in mining - Block.current_block_version() - ) - Logger.debug("start nonce #{start_nonce}. Final nonce = #{start_nonce + @nonce_per_cycle}") - case Cuckoo.generate(%{unmined_header - | nonce: start_nonce + @nonce_per_cycle}) do - {:ok, mined_header} -> - block = %Block{header: mined_header, txs: valid_txs} - Chain.add_block(block) - Logger.info(fn -> - "Mined block ##{block.header.height}, difficulty target #{block.header.difficulty_target}, nonce #{block.header.nonce}" - end) - {:block_found, 0} - + blocks_for_difficulty_calculation = Chain.get_blocks(latest_block_hash, Difficulty.get_number_of_blocks()) + previous_block = cond do + latest_block == Block.genesis_block() -> nil + true -> + blocks = Chain.get_blocks(latest_block_hash, 2) + Enum.at(blocks, 1) + end + try do + BlockValidation.validate_block!(latest_block, previous_block, chain_state) + + valid_txs = BlockValidation.filter_invalid_transactions_chainstate(ordered_txs_list, chain_state) + {_, pubkey} = Keys.pubkey() + valid_txs = [get_coinbase_transaction(pubkey) | valid_txs] + root_hash = BlockValidation.calculate_root_hash(valid_txs) + + new_block_state = ChainState.calculate_block_state(valid_txs) + new_chain_state = ChainState.calculate_chain_state(new_block_state, chain_state) + chain_state_hash = ChainState.calculate_chain_state_hash(new_chain_state) + + latest_block_hash = BlockValidation.block_header_hash(latest_block.header) + + difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) + + unmined_header = + Header.create( + latest_block.header.height + 1, + latest_block_hash, + root_hash, + chain_state_hash, + difficulty, + 0, #start from nonce 0, will be incremented in mining + Block.current_block_version() + ) + Logger.debug("start nonce #{start_nonce}. Final nonce = #{start_nonce + @nonce_per_cycle}") + case Cuckoo.generate(%{unmined_header + | nonce: start_nonce + @nonce_per_cycle}) do + {:ok, mined_header} -> + block = %Block{header: mined_header, txs: valid_txs} + Logger.info(fn -> + "Mined block ##{block.header.height}, difficulty target #{block.header.difficulty_target}, nonce #{block.header.nonce}" + end) + Chain.add_block(block) + {:block_found, 0} + + {:error, _message} -> + {:no_block_found, start_nonce + @nonce_per_cycle} + end + catch {:error, _message} -> - {:no_block_found, start_nonce + @nonce_per_cycle} + Logger.error(fn -> + "Failed to mine block" + end) end end end diff --git a/apps/aecore/lib/aecore/peers/worker.ex b/apps/aecore/lib/aecore/peers/worker.ex index e4076df0..f6f98250 100644 --- a/apps/aecore/lib/aecore/peers/worker.ex +++ b/apps/aecore/lib/aecore/peers/worker.ex @@ -42,7 +42,7 @@ defmodule Aecore.Peers.Worker do @spec genesis_block_header_hash() :: term() def genesis_block_header_hash() do - Block.genesis_header() + Block.genesis_block().header |> BlockValidation.block_header_hash() |> Base.encode16() end @@ -134,10 +134,6 @@ defmodule Aecore.Peers.Worker do end ## Internal functions - defp send_to_peers(_uri, _data, []) do - Logger.warn("Empty peers list") - end - defp send_to_peers(uri, data, peers) do for peer <- peers do HttpClient.post(peer, data, uri) diff --git a/apps/aecore/lib/aecore/structures/block.ex b/apps/aecore/lib/aecore/structures/block.ex index 182124cf..cd4d31a7 100644 --- a/apps/aecore/lib/aecore/structures/block.ex +++ b/apps/aecore/lib/aecore/structures/block.ex @@ -18,8 +18,7 @@ defmodule Aecore.Structures.Block do @current_block_version end - @spec genesis_header() :: Header.header() - def genesis_header() do + defp genesis_header() do h = Application.get_env(:aecore, :pow)[:genesis_header] struct(Aecore.Structures.Header, h) end diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 93a4e0a6..695f81af 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -54,7 +54,7 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do end @spec block_header_hash(Header.header()) :: binary() - def block_header_hash(header) do + def block_header_hash(%Header{} = header) do block_header_bin = :erlang.term_to_binary(header) :crypto.hash(:sha256, block_header_bin) end diff --git a/apps/aecore/test/aecore_chain_test.exs b/apps/aecore/test/aecore_chain_test.exs index 63a51210..8d105392 100644 --- a/apps/aecore/test/aecore_chain_test.exs +++ b/apps/aecore/test/aecore_chain_test.exs @@ -21,26 +21,35 @@ defmodule AecoreChainTest do test "add block" do Miner.resume() Miner.suspend() + latest_block = Chain.latest_block() - chain_state = Chain.chain_state() + latest_block_hash = BlockValidation.block_header_hash(latest_block.header) + + chain_state = Chain.chain_state(latest_block_hash) new_block_state = ChainState.calculate_block_state([]) new_chain_state = ChainState.calculate_chain_state(new_block_state, chain_state) new_chain_state_hash = ChainState.calculate_chain_state_hash(new_chain_state) - prev_block_hash = BlockValidation.block_header_hash(latest_block.header) + block = %Block{header: %Header{height: latest_block.header.height + 1, - prev_hash: prev_block_hash, + prev_hash: latest_block_hash, txs_hash: <<0::256>>,chain_state_hash: new_chain_state_hash, difficulty_target: 1, nonce: 0, timestamp: System.system_time(:milliseconds), version: 1}, txs: []} {:ok, nbh} = Aecore.Pow.Cuckoo.generate(block.header) block = %{block | header: nbh} - {latest_block, previous_block} = Chain.get_prior_blocks_for_validity_check() + + latest_block = Chain.latest_block() + latest_block_hash = BlockValidation.block_header_hash(latest_block.header) + latest_block_hash_hex = latest_block_hash |> Base.encode16() + [latest_block | [previous_block | []]] = Chain.get_blocks(latest_block_hash, 2) + + assert latest_block == Chain.get_block_by_hex_hash(latest_block_hash_hex) assert previous_block.header.height + 1 == latest_block.header.height - assert BlockValidation.validate_block!(latest_block, previous_block, - Chain.chain_state) + assert BlockValidation.validate_block!(latest_block, previous_block, Chain.chain_state()) assert :ok = Chain.add_block(block) assert latest_block = Chain.latest_block() assert latest_block.header.height == block.header.height + length = length(Chain.all_blocks()) assert length > 1 end diff --git a/apps/aecore/test/aecore_hashcash_test.exs b/apps/aecore/test/aecore_hashcash_test.exs index c94b5398..2eab6f99 100644 --- a/apps/aecore/test/aecore_hashcash_test.exs +++ b/apps/aecore/test/aecore_hashcash_test.exs @@ -4,22 +4,23 @@ defmodule HashcashTest do alias Aecore.Pow.Hashcash alias Aecore.Structures.Header + alias Aecore.Structures.Block - @tag timeout: 10000000 - test "successfull test" do - header = - %Header{ - height: 0, - prev_hash: <<0::256>>, - txs_hash: <<0::256>>, - chain_state_hash: <<0 :: 256>>, - timestamp: 1_507_275_094_308, - nonce: 19, - version: @genesis_block_version, - difficulty_target: 1 - } - start_nonce = 0 - assert {:ok, mined_header} = Hashcash.generate(header, start_nonce) - assert :true = Hashcash.verify(mined_header) - end + #@tag timeout: 10000000 + #test "successfull test" do + # header = + # %Header{ + # height: 0, + # prev_hash: <<0::256>>, + # txs_hash: <<0::256>>, + # chain_state_hash: <<0 :: 256>>, + # timestamp: 1_507_275_094_308, + # nonce: 19, + # version: Block.genesis_block().header.version, + # difficulty_target: 1 + # } + # start_nonce = 0 + # assert {:ok, mined_header} = Hashcash.generate(header, start_nonce) + # assert :true = Hashcash.verify(mined_header) + #end end diff --git a/apps/aecore/test/aecore_keys_test.exs b/apps/aecore/test/aecore_keys_test.exs index 0dbe557f..725485a8 100644 --- a/apps/aecore/test/aecore_keys_test.exs +++ b/apps/aecore/test/aecore_keys_test.exs @@ -20,7 +20,7 @@ defmodule AecoreKeysTest do test "sign transaction" do {:ok, to_account} = Keys.pubkey() - assert {:ok, _} = Keys.sign_tx(to_account, 5, Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1) + assert {:ok, _} = Keys.sign_tx(to_account, 5, Map.get(Chain.chain_state(), to_account, %{nonce: 0}).nonce + 1) end test "check pubkey length" do diff --git a/apps/aecore/test/aecore_pow_cuckoo_test.exs b/apps/aecore/test/aecore_pow_cuckoo_test.exs index f16c60e0..b47836dc 100644 --- a/apps/aecore/test/aecore_pow_cuckoo_test.exs +++ b/apps/aecore/test/aecore_pow_cuckoo_test.exs @@ -10,7 +10,7 @@ defmodule AecoreCuckooTest do """ @tag timeout: 1000000000 test "Generate with a winning nonce and high target threshold, verify it" do - block_header = %{Block.genesis_header() | pow_evidence: nil } + block_header = %{Block.genesis_block().header | pow_evidence: nil } {t1, res} = :timer.tc(Cuckoo, :generate, [block_header]) diff --git a/apps/aecore/test/aecore_tx_test.exs b/apps/aecore/test/aecore_tx_test.exs index 58120662..abc6bdeb 100644 --- a/apps/aecore/test/aecore_tx_test.exs +++ b/apps/aecore/test/aecore_tx_test.exs @@ -15,7 +15,7 @@ defmodule AecoreTxTest do test "create and verify a signed tx" do {:ok, to_account} = Keys.pubkey() - {:ok, tx} = Keys.sign_tx(to_account, 5, Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1) + {:ok, tx} = Keys.sign_tx(to_account, 5, Map.get(Chain.chain_state(), to_account, %{nonce: 0}).nonce + 1) assert :true = Keys.verify_tx(tx) end diff --git a/apps/aecore/test/aecore_txs_pool_test.exs b/apps/aecore/test/aecore_txs_pool_test.exs index 7eaf05b8..b6ea4f7a 100644 --- a/apps/aecore/test/aecore_txs_pool_test.exs +++ b/apps/aecore/test/aecore_txs_pool_test.exs @@ -17,10 +17,8 @@ defmodule AecoreTxsPoolTest do @tag timeout: 1000000000 test "add transaction, remove it and get pool" do {:ok, to_account} = Keys.pubkey() - {:ok, tx1} = Keys.sign_tx(to_account, 5, - Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1) - {:ok, tx2} = Keys.sign_tx(to_account, 5, - Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1) + {:ok, tx1} = Keys.sign_tx(to_account, 5, Map.get(Chain.chain_state(), to_account, %{nonce: 0}).nonce + 1) + {:ok, tx2} = Keys.sign_tx(to_account, 5, Map.get(Chain.chain_state(), to_account, %{nonce: 0}).nonce + 1) Miner.resume() Miner.suspend() assert :ok = Pool.add_transaction(tx1) @@ -29,8 +27,8 @@ defmodule AecoreTxsPoolTest do assert Enum.count(Pool.get_pool()) == 1 Miner.resume() Miner.suspend() - assert length(Chain.all_blocks) > 1 - assert Enum.count(Chain.latest_block.txs) == 2 + assert length(Chain.all_blocks()) > 1 + assert Enum.count(Chain.latest_block().txs) == 2 assert Enum.count(Pool.get_pool()) == 0 end diff --git a/apps/aehttpserver/web/controllers/block_controller.ex b/apps/aehttpserver/web/controllers/block_controller.ex index d880a0b2..e58dafdc 100644 --- a/apps/aehttpserver/web/controllers/block_controller.ex +++ b/apps/aehttpserver/web/controllers/block_controller.ex @@ -6,7 +6,7 @@ defmodule Aehttpserver.BlockController do alias Aecore.Structures.Block def show(conn, params) do - block = Chain.get_block_by_hash(params["hash"]) + block = Chain.get_block_by_hex_hash(params["hash"]) case (block) do %Block{} -> block_hex_values = Serialization.block(block, :serialize) diff --git a/apps/aehttpserver/web/controllers/info_controller.ex b/apps/aehttpserver/web/controllers/info_controller.ex index fe94c929..440fa98e 100644 --- a/apps/aehttpserver/web/controllers/info_controller.ex +++ b/apps/aehttpserver/web/controllers/info_controller.ex @@ -12,7 +12,7 @@ defmodule Aehttpserver.InfoController do |> BlockValidation.block_header_hash() |> Base.encode16() - genesis_block_header = Block.genesis_header() + genesis_block_header = Block.genesis_block().header genesis_block_hash = genesis_block_header |> BlockValidation.block_header_hash() |> Base.encode16()