Skip to content

Commit 2055e9f

Browse files
author
HristianHristov
committed
[GH-#79] Checks if peers nonces are equal
2 parents 58b3f0d + c328b5c commit 2055e9f

33 files changed

+481
-288
lines changed

README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,31 @@ To suspend/stop the miner from mining:
2929
`Aecore.Miner.Worker.suspend() `
3030

3131
#### **API calls**
32-
To add block to the blockchain:
32+
To add a block to the blockchain:
3333

3434
`Aecore.Chain.Worker.add_block(%Block{}) :: :ok`
3535

36-
To get the all blocks in the current chain:
36+
To get all blocks in the current chain:
3737

3838
`Aecore.Chain.Worker.all_blocks() :: list()`
3939

40-
To get latest block added to the chain:
40+
To get the latest block added to the chain:
4141

4242
`Aecore.Chain.Worker.latest_block() :: %Block{}`
4343

4444
To get the latest chainstate:
4545

4646
`Aecore.Chain.Worker.chain_state() :: map()`
4747

48-
To add transaction to the Transaction Pool:
48+
To add a transaction to the Transaction Pool:
4949

5050
`Aecore.Txs.Pool.Worker.add_transaction(%SignedTx{}) :: :ok | :error`
5151

52-
To remove transaction from the Transaction Pool:
52+
To remove a transaction from the Transaction Pool:
5353

5454
`Aecore.Txs.Pool.Worker.remove_transaction(%SignedTx{}) :: :ok | :error`
5555

56-
To inspect transactions in the Transaction Pool:
56+
To inspect all transactions in the Transaction Pool:
5757

5858
`Aecore.Txs.Pool.Worker.get_pool() :: map() `
5959

@@ -74,4 +74,4 @@ the log can be found in the source folder under:`apps/aecore/logs`
7474

7575
## HTTP-API
7676

77-
The node will run a http api at `localhost:4000`
77+
The node will run an http API at `localhost:4000`

apps/aecore/lib/aecore.ex

+5-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ defmodule Aecore do
22
use Application
33

44
def start(_type, _args) do
5-
import Supervisor.Spec
6-
75
children = [
8-
supervisor(Aecore.Keys.Worker.Supervisor, []),
9-
supervisor(Aecore.Chain.Worker.Supervisor, []),
10-
supervisor(Aecore.Miner.Worker.Supervisor, []),
11-
supervisor(Aecore.Txs.Pool.Worker.Supervisor, []),
12-
supervisor(Aecore.Peers.Worker.Supervisor, [])
6+
Aecore.Keys.Worker.Supervisor,
7+
Aecore.Chain.Worker.Supervisor,
8+
Aecore.Miner.Worker.Supervisor,
9+
Aecore.Txs.Pool.Worker.Supervisor,
10+
Aecore.Peers.Worker.Supervisor
1311
]
1412

1513
Supervisor.start_link(children, strategy: :one_for_one)

apps/aecore/lib/aecore/chain/worker.ex

+117-67
Original file line numberDiff line numberDiff line change
@@ -7,109 +7,137 @@ defmodule Aecore.Chain.Worker do
77

88
alias Aecore.Structures.Block
99
alias Aecore.Chain.ChainState
10-
alias Aecore.Utils.Blockchain.BlockValidation
11-
alias Aecore.Utils.Blockchain.Difficulty
1210
alias Aecore.Txs.Pool.Worker, as: Pool
11+
alias Aecore.Utils.Blockchain.BlockValidation
12+
alias Aecore.Peers.Worker, as: Peers
1313

1414
use GenServer
1515

16-
def start_link do
17-
GenServer.start_link(
18-
__MODULE__,
19-
{[Block.genesis_block()], ChainState.calculate_block_state(Block.genesis_block().txs)},
20-
name: __MODULE__
21-
)
16+
def start_link(_args) do
17+
GenServer.start_link(__MODULE__, {}, name: __MODULE__)
2218
end
2319

24-
def init(initial_state) do
20+
def init(_) do
21+
genesis_block_hash = BlockValidation.block_header_hash(Block.genesis_block().header)
22+
23+
genesis_block_map = %{genesis_block_hash => Block.genesis_block()}
24+
genesis_chain_state = ChainState.calculate_block_state(Block.genesis_block().txs)
25+
latest_block_chain_state = %{genesis_block_hash => genesis_chain_state}
26+
27+
initial_state = {genesis_block_map, latest_block_chain_state}
28+
2529
{:ok, initial_state}
2630
end
2731

2832
@spec latest_block() :: %Block{}
2933
def latest_block() do
30-
GenServer.call(__MODULE__, :latest_block)
31-
end
34+
latest_block_hashes = get_latest_block_chain_state() |> Map.keys()
35+
latest_block_hash = case(length(latest_block_hashes)) do
36+
1 -> List.first(latest_block_hashes)
37+
_ -> throw({:error, "multiple or none latest block hashes"})
38+
end
3239

33-
@spec get_prior_blocks_for_validity_check() :: tuple()
34-
def get_prior_blocks_for_validity_check() do
35-
GenServer.call(__MODULE__, :get_prior_blocks_for_validity_check)
40+
get_block(latest_block_hash)
3641
end
3742

38-
@spec get_block_by_hash(term()) :: %Block{}
39-
def get_block_by_hash(hash) do
40-
GenServer.call(__MODULE__, {:get_block_by_hash, hash})
43+
@spec get_latest_block_chain_state() :: tuple()
44+
def get_latest_block_chain_state() do
45+
GenServer.call(__MODULE__, :get_latest_block_chain_state)
4146
end
4247

43-
@spec all_blocks() :: list()
44-
def all_blocks() do
45-
GenServer.call(__MODULE__, :all_blocks)
48+
@spec get_block_by_hex_hash(term()) :: %Block{}
49+
def get_block_by_hex_hash(hash) do
50+
GenServer.call(__MODULE__, {:get_block_by_hex_hash, hash})
4651
end
4752

48-
@spec add_block(%Block{}) :: :ok
49-
def add_block(%Block{} = b) do
50-
GenServer.call(__MODULE__, {:add_block, b})
53+
@spec get_block(term()) :: %Block{}
54+
def get_block(hash) do
55+
GenServer.call(__MODULE__, {:get_block, hash})
5156
end
5257

53-
@spec chain_state() :: map()
54-
def chain_state() do
55-
GenServer.call(__MODULE__, :chain_state)
58+
@spec get_blocks(binary(), integer()) :: :ok
59+
def get_blocks(start_block_hash, size) do
60+
Enum.reverse(get_blocks([], start_block_hash, size))
5661
end
5762

58-
@spec get_blocks_for_difficulty_calculation() :: list()
59-
def get_blocks_for_difficulty_calculation() do
60-
GenServer.call(__MODULE__, :get_blocks_for_difficulty_calculation)
63+
@spec add_block(%Block{}) :: :ok
64+
def add_block(%Block{} = block) do
65+
GenServer.call(__MODULE__, {:add_block, block})
6166
end
6267

63-
def handle_call(:latest_block, _from, state) do
64-
[lb | _] = elem(state, 0)
65-
{:reply, lb, state}
68+
@spec chain_state(binary()) :: map()
69+
def chain_state(latest_block_hash) do
70+
GenServer.call(__MODULE__, {:chain_state, latest_block_hash})
6671
end
6772

68-
def handle_call(:get_prior_blocks_for_validity_check, _from, state) do
69-
chain = elem(state, 0)
70-
71-
if length(chain) == 1 do
72-
[lb | _] = chain
73-
{:reply, {lb, nil}, state}
74-
else
75-
[lb, prev | _] = chain
76-
{:reply, {lb, prev}, state}
77-
end
73+
def handle_call(:get_latest_block_chain_state, _from, state) do
74+
{_, latest_block_chain_state} = state
75+
{:reply, latest_block_chain_state, state}
7876
end
7977

80-
def handle_call({:get_block_by_hash, hash}, _from, state) do
81-
block = Enum.find(elem(state, 0), fn(block) ->
82-
block.header
83-
|> BlockValidation.block_header_hash()
84-
|> Base.encode16() == hash end)
78+
def handle_call({:get_block, block_hash}, _from, state) do
79+
{block_map, _} = state
80+
block = block_map[block_hash]
81+
8582
if(block != nil) do
8683
{:reply, block, state}
8784
else
8885
{:reply, {:error, "Block not found"}, state}
8986
end
9087
end
9188

92-
def handle_call(:all_blocks, _from, state) do
93-
chain = elem(state, 0)
94-
{:reply, chain, state}
89+
def handle_call({:get_block_by_hex_hash, hash}, _from, state) do
90+
{chain, _} = state
91+
case(Enum.find(chain, fn{block_hash, _block} ->
92+
block_hash |> Base.encode16() == hash end)) do
93+
{_, block} ->
94+
{:reply, block, state}
95+
nil ->
96+
{:reply, {:error, "Block not found"}, state}
97+
end
9598
end
9699

97-
def handle_call({:add_block, %Block{} = b}, _from, state) do
98-
{chain, prev_chain_state} = state
99-
[prior_block | _] = chain
100-
new_block_state = ChainState.calculate_block_state(b.txs)
101-
new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_chain_state)
100+
def handle_call({:add_block, %Block{} = block}, _from, state) do
101+
{chain, chain_state} = state
102+
prev_block_chain_state = chain_state[block.header.prev_hash]
103+
new_block_state = ChainState.calculate_block_state(block.txs)
104+
new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_block_chain_state)
102105

103106
try do
104-
BlockValidation.validate_block!(b, prior_block, new_chain_state)
105-
Enum.each(b.txs, fn(tx) -> Pool.remove_transaction(tx) end)
107+
BlockValidation.validate_block!(block, chain[block.header.prev_hash], new_chain_state)
108+
109+
Enum.each(block.txs, fn(tx) -> Pool.remove_transaction(tx) end)
110+
111+
{block_map, latest_block_chain_state} = state
112+
block_hash = BlockValidation.block_header_hash(block.header)
113+
updated_block_map = Map.put(block_map, block_hash, block)
114+
has_prev_block = Map.has_key?(latest_block_chain_state, block.header.prev_hash)
115+
116+
{deleted_latest_chain_state, prev_chain_state} = case has_prev_block do
117+
true ->
118+
prev_chain_state = Map.get(latest_block_chain_state, block.header.prev_hash)
119+
{Map.delete(latest_block_chain_state, block.header.prev_hash), prev_chain_state}
120+
false ->
121+
{latest_block_chain_state, %{}}
122+
end
123+
124+
new_block_state = ChainState.calculate_block_state(block.txs)
125+
new_chain_state = ChainState.calculate_chain_state(new_block_state, prev_chain_state)
126+
127+
updated_latest_block_chainstate = Map.put(deleted_latest_chain_state, block_hash, new_chain_state)
128+
106129
total_tokens = ChainState.calculate_total_tokens(new_chain_state)
130+
107131
Logger.info(fn ->
108-
"Added block ##{b.header.height} with hash #{b.header
132+
"Added block ##{block.header.height} with hash #{block.header
109133
|> BlockValidation.block_header_hash()
110134
|> Base.encode16()}, total tokens: #{total_tokens}"
111135
end)
112-
{:reply, :ok, {[b | chain], new_chain_state}}
136+
137+
## Block was validated, now we can send it to other peers
138+
Peers.broadcast_to_all({:new_block, block})
139+
140+
{:reply, :ok, {updated_block_map, updated_latest_block_chainstate}}
113141
catch
114142
{:error, message} ->
115143
Logger.error(fn ->
@@ -119,15 +147,37 @@ defmodule Aecore.Chain.Worker do
119147
end
120148
end
121149

122-
def handle_call(:chain_state, _from, state) do
123-
chain_state = elem(state, 1)
124-
{:reply, chain_state, state}
150+
def handle_call({:chain_state, latest_block_hash}, _from, state) do
151+
{_, chain_state} = state
152+
{:reply, chain_state[latest_block_hash], state}
125153
end
126154

127-
def handle_call(:get_blocks_for_difficulty_calculation, _from, state) do
128-
chain = elem(state, 0)
129-
number_of_blocks = Difficulty.get_number_of_blocks()
130-
blocks_for_difficulty_calculation = Enum.take(chain, number_of_blocks)
131-
{:reply, blocks_for_difficulty_calculation, state}
155+
def chain_state() do
156+
latest_block = latest_block()
157+
latest_block_hash = BlockValidation.block_header_hash(latest_block.header)
158+
chain_state(latest_block_hash)
159+
end
160+
161+
def all_blocks() do
162+
latest_block_obj = latest_block()
163+
latest_block_hash = BlockValidation.block_header_hash(latest_block_obj.header)
164+
get_blocks(latest_block_hash, latest_block_obj.header.height)
165+
end
166+
167+
defp get_blocks(blocks_acc, next_block_hash, size) do
168+
cond do
169+
size > 0 ->
170+
case(GenServer.call(__MODULE__, {:get_block, next_block_hash})) do
171+
{:error, _} -> blocks_acc
172+
block ->
173+
updated_block_acc = [block | blocks_acc]
174+
prev_block_hash = block.header.prev_hash
175+
next_size = size - 1
176+
177+
get_blocks(updated_block_acc, prev_block_hash, next_size)
178+
end
179+
true ->
180+
blocks_acc
181+
end
132182
end
133183
end
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
defmodule Aecore.Chain.Worker.Supervisor do
22
use Supervisor
33

4-
def start_link() do
4+
def start_link(_args) do
55
Supervisor.start_link(__MODULE__, :ok)
66
end
77

88
def init(:ok) do
99
children = [
10-
worker(Aecore.Chain.Worker, [])
10+
Aecore.Chain.Worker
1111
]
1212

13-
supervise(children, strategy: :one_for_one)
13+
Supervisor.init(children, strategy: :one_for_one)
1414
end
1515
end

apps/aecore/lib/aecore/keys/worker.ex

+18-8
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ defmodule Aecore.Keys.Worker do
99

1010
@filename_pub "key.pub"
1111
@filename_priv "key"
12+
@pub_key_length 65
1213

13-
def start_link() do
14+
def start_link(_args) do
1415
GenServer.start_link(
1516
__MODULE__,
1617
%{
@@ -123,19 +124,23 @@ defmodule Aecore.Keys.Worker do
123124
0
124125
}
125126
end
126-
127127
def handle_call(
128128
{:verify, {term, signature, pub_key}},
129129
_from,
130130
%{algo: algo, digest: digest, curve: curve} = state
131131
) do
132-
result =
133-
:crypto.verify(algo, digest, :erlang.term_to_binary(term), signature, [
134-
pub_key,
135-
:crypto.ec_curve(curve)
136-
])
132+
case is_valid_pub_key(pub_key) do
133+
true ->
134+
result =
135+
:crypto.verify(algo, digest, :erlang.term_to_binary(term), signature, [
136+
pub_key,
137+
:crypto.ec_curve(curve)
138+
])
137139

138-
{:reply, result, state}
140+
{:reply, result, state}
141+
false ->
142+
{:reply, {:error, "Key length is not valid!"}, state}
143+
end
139144
end
140145

141146
def handle_call(
@@ -294,6 +299,11 @@ defmodule Aecore.Keys.Worker do
294299
pub
295300
end
296301

302+
defp is_valid_pub_key(pub_key_str) do
303+
pub_key_str
304+
|> byte_size() == @pub_key_length
305+
end
306+
297307
defp padding128(bin) do
298308
pad0 = 128 - :erlang.size(bin)
299309
pad1 = pad0 * 8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
defmodule Aecore.Keys.Worker.Supervisor do
22
use Supervisor
33

4-
def start_link() do
4+
def start_link(_args) do
55
Supervisor.start_link(__MODULE__, :ok)
66
end
77

88
def init(:ok) do
99
children = [
10-
worker(Aecore.Keys.Worker, [])
10+
Aecore.Keys.Worker
1111
]
1212

13-
supervise(children, strategy: :one_for_one)
13+
Supervisor.init(children, strategy: :one_for_one)
1414
end
1515
end

0 commit comments

Comments
 (0)