From 53f40ab1320e4709940280bebc2a32c1c5547ed1 Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Mon, 30 Oct 2017 15:35:27 +0200 Subject: [PATCH 1/7] [GH-#83] Added fee to transactions --- apps/aecore/lib/aecore/chain/chain_state.ex | 24 ++- apps/aecore/lib/aecore/chain/worker.ex | 3 +- apps/aecore/lib/aecore/keys/worker.ex | 6 +- apps/aecore/lib/aecore/miner/worker.ex | 7 +- apps/aecore/lib/aecore/structures/tx_data.ex | 8 +- apps/aecore/test/aecore_chain_state_test.exs | 14 +- apps/aecore/test/aecore_keys_test.exs | 2 +- apps/aecore/test/aecore_tx_test.exs | 2 +- apps/aecore/test/aecore_txs_pool_test.exs | 4 +- apps/aecore/test/aecore_validation_test.exs | 4 +- .../test/multiple_transactions_test.exs | 158 +++++++++--------- 11 files changed, 129 insertions(+), 103 deletions(-) diff --git a/apps/aecore/lib/aecore/chain/chain_state.ex b/apps/aecore/lib/aecore/chain/chain_state.ex index 4e6346ae..a00d9bf4 100644 --- a/apps/aecore/lib/aecore/chain/chain_state.ex +++ b/apps/aecore/lib/aecore/chain/chain_state.ex @@ -4,6 +4,8 @@ defmodule Aecore.Chain.ChainState do The chain state is a map, telling us what amount of tokens each account has. """ + alias Aecore.Keys.Worker, as: Keys + @doc """ Calculates the balance of each account mentioned in the transactions a single block, returns a map with the @@ -19,7 +21,8 @@ defmodule Aecore.Chain.ChainState do cond do transaction.data.from_acc != nil -> update_block_state(block_state, transaction.data.from_acc, - -transaction.data.value, transaction.data.nonce) + -(transaction.data.value + transaction.data.fee), + transaction.data.nonce) true -> block_state @@ -79,6 +82,25 @@ defmodule Aecore.Chain.ChainState do Enum.all?() end + @spec add_fees_to_block_state(list(), map()) :: map() + def add_fees_to_block_state(txs, block_state) do + pubkey = elem(Keys.pubkey(), 1) + total_fees = List.foldl(txs, 0, fn(tx, acc) -> + acc + tx.data.fee + end) + + cond do + !Map.has_key?(block_state, pubkey) -> + Map.put(block_state, pubkey, %{balance: total_fees, nonce: 0}) + + true -> + current_pubkey_block_state = Map.get(block_state, pubkey) + new_pubkey_block_state = %{balance: current_pubkey_block_state.balance + total_fees, + nonce: current_pubkey_block_state.nonce} + Map.replace(block_state, pubkey, new_pubkey_block_state) + end + end + @spec update_block_state(map(), binary(), integer(), integer()) :: map() defp update_block_state(block_state, account, value, nonce) do block_state_filled_empty = diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index 56b85a04..b72e8a85 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -98,7 +98,8 @@ defmodule Aecore.Chain.Worker 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) + new_block_state_with_fees = ChainState.add_fees_to_block_state(b.txs, new_block_state) + new_chain_state = ChainState.calculate_chain_state(new_block_state_with_fees, prev_chain_state) try do BlockValidation.validate_block!(b, prior_block, new_chain_state) diff --git a/apps/aecore/lib/aecore/keys/worker.ex b/apps/aecore/lib/aecore/keys/worker.ex index d4742c15..f240cfb2 100644 --- a/apps/aecore/lib/aecore/keys/worker.ex +++ b/apps/aecore/lib/aecore/keys/worker.ex @@ -38,10 +38,10 @@ defmodule Aecore.Keys.Worker do - value: The amount of a transaction """ - @spec sign_tx(binary(), integer(), integer()) :: {:ok, %SignedTx{}} - def sign_tx(to_acc, value, nonce) do + @spec sign_tx(binary(), integer(), integer(), integer()) :: {:ok, %SignedTx{}} + def sign_tx(to_acc, value, nonce, fee) do {:ok, from_acc} = pubkey() - {:ok, tx_data} = TxData.create(from_acc, to_acc, value, nonce) + {:ok, tx_data} = TxData.create(from_acc, to_acc, value, nonce, fee) {:ok, signature} = sign(tx_data) signed_tx = %SignedTx{data: tx_data, signature: signature} {:ok, signed_tx} diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index 60bfc150..e3486171 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -95,7 +95,8 @@ defmodule Aecore.Miner.Worker do from_acc: nil, to_acc: to_acc, value: @coinbase_transaction_value, - nonce: 0 + nonce: 0, + fee: 0 } %SignedTx{data: tx_data, signature: nil} @@ -122,7 +123,9 @@ defmodule Aecore.Miner.Worker do 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) + new_block_state_with_fees = ChainState.add_fees_to_block_state(valid_txs, new_block_state) + + new_chain_state = ChainState.calculate_chain_state(new_block_state_with_fees, chain_state) chain_state_hash = ChainState.calculate_chain_state_hash(new_chain_state) latest_block_hash = BlockValidation.block_header_hash(latest_block.header) diff --git a/apps/aecore/lib/aecore/structures/tx_data.ex b/apps/aecore/lib/aecore/structures/tx_data.ex index 08b9b511..c7425c6f 100644 --- a/apps/aecore/lib/aecore/structures/tx_data.ex +++ b/apps/aecore/lib/aecore/structures/tx_data.ex @@ -15,11 +15,11 @@ defmodule Aecore.Structures.TxData do - to_acc: To account is the public address of the account receiving the transaction - value: The amount of a transaction """ - defstruct [:nonce, :from_acc, :to_acc, :value] + defstruct [:nonce, :from_acc, :to_acc, :value, :fee] use ExConstructor - @spec create(binary(), binary(), integer(), integer()) :: {:ok, %TxData{}} - def create(from_acc, to_acc, value, nonce) do - {:ok, %TxData{from_acc: from_acc, to_acc: to_acc, value: value, nonce: nonce}} + @spec create(binary(), binary(), integer(), integer(), integer()) :: {:ok, %TxData{}} + def create(from_acc, to_acc, value, nonce, fee) do + {:ok, %TxData{from_acc: from_acc, to_acc: to_acc, value: value, nonce: nonce, fee: fee}} end end diff --git a/apps/aecore/test/aecore_chain_state_test.exs b/apps/aecore/test/aecore_chain_state_test.exs index 43fc9f35..5834aca5 100644 --- a/apps/aecore/test/aecore_chain_state_test.exs +++ b/apps/aecore/test/aecore_chain_state_test.exs @@ -14,9 +14,9 @@ defmodule AecoreChainStateTest do test "block state" do block = get_block() - assert %{"a" => %{balance: 3, nonce: 102}, - "b" => %{balance: -1, nonce: 1}, - "c" => %{balance: -2, nonce: 1}} == + assert %{"a" => %{balance: 1, nonce: 102}, + "b" => %{balance: -2, nonce: 1}, + "c" => %{balance: -3, nonce: 1}} == ChainState.calculate_block_state(block.txs) end @@ -38,13 +38,13 @@ defmodule AecoreChainStateTest do txs_hash: <<12, 123, 12>>, difficulty_target: 0, nonce: 0, timestamp: System.system_time(:milliseconds), version: 1}, txs: [ %SignedTx{data: %TxData{from_acc: "a", to_acc: "b", - value: 5, nonce: 101}, signature: <<0>>}, + value: 5, nonce: 101, fee: 1}, signature: <<0>>}, %SignedTx{data: %TxData{from_acc: "a", to_acc: "c", - value: 2, nonce: 102}, signature: <<0>>}, + value: 2, nonce: 102, fee: 1}, signature: <<0>>}, %SignedTx{data: %TxData{from_acc: "c", to_acc: "b", - value: 4, nonce: 1}, signature: <<0>>}, + value: 4, nonce: 1, fee: 1}, signature: <<0>>}, %SignedTx{data: %TxData{from_acc: "b", to_acc: "a", - value: 10, nonce: 1}, signature: <<0>>}]} + value: 10, nonce: 1, fee: 1}, signature: <<0>>}]} end end diff --git a/apps/aecore/test/aecore_keys_test.exs b/apps/aecore/test/aecore_keys_test.exs index f6b7a91f..f691d6a2 100644 --- a/apps/aecore/test/aecore_keys_test.exs +++ b/apps/aecore/test/aecore_keys_test.exs @@ -20,6 +20,6 @@ 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, 1) end end diff --git a/apps/aecore/test/aecore_tx_test.exs b/apps/aecore/test/aecore_tx_test.exs index 58120662..d3d9b3ac 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, 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..311fe6fe 100644 --- a/apps/aecore/test/aecore_txs_pool_test.exs +++ b/apps/aecore/test/aecore_txs_pool_test.exs @@ -18,9 +18,9 @@ defmodule AecoreTxsPoolTest do 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) + Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1, 1) {:ok, tx2} = Keys.sign_tx(to_account, 5, - Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1, 1) Miner.resume() Miner.suspend() assert :ok = Pool.add_transaction(tx1) diff --git a/apps/aecore/test/aecore_validation_test.exs b/apps/aecore/test/aecore_validation_test.exs index 6f75b1fb..bd93bd67 100644 --- a/apps/aecore/test/aecore_validation_test.exs +++ b/apps/aecore/test/aecore_validation_test.exs @@ -55,9 +55,9 @@ defmodule AecoreValidationTest do test "validate transactions in a block" 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) + Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1, 1) {:ok, tx2} = Keys.sign_tx(to_account, 10, - Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, to_account, %{nonce: 0}).nonce + 1, 1) block = %{Block.genesis_block | txs: [tx1, tx2]} assert block |> BlockValidation.validate_block_transactions diff --git a/apps/aecore/test/multiple_transactions_test.exs b/apps/aecore/test/multiple_transactions_test.exs index 077a4bdf..a5c4c52d 100644 --- a/apps/aecore/test/multiple_transactions_test.exs +++ b/apps/aecore/test/multiple_transactions_test.exs @@ -23,106 +23,106 @@ defmodule MultipleTransactionsTest do {account1_pub_key, _account1_priv_key} = account1 {account2_pub_key, _account2_priv_key} = account2 {account3_pub_key, _account3_priv_key} = account3 - pubkey = elem(Keys.pubkey, 1) + pubkey = elem(Keys.pubkey(), 1) - # account A has 100 tokens, spends 100 to B should succeed + # account A has 100 tokens, spends 99 (+1 fee) to B should succeed Miner.resume() Miner.suspend() Pool.get_and_empty_pool() {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 100, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account2, 99, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 0 == Chain.chain_state[account1_pub_key].balance - assert 100 == Chain.chain_state[account2_pub_key].balance + assert 99 == Chain.chain_state[account2_pub_key].balance - # account1 => 0; account2 => 100 + # account1 => 0; account2 => 99 - # account A has 100 tokens, spends 110 to B should be invalid + # account A has 100 tokens, spends 109 (+1 fee) to B should be invalid {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 110, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account2, 109, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 100 == Chain.chain_state[account1_pub_key].balance - # acccount1 => 100; account2 => 100 + # acccount1 => 100; account2 => 99 - # account A has 100 tokens, spends 40 to B, and two times 30 to C should succeed + # account A has 100 tokens, spends 39 (+1 fee) to B, and two times 29 (+1 fee) to C should succeed account1_initial_nonce = Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce - tx = create_signed_tx(account1, account2, 40, account1_initial_nonce + 1) + tx = create_signed_tx(account1, account2, 39, account1_initial_nonce + 1, 1) assert :ok = Pool.add_transaction(tx) - tx = create_signed_tx(account1, account3, 30, account1_initial_nonce + 2) + tx = create_signed_tx(account1, account3, 29, account1_initial_nonce + 2, 1) assert :ok = Pool.add_transaction(tx) - tx = create_signed_tx(account1, account3, 30, account1_initial_nonce + 3) + tx = create_signed_tx(account1, account3, 29, account1_initial_nonce + 3, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 0 == Chain.chain_state[account1_pub_key].balance - assert 140 == Chain.chain_state[account2_pub_key].balance - assert 60 == Chain.chain_state[account3_pub_key].balance + assert 138 == Chain.chain_state[account2_pub_key].balance + assert 58 == Chain.chain_state[account3_pub_key].balance - # account1 => 0; account2 => 140; account3 => 60 + # account1 => 0; account2 => 138; account3 => 58 - # account A has 100 tokens, spends 50 to B, and two times 30 to C, + # account A has 100 tokens, spends 49 (+1 fee) to B, and two times 29 (+1 fee) to C, # last transaction to C should be invalid, others be included account1_initial_nonce = Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 50, account1_initial_nonce + 1) + tx = create_signed_tx(account1, account2, 49, account1_initial_nonce + 1, 1) assert :ok = Pool.add_transaction(tx) - tx = create_signed_tx(account1, account3, 30, account1_initial_nonce + 2) + tx = create_signed_tx(account1, account3, 29, account1_initial_nonce + 2, 1) assert :ok = Pool.add_transaction(tx) - tx = create_signed_tx(account1, account3, 30, account1_initial_nonce + 3) + tx = create_signed_tx(account1, account3, 29, account1_initial_nonce + 3, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 20 == Chain.chain_state[account1_pub_key].balance - assert 190 == Chain.chain_state[account2_pub_key].balance - assert 90 == Chain.chain_state[account3_pub_key].balance + assert 187 == Chain.chain_state[account2_pub_key].balance + assert 87 == Chain.chain_state[account3_pub_key].balance - # account1 => 20; account2 => 190; account3 => 90 + # account1 => 20; account2 => 197; account3 => 87 - # account C has 100 tokens, spends 100 to B, B spends 100 to A should succeed - {:ok, tx} = Keys.sign_tx(account3_pub_key, 10, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + # account C has 100 tokens, spends 99 (+1 fee) to B, B spends 99 (+1 fee) to A should succeed + {:ok, tx} = Keys.sign_tx(account3_pub_key, 13, + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account3, account2, 100, - Map.get(Chain.chain_state, account3_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account3, account2, 99, + Map.get(Chain.chain_state, account3_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) - tx = create_signed_tx(account2, account1, 100, - Map.get(Chain.chain_state, account2_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account2, account1, 99, + Map.get(Chain.chain_state, account2_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 0 == Chain.chain_state[account3_pub_key].balance - assert 190 == Chain.chain_state[account2_pub_key].balance - assert 120 == Chain.chain_state[account1_pub_key].balance + assert 186 == Chain.chain_state[account2_pub_key].balance + assert 119 == Chain.chain_state[account1_pub_key].balance end @tag timeout: 10000000 @@ -131,125 +131,125 @@ defmodule MultipleTransactionsTest do {account1_pub_key, _account1_priv_key} = account1 {account2_pub_key, _account2_priv_key} = account2 {account3_pub_key, _account3_priv_key} = account3 - pubkey = elem(Keys.pubkey, 1) + pubkey = elem(Keys.pubkey(), 1) - # account A has 100 tokens, spends 100 to B should succeed + # account A has 100 tokens, spends 99 (+1 fee) to B should succeed Miner.resume() Miner.suspend() Pool.get_and_empty_pool() {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 100, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account2, 99, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 0 == Chain.chain_state[account1_pub_key].balance - assert 100 == Chain.chain_state[account2_pub_key].balance + assert 99 == Chain.chain_state[account2_pub_key].balance - # account1 => 0; account2 => 100 + # account1 => 0; account2 => 99 - # account A has 100 tokens, spends 110 to B should be invalid + # account A has 100 tokens, spends 109 (+1 fee) to B should be invalid {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 110, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account2, 109, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 100 == Chain.chain_state[account1_pub_key].balance - # acccount1 => 100; account2 => 100 + # acccount1 => 100; account2 => 99 - # account A has 100 tokens, spends 40 to B, and two times 30 to C should succeed - tx = create_signed_tx(account1, account2, 40, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + # account A has 100 tokens, spends 39 (+1 fee) to B, and two times 29 (+1 fee) to C should succeed + tx = create_signed_tx(account1, account2, 39, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account3, 30, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account3, 29, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account3, 30, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account3, 29, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 0 == Chain.chain_state[account1_pub_key].balance - assert 140 == Chain.chain_state[account2_pub_key].balance - assert 60 == Chain.chain_state[account3_pub_key].balance + assert 138 == Chain.chain_state[account2_pub_key].balance + assert 58 == Chain.chain_state[account3_pub_key].balance - # account1 => 0; account2 => 140; account3 => 60 + # account1 => 0; account2 => 138; account3 => 58 - # account A has 100 tokens, spends 50 to B, and two times 30 to C, + # account A has 99 (+1 fee) tokens, spends 49 (+1 fee) to B, and two times 29 (+1 fee) to C, # last transaction to C should be invalid, others be included {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 50, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account2, 49, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account3, 30, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account3, 29, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account3, 30, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account3, 29, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 20 == Chain.chain_state[account1_pub_key].balance - assert 190 == Chain.chain_state[account2_pub_key].balance - assert 90 == Chain.chain_state[account3_pub_key].balance + assert 187 == Chain.chain_state[account2_pub_key].balance + assert 87 == Chain.chain_state[account3_pub_key].balance - # account1 => 20; account2 => 190; account3 => 90 + # account1 => 20; account2 => 187; account3 => 87 - # account A has 100 tokens, spends 100 to B, B spends 100 to C should succeed + # account A has 100 tokens, spends 99 (+1 fee) to B, B spends 99 (+1 fee) to C should succeed {:ok, tx} = Keys.sign_tx(account1_pub_key, 80, - Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1) + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 100, - Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account1, account2, 99, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account2, account3, 100, - Map.get(Chain.chain_state, account2_pub_key, %{nonce: 0}).nonce + 1) + tx = create_signed_tx(account2, account3, 99, + Map.get(Chain.chain_state, account2_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() assert 0 == Chain.chain_state[account1_pub_key].balance - assert 190 == Chain.chain_state[account2_pub_key].balance - assert 190 == Chain.chain_state[account3_pub_key].balance + assert 186 == Chain.chain_state[account2_pub_key].balance + assert 186 == Chain.chain_state[account3_pub_key].balance end defp get_accounts_one_block() do @@ -310,10 +310,10 @@ defmodule MultipleTransactionsTest do {account1, account2, account3} end - defp create_signed_tx(from_acc, to_acc, value, nonce) do + defp create_signed_tx(from_acc, to_acc, value, nonce, fee) do {from_acc_pub_key, from_acc_priv_key} = from_acc {to_acc_pub_key, _to_acc_priv_key} = to_acc - {:ok, tx_data} = TxData.create(from_acc_pub_key, to_acc_pub_key, value, nonce) + {:ok, tx_data} = TxData.create(from_acc_pub_key, to_acc_pub_key, value, nonce, fee) {:ok, signature} = Keys.sign(tx_data, from_acc_priv_key) %SignedTx{data: tx_data, signature: signature} From 0fe9aadb0fefa807de5b4c2689b3d7623c93e5f2 Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Tue, 31 Oct 2017 16:09:50 +0200 Subject: [PATCH 2/7] [GH-#83] Small refactoring --- apps/aecore/lib/aecore/chain/chain_state.ex | 15 ++++++--------- apps/aecore/lib/aecore/chain/worker.ex | 4 +++- apps/aecore/lib/aecore/miner/worker.ex | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/aecore/lib/aecore/chain/chain_state.ex b/apps/aecore/lib/aecore/chain/chain_state.ex index a00d9bf4..6722f954 100644 --- a/apps/aecore/lib/aecore/chain/chain_state.ex +++ b/apps/aecore/lib/aecore/chain/chain_state.ex @@ -4,8 +4,6 @@ defmodule Aecore.Chain.ChainState do The chain state is a map, telling us what amount of tokens each account has. """ - alias Aecore.Keys.Worker, as: Keys - @doc """ Calculates the balance of each account mentioned in the transactions a single block, returns a map with the @@ -82,22 +80,21 @@ defmodule Aecore.Chain.ChainState do Enum.all?() end - @spec add_fees_to_block_state(list(), map()) :: map() - def add_fees_to_block_state(txs, block_state) do - pubkey = elem(Keys.pubkey(), 1) + @spec add_fees_to_block_state(list(), map(), binary()) :: map() + def add_fees_to_block_state(txs, block_state, account) do total_fees = List.foldl(txs, 0, fn(tx, acc) -> acc + tx.data.fee end) cond do - !Map.has_key?(block_state, pubkey) -> - Map.put(block_state, pubkey, %{balance: total_fees, nonce: 0}) + !Map.has_key?(block_state, account) -> + Map.put(block_state, account, %{balance: total_fees, nonce: 0}) true -> - current_pubkey_block_state = Map.get(block_state, pubkey) + current_pubkey_block_state = Map.get(block_state, account) new_pubkey_block_state = %{balance: current_pubkey_block_state.balance + total_fees, nonce: current_pubkey_block_state.nonce} - Map.replace(block_state, pubkey, new_pubkey_block_state) + Map.replace(block_state, account, new_pubkey_block_state) end end diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index b72e8a85..159407ab 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -10,6 +10,7 @@ defmodule Aecore.Chain.Worker do alias Aecore.Utils.Blockchain.BlockValidation alias Aecore.Utils.Blockchain.Difficulty alias Aecore.Txs.Pool.Worker, as: Pool + alias Aecore.Keys.Worker, as: Keys use GenServer @@ -97,8 +98,9 @@ defmodule Aecore.Chain.Worker do def handle_call({:add_block, %Block{} = b}, _from, state) do {chain, prev_chain_state} = state [prior_block | _] = chain + pubkey = elem(Keys.pubkey(), 1) new_block_state = ChainState.calculate_block_state(b.txs) - new_block_state_with_fees = ChainState.add_fees_to_block_state(b.txs, new_block_state) + new_block_state_with_fees = ChainState.add_fees_to_block_state(b.txs, new_block_state, pubkey) new_chain_state = ChainState.calculate_chain_state(new_block_state_with_fees, prev_chain_state) try do diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index e3486171..da9d2fa5 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -123,7 +123,7 @@ defmodule Aecore.Miner.Worker do root_hash = BlockValidation.calculate_root_hash(valid_txs) new_block_state = ChainState.calculate_block_state(valid_txs) - new_block_state_with_fees = ChainState.add_fees_to_block_state(valid_txs, new_block_state) + new_block_state_with_fees = ChainState.add_fees_to_block_state(valid_txs, new_block_state, pubkey) new_chain_state = ChainState.calculate_chain_state(new_block_state_with_fees, chain_state) chain_state_hash = ChainState.calculate_chain_state_hash(new_chain_state) From e3212cb615dc05d1b4d3a5da949d9ad6e144ca4e Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Wed, 1 Nov 2017 12:26:48 +0200 Subject: [PATCH 3/7] [GH-#83] Fees are now added to the coinbase transaction --- apps/aecore/lib/aecore/chain/chain_state.ex | 18 ------------------ apps/aecore/lib/aecore/chain/worker.ex | 3 +-- apps/aecore/lib/aecore/miner/worker.ex | 15 ++++++++------- .../utils/blockchain/block_validation.ex | 3 ++- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/apps/aecore/lib/aecore/chain/chain_state.ex b/apps/aecore/lib/aecore/chain/chain_state.ex index 6722f954..35a9364e 100644 --- a/apps/aecore/lib/aecore/chain/chain_state.ex +++ b/apps/aecore/lib/aecore/chain/chain_state.ex @@ -80,24 +80,6 @@ defmodule Aecore.Chain.ChainState do Enum.all?() end - @spec add_fees_to_block_state(list(), map(), binary()) :: map() - def add_fees_to_block_state(txs, block_state, account) do - total_fees = List.foldl(txs, 0, fn(tx, acc) -> - acc + tx.data.fee - end) - - cond do - !Map.has_key?(block_state, account) -> - Map.put(block_state, account, %{balance: total_fees, nonce: 0}) - - true -> - current_pubkey_block_state = Map.get(block_state, account) - new_pubkey_block_state = %{balance: current_pubkey_block_state.balance + total_fees, - nonce: current_pubkey_block_state.nonce} - Map.replace(block_state, account, new_pubkey_block_state) - end - end - @spec update_block_state(map(), binary(), integer(), integer()) :: map() defp update_block_state(block_state, account, value, nonce) do block_state_filled_empty = diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index 159407ab..17d9da71 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -100,8 +100,7 @@ defmodule Aecore.Chain.Worker do [prior_block | _] = chain pubkey = elem(Keys.pubkey(), 1) new_block_state = ChainState.calculate_block_state(b.txs) - new_block_state_with_fees = ChainState.add_fees_to_block_state(b.txs, new_block_state, pubkey) - new_chain_state = ChainState.calculate_chain_state(new_block_state_with_fees, prev_chain_state) + new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_chain_state) try do BlockValidation.validate_block!(b, prior_block, new_chain_state) diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index da9d2fa5..d8624ef9 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -90,13 +90,13 @@ defmodule Aecore.Miner.Worker do {:next_state, :idle, data} end - def get_coinbase_transaction(to_acc) do + def get_coinbase_transaction(to_acc, total_fees) do tx_data = %TxData{ from_acc: nil, to_acc: to_acc, - value: @coinbase_transaction_value, + value: @coinbase_transaction_value + total_fees, nonce: 0, - fee: 0 + fee: total_fees } %SignedTx{data: tx_data, signature: nil} @@ -119,13 +119,14 @@ defmodule Aecore.Miner.Worker do valid_txs = BlockValidation.filter_invalid_transactions_chainstate(ordered_txs_list, chain_state) {_, pubkey} = Keys.pubkey() - valid_txs = [get_coinbase_transaction(pubkey) | valid_txs] + total_fees = List.foldl(valid_txs, 0, fn(tx, acc) -> + acc + tx.data.fee + end) + valid_txs = [get_coinbase_transaction(pubkey, total_fees) | valid_txs] root_hash = BlockValidation.calculate_root_hash(valid_txs) new_block_state = ChainState.calculate_block_state(valid_txs) - new_block_state_with_fees = ChainState.add_fees_to_block_state(valid_txs, new_block_state, pubkey) - - new_chain_state = ChainState.calculate_chain_state(new_block_state_with_fees, chain_state) + 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) diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 0a15809d..23384183 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -37,6 +37,7 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do throw({:error, "One or more transactions not valid"}) coinbase_transactions_sum > Miner.coinbase_transaction_value() -> + IO.puts(coinbase_transactions_sum) throw({:error, "Sum of coinbase transactions values exceeds the maximum coinbase transactions value"}) new_block.header.chain_state_hash != chain_state_hash -> @@ -150,7 +151,7 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do block.txs |> Enum.map( fn tx -> cond do - SignedTx.is_coinbase(tx) -> tx.data.value + SignedTx.is_coinbase(tx) -> tx.data.value - tx.data.fee true -> 0 end end From dd9847010271c316e2810a6db263308bbd321b11 Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Wed, 1 Nov 2017 14:57:59 +0200 Subject: [PATCH 4/7] [GH-#83] Added test --- .../test/multiple_transactions_test.exs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/apps/aecore/test/multiple_transactions_test.exs b/apps/aecore/test/multiple_transactions_test.exs index a5c4c52d..8981ca2d 100644 --- a/apps/aecore/test/multiple_transactions_test.exs +++ b/apps/aecore/test/multiple_transactions_test.exs @@ -252,6 +252,36 @@ defmodule MultipleTransactionsTest do assert 186 == Chain.chain_state[account3_pub_key].balance end + @tag timeout: 10000000 + test "in one block, miner collects all the fees from the transactions" do + {account1, account2, account3} = get_accounts_one_block() + {account1_pub_key, _account1_priv_key} = account1 + {account2_pub_key, _account2_priv_key} = account2 + {account3_pub_key, _account3_priv_key} = account3 + pubkey = elem(Keys.pubkey(), 1) + + Miner.resume() + Miner.suspend() + Pool.get_and_empty_pool() + {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) + assert :ok = Pool.add_transaction(tx) + Miner.resume() + Miner.suspend() + Pool.get_and_empty_pool() + tx = create_signed_tx(account1, account2, 99, + Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) + assert :ok = Pool.add_transaction(tx) + tx = create_signed_tx(account2, account3, 99, + Map.get(Chain.chain_state, account2_pub_key, %{nonce: 0}).nonce + 1, 1) + assert :ok = Pool.add_transaction(tx) + miner_balance_before_mining = Map.get(Chain.chain_state, pubkey).balance + Miner.resume() + Miner.suspend() + miner_balance_after_mining = Map.get(Chain.chain_state, pubkey).balance + assert miner_balance_after_mining == miner_balance_before_mining + Miner.coinbase_transaction_value() + 2 + end + defp get_accounts_one_block() do account1 = { <<4, 94, 96, 161, 182, 76, 153, 22, 179, 136, 60, 87, 225, 135, 253, 179, 80, From 741239fa3bfd0f747368343d9ee8ed3b7585c091 Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Fri, 3 Nov 2017 11:48:05 +0200 Subject: [PATCH 5/7] [GH-#83] Fixed test --- .../utils/blockchain/block_validation.ex | 1 - .../test/multiple_transactions_test.exs | 41 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 23384183..145b84b3 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -37,7 +37,6 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do throw({:error, "One or more transactions not valid"}) coinbase_transactions_sum > Miner.coinbase_transaction_value() -> - IO.puts(coinbase_transactions_sum) throw({:error, "Sum of coinbase transactions values exceeds the maximum coinbase transactions value"}) new_block.header.chain_state_hash != chain_state_hash -> diff --git a/apps/aecore/test/multiple_transactions_test.exs b/apps/aecore/test/multiple_transactions_test.exs index 8981ca2d..9fd1b61a 100644 --- a/apps/aecore/test/multiple_transactions_test.exs +++ b/apps/aecore/test/multiple_transactions_test.exs @@ -254,22 +254,27 @@ defmodule MultipleTransactionsTest do @tag timeout: 10000000 test "in one block, miner collects all the fees from the transactions" do - {account1, account2, account3} = get_accounts_one_block() + {account1, account2, account3} = get_accounts_miner_fees() {account1_pub_key, _account1_priv_key} = account1 {account2_pub_key, _account2_priv_key} = account2 - {account3_pub_key, _account3_priv_key} = account3 pubkey = elem(Keys.pubkey(), 1) + Miner.resume() + Miner.suspend() + Pool.get_and_empty_pool() Miner.resume() Miner.suspend() Pool.get_and_empty_pool() {:ok, tx} = Keys.sign_tx(account1_pub_key, 100, Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 1, 0) assert :ok = Pool.add_transaction(tx) + {:ok, tx} = Keys.sign_tx(account2_pub_key, 100, + Map.get(Chain.chain_state, pubkey, %{nonce: 0}).nonce + 2, 0) + assert :ok = Pool.add_transaction(tx) Miner.resume() Miner.suspend() Pool.get_and_empty_pool() - tx = create_signed_tx(account1, account2, 99, + tx = create_signed_tx(account1, account3, 99, Map.get(Chain.chain_state, account1_pub_key, %{nonce: 0}).nonce + 1, 1) assert :ok = Pool.add_transaction(tx) tx = create_signed_tx(account2, account3, 99, @@ -278,6 +283,7 @@ defmodule MultipleTransactionsTest do miner_balance_before_mining = Map.get(Chain.chain_state, pubkey).balance Miner.resume() Miner.suspend() + Pool.get_and_empty_pool() miner_balance_after_mining = Map.get(Chain.chain_state, pubkey).balance assert miner_balance_after_mining == miner_balance_before_mining + Miner.coinbase_transaction_value() + 2 end @@ -340,6 +346,35 @@ defmodule MultipleTransactionsTest do {account1, account2, account3} end + defp get_accounts_miner_fees() do + account1 = { + <<4, 231, 192, 96, 22, 175, 115, 58, 27, 93, 216, 187, 43, 116, 150, 164, 153, + 80, 134, 135, 12, 127, 173, 196, 198, 181, 84, 119, 225, 204, 150, 176, 26, + 119, 103, 128, 201, 93, 131, 7, 169, 48, 28, 60, 16, 112, 65, 8, 46, 212, 32, + 251, 135, 81, 99, 146, 67, 139, 42, 151, 183, 210, 45, 195, 8>>, + <<129, 187, 237, 185, 104, 21, 152, 221, 22, 1, 87, 152, 137, 25, 107, 214, 19, + 227, 128, 210, 180, 224, 113, 196, 232, 254, 249, 247, 230, 252, 242, 32>> + } + account2 = { + <<4, 176, 20, 135, 174, 148, 149, 10, 132, 176, 41, 79, 141, 161, 151, 96, 65, + 70, 198, 93, 25, 11, 90, 105, 57, 41, 39, 255, 197, 140, 163, 9, 180, 126, + 111, 71, 178, 86, 250, 177, 170, 211, 70, 146, 111, 201, 137, 230, 98, 8, + 205, 109, 234, 51, 50, 140, 9, 177, 130, 222, 196, 54, 98, 55, 243>>, + <<3, 213, 65, 255, 170, 53, 52, 113, 72, 39, 215, 55, 3, 120, 107, 138, 229, 5, + 32, 56, 255, 130, 166, 97, 131, 94, 156, 186, 57, 55, 189, 228>> + } + account3 = { + <<4, 163, 213, 138, 149, 50, 37, 22, 21, 221, 239, 158, 126, 245, 61, 146, 7, + 15, 86, 26, 224, 169, 46, 191, 199, 39, 172, 189, 146, 10, 111, 160, 51, 7, + 33, 236, 50, 4, 211, 92, 192, 17, 134, 144, 168, 106, 126, 235, 101, 133, + 156, 66, 66, 39, 248, 210, 14, 251, 91, 86, 59, 29, 153, 150, 190>>, + <<147, 131, 218, 194, 163, 243, 40, 42, 172, 5, 190, 120, 23, 16, 43, 0, 249, + 175, 101, 170, 182, 11, 49, 209, 39, 253, 111, 114, 182, 253, 131, 31>> + } + + {account1, account2, account3} + end + defp create_signed_tx(from_acc, to_acc, value, nonce, fee) do {from_acc_pub_key, from_acc_priv_key} = from_acc {to_acc_pub_key, _to_acc_priv_key} = to_acc From e0bd001fc51199933b57fbafe72a48ffbadc36ef Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Fri, 3 Nov 2017 12:38:01 +0200 Subject: [PATCH 6/7] [GH-#83] Fixed sum_coinbase_transactions --- apps/aecore/lib/aecore/miner/worker.ex | 10 +++++++--- .../lib/aecore/utils/blockchain/block_validation.ex | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index f7a2be5b..99f09ccf 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -114,6 +114,12 @@ 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) -> + acc + tx.data.fee + end) + end + ## Internal @spec mine_next_block(integer()) :: :ok | :error defp mine_next_block(start_nonce) do @@ -136,9 +142,7 @@ defmodule Aecore.Miner.Worker do valid_txs = BlockValidation.filter_invalid_transactions_chainstate(ordered_txs_list, chain_state) {_, pubkey} = Keys.pubkey() - total_fees = List.foldl(valid_txs, 0, fn(tx, acc) -> - acc + tx.data.fee - end) + 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) diff --git a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 19744a0b..15d6a70b 100644 --- a/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex +++ b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex @@ -17,6 +17,7 @@ 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) cond do # do not check previous block hash for genesis block, there is none @@ -36,7 +37,7 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do !(validate_block_transactions(new_block) |> Enum.all?()) -> throw({:error, "One or more transactions not valid"}) - coinbase_transactions_sum > Miner.coinbase_transaction_value() -> + coinbase_transactions_sum > Miner.coinbase_transaction_value() + total_fees -> throw({:error, "Sum of coinbase transactions values exceeds the maximum coinbase transactions value"}) new_block.header.chain_state_hash != chain_state_hash -> @@ -150,7 +151,7 @@ defmodule Aecore.Utils.Blockchain.BlockValidation do block.txs |> Enum.map( fn tx -> cond do - SignedTx.is_coinbase(tx) -> tx.data.value - tx.data.fee + SignedTx.is_coinbase(tx) -> tx.data.value true -> 0 end end From e9d35fe3fe68e0052308983d5abfe90f4fbba7ee Mon Sep 17 00:00:00 2001 From: cheezus1 Date: Fri, 3 Nov 2017 12:44:19 +0200 Subject: [PATCH 7/7] [GH-#83] Fixed coinbase tx fee --- apps/aecore/lib/aecore/miner/worker.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/aecore/lib/aecore/miner/worker.ex b/apps/aecore/lib/aecore/miner/worker.ex index 99f09ccf..028c56c6 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -106,7 +106,7 @@ defmodule Aecore.Miner.Worker do to_acc: to_acc, value: @coinbase_transaction_value + total_fees, nonce: 0, - fee: total_fees + fee: 0 } %SignedTx{data: tx_data, signature: nil}