@@ -7,114 +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
13
12
alias Aecore.Peers.Worker , as: Peers
14
13
15
14
use GenServer
16
15
17
16
def start_link do
18
- GenServer . start_link (
19
- __MODULE__ ,
20
- { [ Block . genesis_block ( ) ] , ChainState . calculate_block_state ( Block . genesis_block ( ) . txs ) } ,
21
- name: __MODULE__
22
- )
17
+ GenServer . start_link ( __MODULE__ , { } , name: __MODULE__ )
23
18
end
24
19
25
- 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
+
26
29
{ :ok , initial_state }
27
30
end
28
31
29
32
@ spec latest_block ( ) :: % Block { }
30
33
def latest_block ( ) do
31
- GenServer . call ( __MODULE__ , :latest_block )
32
- 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
33
39
34
- @ spec get_prior_blocks_for_validity_check ( ) :: tuple ( )
35
- def get_prior_blocks_for_validity_check ( ) do
36
- GenServer . call ( __MODULE__ , :get_prior_blocks_for_validity_check )
40
+ get_block ( latest_block_hash )
37
41
end
38
42
39
- @ spec get_block_by_hash ( term ( ) ) :: % Block { }
40
- def get_block_by_hash ( hash ) do
41
- 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 )
42
46
end
43
47
44
- @ spec all_blocks ( ) :: list ( )
45
- def all_blocks ( ) do
46
- 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 } )
47
51
end
48
52
49
- @ spec add_block ( % Block { } ) :: :ok
50
- def add_block ( % Block { } = b ) do
51
- 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 } )
52
56
end
53
57
54
- @ spec chain_state ( ) :: map ( )
55
- def chain_state ( ) do
56
- 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 ) )
57
61
end
58
62
59
- @ spec get_blocks_for_difficulty_calculation ( ) :: list ( )
60
- def get_blocks_for_difficulty_calculation ( ) do
61
- 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 } )
62
66
end
63
67
64
- def handle_call ( :latest_block , _from , state ) do
65
- [ lb | _ ] = elem ( state , 0 )
66
- { :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 } )
67
71
end
68
72
69
- def handle_call ( :get_prior_blocks_for_validity_check , _from , state ) do
70
- chain = elem ( state , 0 )
71
-
72
- if length ( chain ) == 1 do
73
- [ lb | _ ] = chain
74
- { :reply , { lb , nil } , state }
75
- else
76
- [ lb , prev | _ ] = chain
77
- { :reply , { lb , prev } , state }
78
- 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 }
79
76
end
80
77
81
- def handle_call ( { :get_block_by_hash , hash } , _from , state ) do
82
- block = Enum . find ( elem ( state , 0 ) , fn ( block ) ->
83
- block . header
84
- |> BlockValidation . block_header_hash ( )
85
- |> 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
+
86
82
if ( block != nil ) do
87
83
{ :reply , block , state }
88
84
else
89
85
{ :reply , { :error , "Block not found" } , state }
90
86
end
91
87
end
92
88
93
- def handle_call ( :all_blocks , _from , state ) do
94
- chain = elem ( state , 0 )
95
- { :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
96
98
end
97
99
98
- def handle_call ( { :add_block , % Block { } = b } , _from , state ) do
99
- { chain , prev_chain_state } = state
100
- [ prior_block | _ ] = chain
101
- new_block_state = ChainState . calculate_block_state ( b . txs )
102
- 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 )
103
105
104
106
try do
105
- BlockValidation . validate_block! ( b , prior_block , new_chain_state )
106
- 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
+
107
129
total_tokens = ChainState . calculate_total_tokens ( new_chain_state )
130
+
108
131
Logger . info ( fn ->
109
- "Added block ##{ b . header . height } with hash #{ b . header
132
+ "Added block ##{ block . header . height } with hash #{ block . header
110
133
|> BlockValidation . block_header_hash ( )
111
134
|> Base . encode16 ( ) } , total tokens: #{ total_tokens } "
112
135
end )
113
136
114
137
## Block was validated, now we can send it to other peers
115
- Peers . broadcast_to_all ( { :new_block , b } )
138
+ Peers . broadcast_to_all ( { :new_block , block } )
116
139
117
- { :reply , :ok , { [ b | chain ] , new_chain_state } }
140
+ { :reply , :ok , { updated_block_map , updated_latest_block_chainstate } }
118
141
catch
119
142
{ :error , message } ->
120
143
Logger . error ( fn ->
@@ -124,15 +147,37 @@ defmodule Aecore.Chain.Worker do
124
147
end
125
148
end
126
149
127
- def handle_call ( :chain_state , _from , state ) do
128
- chain_state = elem ( state , 1 )
129
- { :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 }
130
153
end
131
154
132
- def handle_call ( :get_blocks_for_difficulty_calculation , _from , state ) do
133
- chain = elem ( state , 0 )
134
- number_of_blocks = Difficulty . get_number_of_blocks ( )
135
- blocks_for_difficulty_calculation = Enum . take ( chain , number_of_blocks )
136
- { :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
137
182
end
138
183
end
0 commit comments