@@ -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
133183end
0 commit comments