From 0afdf0d145738be9fe09c5d7ed3cbbce35d3a8cd Mon Sep 17 00:00:00 2001 From: Nedelcho Delchev Date: Mon, 20 Nov 2017 18:06:29 +0200 Subject: [PATCH 1/4] Validation for difficulty target --- apps/aecore/lib/aecore/chain/worker.ex | 20 ++++++++++++++--- apps/aecore/lib/aecore/miner/worker.ex | 22 ++++++++++--------- .../utils/blockchain/block_validation.ex | 10 +++++++-- .../lib/aecore/utils/blockchain/difficulty.ex | 9 +------- apps/aecore/test/aecore_chain_test.exs | 15 +++++++++---- apps/aecore/test/aecore_hashcash_test.exs | 6 ++--- apps/aecore/test/aecore_validation_test.exs | 4 +++- .../web/controllers/block_controller.ex | 2 +- 8 files changed, 56 insertions(+), 32 deletions(-) diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index f1a9a541..7a9bdf5f 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -11,6 +11,7 @@ defmodule Aecore.Chain.Worker do alias Aecore.Utils.Blockchain.BlockValidation alias Aecore.Peers.Worker, as: Peers alias Aecore.Utils.Persistence + alias Aecore.Utils.Blockchain.Difficulty use GenServer @@ -67,8 +68,23 @@ defmodule Aecore.Chain.Worker do Enum.reverse(get_blocks([], start_block_hash, size)) end + @spec add_validated_block(%Block{}) :: :ok + def add_validated_block(%Block{} = block) do + latest_block = latest_block() + + prev_block_chain_state = chain_state() + new_block_state = ChainState.calculate_block_state(block.txs) + new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_block_chain_state) + + latest_header_hash = BlockValidation.block_header_hash(latest_block.header) + blocks_for_difficulty_calculation = get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) + + BlockValidation.validate_block!(block, latest_block, new_chain_state, blocks_for_difficulty_calculation) + add_block(block) + end + @spec add_block(%Block{}) :: :ok - def add_block(%Block{} = block) do + defp add_block(%Block{} = block) do GenServer.call(__MODULE__, {:add_block, block}) end @@ -137,8 +153,6 @@ defmodule Aecore.Chain.Worker do new_block_txs_index = calculate_block_acc_txs_info(block) new_txs_index = update_txs_index(txs_index, new_block_txs_index) try do - BlockValidation.validate_block!(block, block_map[block.header.prev_hash], new_chain_state) - Enum.each(block.txs, fn(tx) -> Pool.remove_transaction(tx) end) block_hash = BlockValidation.block_header_hash(block.header) diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index d694615b..3ff7a3a6 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -138,7 +138,17 @@ defmodule Aecore.Miner.Worker do Enum.at(blocks, 1) end try do - BlockValidation.validate_block!(latest_block, previous_block, chain_state) + + BlockValidation.validate_block!(latest_block, previous_block, chain_state, blocks_for_difficulty_calculation) + + catch + {:error, _message} -> + Logger.error(fn -> + "Failed to mine block" + end) + end + + difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) valid_txs = BlockValidation.filter_invalid_transactions_chainstate(ordered_txs_list, chain_state) {_, pubkey} = Keys.pubkey() @@ -152,8 +162,6 @@ defmodule Aecore.Miner.Worker do 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, @@ -172,17 +180,11 @@ defmodule Aecore.Miner.Worker do Logger.info(fn -> "Mined block ##{block.header.height}, difficulty target #{block.header.difficulty_target}, nonce #{block.header.nonce}" end) - Chain.add_block(block) + Chain.add_validated_block(block) {:block_found, 0} {:error, _message} -> {:no_block_found, start_nonce + @nonce_per_cycle} end - catch - {:error, _message} -> - Logger.error(fn -> - "Failed to mine block" - end) - end end end diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 15d6a70b..0f842cec 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -7,9 +7,10 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do alias Aecore.Structures.Header alias Aecore.Structures.SignedTx alias Aecore.Chain.ChainState + alias Aecore.Utils.Blockchain.Difficulty - @spec validate_block!(Block.block(), Block.block(), map()) :: {:error, term()} | :ok - def validate_block!(new_block, previous_block, chain_state) do + @spec validate_block!(Block.block(), Block.block(), map(), list()) :: {:error, term()} | :ok + def validate_block!(new_block, previous_block, chain_state, blocks_for_difficulty_calculation) do is_genesis = new_block == Block.genesis_block() && previous_block == nil chain_state_hash = ChainState.calculate_chain_state_hash(chain_state) @@ -19,6 +20,8 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do coinbase_transactions_sum = sum_coinbase_transactions(new_block) total_fees = Miner.calculate_total_fees(new_block.txs) + difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) + cond do # do not check previous block hash for genesis block, there is none !(is_genesis || check_prev_hash(new_block, previous_block)) -> @@ -49,6 +52,9 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do new_block.header.version != Block.current_block_version() -> throw({:error, "Invalid block version"}) + difficulty != new_block.header.difficulty_target -> + throw({:error, "Invalid block difficulty"}) + true -> :ok end diff --git a/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex b/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex index e0e23fa1..197c3569 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex @@ -1,5 +1,4 @@ defmodule Aecore.Utils.Blockchain.Difficulty do - alias Aecore.Chain.Worker, as: Chain @number_of_blocks 100 @max_difficulty_change 2 @@ -9,13 +8,7 @@ defmodule Aecore.Utils.Blockchain.Difficulty do @number_of_blocks end - @spec calculate_next_difficulty :: integer() - def calculate_next_difficulty() do - Chain.get_blocks_for_difficulty_calculation() - |> calculate_next_difficulty() - end - - @spec calculate_next_difficulty :: integer() + @spec calculate_next_difficulty(list()) :: integer() def calculate_next_difficulty(list) do [latest_block | _] = list diff --git a/apps/aecore/test/aecore_chain_test.exs b/apps/aecore/test/aecore_chain_test.exs index c387739a..0517d395 100644 --- a/apps/aecore/test/aecore_chain_test.exs +++ b/apps/aecore/test/aecore_chain_test.exs @@ -11,6 +11,7 @@ defmodule AecoreChainTest do alias Aecore.Utils.Blockchain.BlockValidation alias Aecore.Chain.Worker, as: Chain alias Aecore.Miner.Worker, as: Miner + alias Aecore.Utils.Blockchain.Difficulty setup do Chain.start_link([]) @@ -32,21 +33,27 @@ defmodule AecoreChainTest do block = %Block{header: %Header{height: latest_block.header.height + 1, prev_hash: latest_block_hash, - txs_hash: <<0::256>>,chain_state_hash: new_chain_state_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: []} + timestamp: System.system_time(:milliseconds), + version: 1}, txs: []} {:ok, nbh} = Aecore.Pow.Cuckoo.generate(block.header) block = %{block | header: nbh} latest_block = Chain.latest_block() latest_block_hash = BlockValidation.block_header_hash(latest_block.header) + blocks_for_difficulty_calculation = Chain.get_blocks(latest_block_hash, + Difficulty.get_number_of_blocks) 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 :ok = Chain.add_block(block) + assert BlockValidation.validate_block!(latest_block, previous_block, + Chain.chain_state(), + blocks_for_difficulty_calculation) + assert :ok = Chain.add_validated_block(block) assert latest_block = Chain.latest_block() assert latest_block.header.height == block.header.height diff --git a/apps/aecore/test/aecore_hashcash_test.exs b/apps/aecore/test/aecore_hashcash_test.exs index 2eab6f99..5a7b2760 100644 --- a/apps/aecore/test/aecore_hashcash_test.exs +++ b/apps/aecore/test/aecore_hashcash_test.exs @@ -2,9 +2,9 @@ defmodule HashcashTest do use ExUnit.Case doctest Aecore.Pow.Hashcash - alias Aecore.Pow.Hashcash - alias Aecore.Structures.Header - alias Aecore.Structures.Block + # alias Aecore.Pow.Hashcash + # alias Aecore.Structures.Header + # alias Aecore.Structures.Block #@tag timeout: 10000000 #test "successfull test" do diff --git a/apps/aecore/test/aecore_validation_test.exs b/apps/aecore/test/aecore_validation_test.exs index bd93bd67..02e509f1 100644 --- a/apps/aecore/test/aecore_validation_test.exs +++ b/apps/aecore/test/aecore_validation_test.exs @@ -49,7 +49,9 @@ defmodule AecoreValidationTest do txs_hash: <<0::256>>, version: 1}, txs: []} - assert BlockValidation.validate_block!(new_block,prev_block, %{}) == :ok + blocks_for_difficulty_calculation = [new_block, prev_block] + assert BlockValidation.validate_block!(new_block, prev_block, %{}, + blocks_for_difficulty_calculation) == :ok end test "validate transactions in a block" do diff --git a/apps/aehttpserver/web/controllers/block_controller.ex b/apps/aehttpserver/web/controllers/block_controller.ex index e58dafdc..143c4994 100644 --- a/apps/aehttpserver/web/controllers/block_controller.ex +++ b/apps/aehttpserver/web/controllers/block_controller.ex @@ -23,7 +23,7 @@ defmodule Aehttpserver.BlockController do block = Aecore.Utils.Serialization.block(map, :deserialize) - Aecore.Chain.Worker.add_block(block) + Aecore.Chain.Worker.add_validated_block(block) json conn, %{ok: "new block received"} end end From 62192881148542d13847d71efe8d7ecbc4cb5cf3 Mon Sep 17 00:00:00 2001 From: Nedelcho Delchev Date: Tue, 21 Nov 2017 11:40:05 +0200 Subject: [PATCH 2/4] Fixed message display and function names --- README.md | 24 +++++++++---------- apps/aecore/lib/aecore/chain/worker.ex | 14 +++++------ apps/aecore/lib/aecore/miner/worker.ex | 6 ++--- apps/aecore/test/aecore_chain_test.exs | 2 +- .../web/controllers/block_controller.ex | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 0645930e..1b1f19e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # **Elixir blockchain research** -This is an elixir implementation of a basic blockchain. We aim to keep the blockchain as simple as possible and to research and experiment with different technologies +This is an elixir implementation of a basic blockchain. We aim to keep the blockchain as simple as possible and to research and experiment with different technologies ## Getting Started @@ -10,7 +10,7 @@ These instructions will get you a copy of the project up and running on your loc ## Prerequisites -To install and use the Elixir Blockchain you will need [Elixir](https://elixir-lang.org/install.html) and the source code by cloning or downloading the repository. +To install and use the Elixir Blockchain you will need [Elixir](https://elixir-lang.org/install.html) and the source code by cloning or downloading the repository. ## Usage #### **Starting the application** @@ -19,29 +19,29 @@ Start the application in interactive Elixir mode `iex -S mix phx.server` #### **Starting the miner** -To start the miner use the following command in the command prompt: +To start the miner use the following command in the command prompt: `Aecore.Miner.Worker.resume()` This will continuously mine new blocks until terminated or suspended. -To suspend/stop the miner from mining: +To suspend/stop the miner from mining: `Aecore.Miner.Worker.suspend() ` #### **API calls** -To add a block to the blockchain: +To add a block to the blockchain: `Aecore.Chain.Worker.add_block(%Block{}) :: :ok` -To get all blocks in the current chain: +To get all blocks in the current chain: `Aecore.Chain.Worker.all_blocks() :: list()` -To get the latest block added to the chain: +To get the latest block added to the chain: `Aecore.Chain.Worker.latest_block() :: %Block{}` -To get the latest chainstate: +To get the latest chainstate: `Aecore.Chain.Worker.chain_state() :: map()` @@ -49,11 +49,11 @@ To add a transaction to the Transaction Pool: `Aecore.Txs.Pool.Worker.add_transaction(%SignedTx{}) :: :ok | :error` -To remove a transaction from the Transaction Pool: +To remove a transaction from the Transaction Pool: `Aecore.Txs.Pool.Worker.remove_transaction(%SignedTx{}) :: :ok | :error` -To inspect all transactions in the Transaction Pool: +To inspect all transactions in the Transaction Pool: `Aecore.Txs.Pool.Worker.get_pool() :: map() ` @@ -63,9 +63,9 @@ To run the automatic tests: `mix test` -## Logging +## Logging -To debug, see errors, warnings and info about the blockchain, +To debug, see errors, warnings and info about the blockchain, the log can be found in the source folder under:`apps/aecore/logs` `09:59:16.298 [info] Added block #1 with hash 6C449AC3B5E38857DC85310873979F45992270BF54304B3F60BE4F64373991B5, total tokens: 100 ` diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index 7a9bdf5f..c73cb823 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -68,8 +68,8 @@ defmodule Aecore.Chain.Worker do Enum.reverse(get_blocks([], start_block_hash, size)) end - @spec add_validated_block(%Block{}) :: :ok - def add_validated_block(%Block{} = block) do + @spec add_block(%Block{}) :: :ok + def add_block(%Block{} = block) do latest_block = latest_block() prev_block_chain_state = chain_state() @@ -80,12 +80,12 @@ defmodule Aecore.Chain.Worker do blocks_for_difficulty_calculation = get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) BlockValidation.validate_block!(block, latest_block, new_chain_state, blocks_for_difficulty_calculation) - add_block(block) + add_validated_block(block) end - @spec add_block(%Block{}) :: :ok - defp add_block(%Block{} = block) do - GenServer.call(__MODULE__, {:add_block, block}) + @spec add_validated_block(%Block{}) :: :ok + defp add_validated_block(%Block{} = block) do + GenServer.call(__MODULE__, {:add_validated_block, block}) end @spec chain_state(binary()) :: map() @@ -143,7 +143,7 @@ defmodule Aecore.Chain.Worker do end end - def handle_call({:add_block, %Block{} = block}, _from, state) do + def handle_call({:add_validated_block, %Block{} = block}, _from, state) do {block_map, latest_block_chain_state, txs_index} = state prev_block_chain_state = latest_block_chain_state[block.header.prev_hash] new_block_state = ChainState.calculate_block_state(block.txs) diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index 3ff7a3a6..e0bbe80e 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -142,9 +142,9 @@ defmodule Aecore.Miner.Worker do BlockValidation.validate_block!(latest_block, previous_block, chain_state, blocks_for_difficulty_calculation) catch - {:error, _message} -> + {:error, message} -> Logger.error(fn -> - "Failed to mine block" + "Failed to mine block: #{message}" end) end @@ -180,7 +180,7 @@ defmodule Aecore.Miner.Worker do Logger.info(fn -> "Mined block ##{block.header.height}, difficulty target #{block.header.difficulty_target}, nonce #{block.header.nonce}" end) - Chain.add_validated_block(block) + Chain.add_block(block) {:block_found, 0} {:error, _message} -> diff --git a/apps/aecore/test/aecore_chain_test.exs b/apps/aecore/test/aecore_chain_test.exs index 0517d395..c5a48572 100644 --- a/apps/aecore/test/aecore_chain_test.exs +++ b/apps/aecore/test/aecore_chain_test.exs @@ -53,7 +53,7 @@ defmodule AecoreChainTest do assert BlockValidation.validate_block!(latest_block, previous_block, Chain.chain_state(), blocks_for_difficulty_calculation) - assert :ok = Chain.add_validated_block(block) + assert :ok = Chain.add_block(block) assert latest_block = Chain.latest_block() assert latest_block.header.height == block.header.height diff --git a/apps/aehttpserver/web/controllers/block_controller.ex b/apps/aehttpserver/web/controllers/block_controller.ex index 143c4994..e58dafdc 100644 --- a/apps/aehttpserver/web/controllers/block_controller.ex +++ b/apps/aehttpserver/web/controllers/block_controller.ex @@ -23,7 +23,7 @@ defmodule Aehttpserver.BlockController do block = Aecore.Utils.Serialization.block(map, :deserialize) - Aecore.Chain.Worker.add_validated_block(block) + Aecore.Chain.Worker.add_block(block) json conn, %{ok: "new block received"} end end From 101f2a462a9085f0dbdc2dbe4a063418ea4d0929 Mon Sep 17 00:00:00 2001 From: Nedelcho Delchev Date: Tue, 21 Nov 2017 14:49:28 +0200 Subject: [PATCH 3/4] Different approach for getting blocks in add_block --- apps/aecore/lib/aecore/chain/worker.ex | 8 +++++++- .../lib/aecore/utils/blockchain/block_validation.ex | 3 +++ apps/aecore/lib/aecore/utils/blockchain/difficulty.ex | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index c73cb823..7d1b4827 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -77,7 +77,13 @@ defmodule Aecore.Chain.Worker do new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_block_chain_state) latest_header_hash = BlockValidation.block_header_hash(latest_block.header) - blocks_for_difficulty_calculation = get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) + + blocks_for_difficulty_calculation = if(block.header.height == 1) do + get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) + else + something = get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) + [block | something] |> Enum.take(Difficulty.get_number_of_blocks()) + end BlockValidation.validate_block!(block, latest_block, new_chain_state, blocks_for_difficulty_calculation) add_validated_block(block) diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 0f842cec..4807a3ba 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -20,6 +20,9 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do coinbase_transactions_sum = sum_coinbase_transactions(new_block) total_fees = Miner.calculate_total_fees(new_block.txs) + heights = Enum.map(blocks_for_difficulty_calculation, fn(block) -> block.header.height end) + IO.inspect(heights) + difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) cond do diff --git a/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex b/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex index 197c3569..9211fce9 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex @@ -1,6 +1,6 @@ defmodule Aecore.Utils.Blockchain.Difficulty do - @number_of_blocks 100 + @number_of_blocks 3 @max_difficulty_change 2 @target_distance 60000 From ba6d1e57db339022a02124c85deb72d8c0bd8570 Mon Sep 17 00:00:00 2001 From: Philipp Piwo Date: Tue, 21 Nov 2017 15:36:52 +0200 Subject: [PATCH 4/4] [GH-#85] resolved issue with difficulty validation - do correct check in miner, differing from next calculation --- apps/aecore/lib/aecore/chain/worker.ex | 11 +-- apps/aecore/lib/aecore/miner/worker.ex | 78 +++++++++++++------ apps/aecore/lib/aecore/peers/sync.ex | 1 - .../utils/blockchain/block_validation.ex | 4 - .../lib/aecore/utils/blockchain/difficulty.ex | 2 +- 5 files changed, 56 insertions(+), 40 deletions(-) diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index 7d1b4827..bb4cf001 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -78,13 +78,7 @@ defmodule Aecore.Chain.Worker do latest_header_hash = BlockValidation.block_header_hash(latest_block.header) - blocks_for_difficulty_calculation = if(block.header.height == 1) do - get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) - else - something = get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) - [block | something] |> Enum.take(Difficulty.get_number_of_blocks()) - end - + blocks_for_difficulty_calculation = get_blocks(latest_header_hash, Difficulty.get_number_of_blocks()) BlockValidation.validate_block!(block, latest_block, new_chain_state, blocks_for_difficulty_calculation) add_validated_block(block) end @@ -153,8 +147,7 @@ defmodule Aecore.Chain.Worker do {block_map, latest_block_chain_state, txs_index} = state prev_block_chain_state = latest_block_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) + new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_block_chain_state) new_block_txs_index = calculate_block_acc_txs_info(block) new_txs_index = update_txs_index(txs_index, new_block_txs_index) diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index e0bbe80e..9064b1ab 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -74,9 +74,15 @@ defmodule Aecore.Miner.Worker do ## Running ## def running(:cast, :mine, start_nonce) do - {_, next_nonce} = mine_next_block(start_nonce) - GenStateMachine.cast(__MODULE__, :mine) - {:next_state, :running, next_nonce} + {status, next_nonce} = mine_next_block(start_nonce) + case status do + :error -> + Logger.info("Mining stopped by error") + {:next_state, :idle, 0} + _ -> + GenStateMachine.cast(__MODULE__, :mine) + {:next_state, :running, next_nonce} + end end def running({:call, from}, :get_state, _data) do @@ -88,7 +94,7 @@ defmodule Aecore.Miner.Worker do end def running({:call, from}, :suspend, data) do - Logger.info("Mined stopped by user") + Logger.info("Mining stopped by user") {:next_state, :idle, data, [{:reply, from, :ok}]} end @@ -115,9 +121,13 @@ defmodule Aecore.Miner.Worker do def coinbase_transaction_value, do: @coinbase_transaction_value def calculate_total_fees(txs) do - List.foldl(txs, 0, fn(tx, acc) -> + List.foldl( + txs, + 0, + fn (tx, acc) -> acc + tx.data.fee - end) + end + ) end ## Internal @@ -127,31 +137,37 @@ defmodule Aecore.Miner.Worker do 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_validation = if(latest_block.header.height == 0) do + Chain.get_blocks(latest_block_hash, Difficulty.get_number_of_blocks()) + else + [_ | something] = Chain.get_blocks(latest_block_hash, Difficulty.get_number_of_blocks() + 1) + something + end - 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, blocks_for_difficulty_calculation) - - catch - {:error, message} -> - Logger.error(fn -> - "Failed to mine block: #{message}" - end) - end + try do + BlockValidation.validate_block!( + latest_block, + previous_block, + chain_state, + blocks_for_difficulty_validation + ) + + blocks_for_difficulty_calculation = Chain.get_blocks(latest_block_hash, Difficulty.get_number_of_blocks()) difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) + txs_list = Map.values(Pool.get_pool()) + ordered_txs_list = Enum.sort(txs_list, fn (tx1, tx2) -> tx1.data.nonce < tx2.data.nonce end) valid_txs = BlockValidation.filter_invalid_transactions_chainstate(ordered_txs_list, chain_state) + {_, pubkey} = Keys.pubkey() + total_fees = calculate_total_fees(valid_txs) valid_txs = [get_coinbase_transaction(pubkey, total_fees) | valid_txs] root_hash = BlockValidation.calculate_root_hash(valid_txs) @@ -169,22 +185,34 @@ defmodule Aecore.Miner.Worker do root_hash, chain_state_hash, difficulty, - 0, #start from nonce 0, will be incremented in mining + 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 + + 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) + 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 + message -> + Logger.error(fn -> "Failed to mine block: #{Kernel.inspect(message)}" end) + {:error, message} + end + end end diff --git a/apps/aecore/lib/aecore/peers/sync.ex b/apps/aecore/lib/aecore/peers/sync.ex index e2463a50..e1064484 100644 --- a/apps/aecore/lib/aecore/peers/sync.ex +++ b/apps/aecore/lib/aecore/peers/sync.ex @@ -62,7 +62,6 @@ defmodule Aecore.Peers.Sync do peers_count = map_size(Peers.all_peers()) cond do peers_count == 0 -> - Logger.error(fn -> "No peers" end) {:error, "No peers"} peers_count < @peers_target_count -> all_peers = Map.keys(Peers.all_peers()) diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 4807a3ba..dd7eda4b 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -19,10 +19,6 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do is_difficulty_target_met = Cuckoo.verify(new_block.header) coinbase_transactions_sum = sum_coinbase_transactions(new_block) total_fees = Miner.calculate_total_fees(new_block.txs) - - heights = Enum.map(blocks_for_difficulty_calculation, fn(block) -> block.header.height end) - IO.inspect(heights) - difficulty = Difficulty.calculate_next_difficulty(blocks_for_difficulty_calculation) cond do diff --git a/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex b/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex index 9211fce9..197c3569 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/difficulty.ex @@ -1,6 +1,6 @@ defmodule Aecore.Utils.Blockchain.Difficulty do - @number_of_blocks 3 + @number_of_blocks 100 @max_difficulty_change 2 @target_distance 60000