@@ -10,13 +10,11 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
10
10
11
11
alias LambdaEthereumConsensus.ForkChoice
12
12
alias LambdaEthereumConsensus.P2P.BlockDownloader
13
+ alias LambdaEthereumConsensus.Store.Blocks
13
14
alias Types.SignedBeaconBlock
14
15
15
- @ type state :: % {
16
- pending_blocks: % { Types . root ( ) => SignedBeaconBlock . t ( ) } ,
17
- invalid_blocks: % { Types . root ( ) => map ( ) } ,
18
- blocks_to_download: MapSet . t ( Types . root ( ) )
19
- }
16
+ @ type block_status :: :pending | :invalid | :processing | :download | :unknown
17
+ @ type state :: % { Types . root ( ) => { SignedBeaconBlock . t ( ) | nil , block_status ( ) } }
20
18
21
19
##########################
22
20
### Public API
@@ -31,11 +29,6 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
31
29
GenServer . cast ( __MODULE__ , { :add_block , signed_block } )
32
30
end
33
31
34
- @ spec pending_block? ( Types . root ( ) ) :: boolean ( )
35
- def pending_block? ( block_root ) do
36
- GenServer . call ( __MODULE__ , { :pending_block? , block_root } )
37
- end
38
-
39
32
##########################
40
33
### GenServer Callbacks
41
34
##########################
@@ -45,19 +38,34 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
45
38
def init ( _opts ) do
46
39
schedule_blocks_processing ( )
47
40
schedule_blocks_download ( )
48
- { :ok , % { pending_blocks: % { } , invalid_blocks: % { } , blocks_to_download: MapSet . new ( ) } }
41
+
42
+ { :ok , Map . new ( ) }
49
43
end
50
44
45
+ @ spec handle_cast ( any ( ) , state ( ) ) :: { :noreply , state ( ) }
46
+
51
47
@ impl true
52
48
def handle_cast ( { :add_block , % SignedBeaconBlock { message: block } = signed_block } , state ) do
53
49
block_root = Ssz . hash_tree_root! ( block )
54
- pending_blocks = Map . put ( state . pending_blocks , block_root , signed_block )
55
- { :noreply , Map . put ( state , :pending_blocks , pending_blocks ) }
50
+
51
+ if state |> Map . get ( block_root ) do
52
+ { :noreply , state }
53
+ else
54
+ { :noreply , state |> Map . put ( block_root , { signed_block , :pending } ) }
55
+ end
56
56
end
57
57
58
58
@ impl true
59
- def handle_call ( { :pending_block? , block_root } , _from , state ) do
60
- { :reply , Map . has_key? ( state . pending_blocks , block_root ) , state }
59
+ def handle_cast ( { :block_processed , block_root , is_valid? } , state ) do
60
+ if is_valid? do
61
+ state |> Map . delete ( block_root )
62
+ else
63
+ state
64
+ |> Map . put ( block_root , { nil , :invalid } )
65
+ end
66
+ |> then ( fn state ->
67
+ { :noreply , state }
68
+ end )
61
69
end
62
70
63
71
@ spec handle_info ( any ( ) , state ( ) ) :: { :noreply , state ( ) }
@@ -68,44 +76,40 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
68
76
@ impl true
69
77
@ spec handle_info ( atom ( ) , state ( ) ) :: { :noreply , state ( ) }
70
78
def handle_info ( :process_blocks , state ) do
71
- state . pending_blocks
79
+ state
80
+ |> Map . filter ( fn { _ , { _ , s } } -> s == :pending end )
81
+ |> Enum . map ( fn { root , { block , _ } } -> { root , block } end )
72
82
|> Enum . sort_by ( fn { _ , signed_block } -> signed_block . message . slot end )
73
83
|> Enum . reduce ( state , fn { block_root , signed_block } , state ->
74
84
parent_root = signed_block . message . parent_root
85
+ parent_status = get_block_status ( state , parent_root )
75
86
76
87
cond do
88
+ # If already processed, remove it
89
+ Blocks . get_block ( block_root ) ->
90
+ state |> Map . delete ( block_root )
91
+
77
92
# If parent is invalid, block is invalid
78
- state . invalid_blocks |> Map . has_key? ( parent_root ) ->
93
+ parent_status == :invalid ->
94
+ state |> Map . put ( block_root , { nil , :invalid } )
95
+
96
+ # If parent is processing, block is pending
97
+ parent_status == :processing ->
79
98
state
80
- |> Map . update! ( :pending_blocks , & Map . delete ( & 1 , block_root ) )
81
- |> Map . update! (
82
- :invalid_blocks ,
83
- & Map . put ( & 1 , block_root , signed_block . message |> Map . take ( [ :slot , :parent_root ] ) )
84
- )
85
99
86
100
# If parent is pending, block is pending
87
- state . pending_blocks |> Map . has_key? ( parent_root ) ->
101
+ parent_status == :pending ->
88
102
state
89
103
90
- # If already in fork choice, remove from pending
91
- ForkChoice . has_block? ( block_root ) ->
92
- state |> Map . update! ( :pending_blocks , & Map . delete ( & 1 , block_root ) )
93
-
94
104
# If parent is not in fork choice, download parent
95
- not ForkChoice . has_block? ( parent_root ) ->
96
- state |> Map . update! ( :blocks_to_download , & MapSet . put ( & 1 , parent_root ) )
105
+ ! Blocks . get_block ( parent_root ) ->
106
+ state |> Map . put ( parent_root , { nil , :download } )
97
107
98
108
# If all the other conditions are false, add block to fork choice
99
109
true ->
100
- new_state = send_block_to_forkchoice ( state , signed_block , block_root )
101
-
102
- # When on checkpoint sync, we might accumulate a couple of hundred blocks in the pending blocks queue.
103
- # This can cause the ForkChoice to timeout on other call requests since it has to process all the
104
- # pending blocks first.
105
- # TODO: find a better way to handle this
106
- Process . sleep ( 100 )
107
-
108
- new_state
110
+ Logger . info ( "Adding block to fork choice: " , root: block_root )
111
+ ForkChoice . on_block ( signed_block , block_root )
112
+ state |> Map . put ( block_root , { signed_block , :processing } )
109
113
end
110
114
end )
111
115
|> then ( fn state ->
@@ -114,22 +118,12 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
114
118
end )
115
119
end
116
120
117
- @ empty_mapset MapSet . new ( )
118
-
119
- @ impl true
120
- def handle_info ( :download_blocks , % { blocks_to_download: to_download } = state )
121
- when to_download == @ empty_mapset do
122
- schedule_blocks_download ( )
123
- { :noreply , state }
124
- end
125
-
126
121
@ impl true
127
122
def handle_info ( :download_blocks , state ) do
128
- blocks_in_store = state . blocks_to_download |> MapSet . filter ( & ForkChoice . has_block? / 1 )
123
+ blocks_to_download = state |> Map . filter ( fn { _ , { _ , s } } -> s == :download end ) |> Map . keys ( )
129
124
130
125
downloaded_blocks =
131
- state . blocks_to_download
132
- |> MapSet . difference ( blocks_in_store )
126
+ blocks_to_download
133
127
|> Enum . take ( 16 )
134
128
|> BlockDownloader . request_blocks_by_root ( )
135
129
|> case do
@@ -141,38 +135,24 @@ defmodule LambdaEthereumConsensus.Beacon.PendingBlocks do
141
135
[ ]
142
136
end
143
137
144
- for signed_block <- downloaded_blocks do
145
- add_block ( signed_block )
146
- end
147
-
148
- roots_to_remove =
138
+ new_state =
149
139
downloaded_blocks
150
- |> Enum . map ( & Ssz . hash_tree_root! ( & 1 . message ) )
151
- |> MapSet . new ( )
152
- |> MapSet . union ( blocks_in_store )
140
+ |> Enum . reduce ( state , fn signed_block , state ->
141
+ block_root = Ssz . hash_tree_root! ( signed_block . message )
142
+ state |> Map . put ( block_root , { signed_block , :pending } )
143
+ end )
153
144
154
145
schedule_blocks_download ( )
155
- { :noreply , Map . update! ( state , :blocks_to_download , & MapSet . difference ( & 1 , roots_to_remove ) ) }
146
+ { :noreply , new_state }
156
147
end
157
148
158
149
##########################
159
150
### Private Functions
160
151
##########################
161
152
162
- @ spec send_block_to_forkchoice ( state ( ) , SignedBeaconBlock . t ( ) , Types . root ( ) ) :: state ( )
163
- defp send_block_to_forkchoice ( state , signed_block , block_root ) do
164
- case ForkChoice . on_block ( signed_block , block_root ) do
165
- :ok ->
166
- state |> Map . update! ( :pending_blocks , & Map . delete ( & 1 , block_root ) )
167
-
168
- :error ->
169
- state
170
- |> Map . update! ( :pending_blocks , & Map . delete ( & 1 , block_root ) )
171
- |> Map . update! (
172
- :invalid_blocks ,
173
- & Map . put ( & 1 , block_root , signed_block . message |> Map . take ( [ :slot , :parent_root ] ) )
174
- )
175
- end
153
+ @ spec get_block_status ( state ( ) , Types . root ( ) ) :: block_status ( )
154
+ defp get_block_status ( state , block_root ) do
155
+ state |> Map . get ( block_root , { nil , :unknown } ) |> elem ( 1 )
176
156
end
177
157
178
158
def schedule_blocks_processing do
0 commit comments