@@ -7,109 +7,137 @@ defmodule Aecore.Chain.Worker do
7
7
8
8
alias Aecore.Structures.Block
9
9
alias Aecore.Chain.ChainState
10
- alias Aecore.Utils.Blockchain.BlockValidation
11
- alias Aecore.Utils.Blockchain.Difficulty
12
10
alias Aecore.Txs.Pool.Worker , as: Pool
11
+ alias Aecore.Utils.Blockchain.BlockValidation
12
+ alias Aecore.Peers.Worker , as: Peers
13
13
14
14
use GenServer
15
15
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__ )
22
18
end
23
19
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
+
25
29
{ :ok , initial_state }
26
30
end
27
31
28
32
@ spec latest_block ( ) :: % Block { }
29
33
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
32
39
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 )
36
41
end
37
42
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 )
41
46
end
42
47
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 } )
46
51
end
47
52
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 } )
51
56
end
52
57
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 ) )
56
61
end
57
62
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 } )
61
66
end
62
67
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 } )
66
71
end
67
72
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 }
78
76
end
79
77
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
+
85
82
if ( block != nil ) do
86
83
{ :reply , block , state }
87
84
else
88
85
{ :reply , { :error , "Block not found" } , state }
89
86
end
90
87
end
91
88
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
95
98
end
96
99
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 )
102
105
103
106
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
+
106
129
total_tokens = ChainState . calculate_total_tokens ( new_chain_state )
130
+
107
131
Logger . info ( fn ->
108
- "Added block ##{ b . header . height } with hash #{ b . header
132
+ "Added block ##{ block . header . height } with hash #{ block . header
109
133
|> BlockValidation . block_header_hash ( )
110
134
|> Base . encode16 ( ) } , total tokens: #{ total_tokens } "
111
135
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 } }
113
141
catch
114
142
{ :error , message } ->
115
143
Logger . error ( fn ->
@@ -119,15 +147,37 @@ defmodule Aecore.Chain.Worker do
119
147
end
120
148
end
121
149
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 }
125
153
end
126
154
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
132
182
end
133
183
end
0 commit comments