diff --git a/apps/aecore/lib/aecore/chain/chain_state.ex b/apps/aecore/lib/aecore/chain/chain_state.ex index 4e6346ae..35a9364e 100644 --- a/apps/aecore/lib/aecore/chain/chain_state.ex +++ b/apps/aecore/lib/aecore/chain/chain_state.ex @@ -19,7 +19,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 diff --git a/apps/aecore/lib/aecore/chain/worker.ex b/apps/aecore/lib/aecore/chain/worker.ex index 3a448dfc..29c7a0d0 100644 --- a/apps/aecore/lib/aecore/chain/worker.ex +++ b/apps/aecore/lib/aecore/chain/worker.ex @@ -8,6 +8,7 @@ defmodule Aecore.Chain.Worker do alias Aecore.Structures.Block alias Aecore.Chain.ChainState alias Aecore.Txs.Pool.Worker, as: Pool + alias Aecore.Keys.Worker, as: Keys alias Aecore.Utils.Blockchain.BlockValidation alias Aecore.Peers.Worker, as: Peers diff --git a/apps/aecore/lib/aecore/keys/worker.ex b/apps/aecore/lib/aecore/keys/worker.ex index 632622aa..e7daac88 100644 --- a/apps/aecore/lib/aecore/keys/worker.ex +++ b/apps/aecore/lib/aecore/keys/worker.ex @@ -39,10 +39,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 d71ffd42..028c56c6 100644 --- a/apps/aecore/lib/aecore/miner/worker.ex +++ b/apps/aecore/lib/aecore/miner/worker.ex @@ -100,12 +100,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, - nonce: 0 + value: @coinbase_transaction_value + total_fees, + nonce: 0, + fee: 0 } %SignedTx{data: tx_data, signature: nil} @@ -113,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 @@ -135,7 +142,8 @@ 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 = calculate_total_fees(valid_txs) + 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) 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/lib/aecore/utils/blockchain/block_validation.ex b/apps/aecore/lib/aecore/utils/blockchain/block_validation.ex index 695f81af..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 -> 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 0d16c09e..f5a7a605 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, 1) end test "check pubkey length" do diff --git a/apps/aecore/test/aecore_tx_test.exs b/apps/aecore/test/aecore_tx_test.exs index 44222436..365feb07 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 c78779ef..fd97e6b5 100644 --- a/apps/aecore/test/aecore_txs_pool_test.exs +++ b/apps/aecore/test/aecore_txs_pool_test.exs @@ -17,8 +17,10 @@ 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, 1) + {:ok, tx2} = Keys.sign_tx(to_account, 5, + 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 6262d1c8..d4a2cbf6 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,161 @@ 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 + + @tag timeout: 10000000 + test "in one block, miner collects all the fees from the transactions" do + {account1, account2, account3} = get_accounts_miner_fees() + {account1_pub_key, _account1_priv_key} = account1 + {account2_pub_key, _account2_priv_key} = account2 + 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, 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, + 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() + 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 defp get_accounts_one_block() do @@ -310,10 +346,39 @@ defmodule MultipleTransactionsTest do {account1, account2, account3} end - defp create_signed_tx(from_acc, to_acc, value, nonce) do + 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 - {: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} diff --git a/apps/aehttpclient/test/aehttpclient_test.exs b/apps/aehttpclient/test/aehttpclient_test.exs index 3f1038b1..dc3f5c60 100644 --- a/apps/aehttpclient/test/aehttpclient_test.exs +++ b/apps/aehttpclient/test/aehttpclient_test.exs @@ -21,9 +21,9 @@ defmodule AehttpclientTest do def add_txs_to_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) Pool.add_transaction(tx1) Pool.add_transaction(tx2) end