@@ -22,7 +22,6 @@ import (
22
22
"fmt"
23
23
"math/big"
24
24
"runtime"
25
- "sync/atomic"
26
25
"time"
27
26
28
27
"github.com/ethereum/go-ethereum/common"
46
45
// codebase, inherently breaking if the engine is swapped out. Please put common
47
46
// error types into the consensus package.
48
47
var (
49
- errInvalidChain = errors .New ("invalid header chain" )
50
48
errLargeBlockTime = errors .New ("timestamp too big" )
51
49
errZeroBlockTime = errors .New ("timestamp equals parent's" )
52
50
errTooManyUncles = errors .New ("too many uncles" )
@@ -90,111 +88,80 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He
90
88
// a results channel to retrieve the async verifications.
91
89
func (ethash * Ethash ) VerifyHeaders (chain consensus.ChainReader , headers []* types.Header , seals []bool ) (chan <- struct {}, <- chan error ) {
92
90
// If we're running a full engine faking, accept any input as valid
93
- if ethash .fakeFull {
91
+ if ethash .fakeFull || len ( headers ) == 0 {
94
92
abort , results := make (chan struct {}), make (chan error , len (headers ))
95
93
for i := 0 ; i < len (headers ); i ++ {
96
94
results <- nil
97
95
}
98
96
return abort , results
99
97
}
98
+
100
99
// Spawn as many workers as allowed threads
101
100
workers := runtime .GOMAXPROCS (0 )
102
101
if len (headers ) < workers {
103
102
workers = len (headers )
104
103
}
105
- // Create a task channel and spawn the verifiers
106
- type result struct {
107
- index int
108
- err error
109
- }
110
- inputs := make (chan int , workers )
111
- outputs := make (chan result , len (headers ))
112
104
113
- var badblock uint64
105
+ // Create a task channel and spawn the verifiers
106
+ var (
107
+ inputs = make (chan int )
108
+ done = make (chan int , workers )
109
+ errors = make ([]error , len (headers ))
110
+ abort = make (chan struct {})
111
+ )
114
112
for i := 0 ; i < workers ; i ++ {
115
113
go func () {
116
114
for index := range inputs {
117
- // If we've found a bad block already before this, stop validating
118
- if bad := atomic .LoadUint64 (& badblock ); bad != 0 && bad <= headers [index ].Number .Uint64 () {
119
- outputs <- result {index : index , err : errInvalidChain }
120
- continue
121
- }
122
- // We need to look up the first parent
123
- var parent * types.Header
124
- if index == 0 {
125
- parent = chain .GetHeader (headers [0 ].ParentHash , headers [0 ].Number .Uint64 ()- 1 )
126
- } else if headers [index - 1 ].Hash () == headers [index ].ParentHash {
127
- parent = headers [index - 1 ]
128
- }
129
- // Ensure the validation is useful and execute it
130
- var failure error
131
- switch {
132
- case chain .GetHeader (headers [index ].Hash (), headers [index ].Number .Uint64 ()- 1 ) != nil :
133
- outputs <- result {index : index , err : nil }
134
- case parent == nil :
135
- failure = consensus .ErrUnknownAncestor
136
- outputs <- result {index : index , err : failure }
137
- default :
138
- failure = ethash .verifyHeader (chain , headers [index ], parent , false , seals [index ])
139
- outputs <- result {index : index , err : failure }
140
- }
141
- // If a validation failure occurred, mark subsequent blocks invalid
142
- if failure != nil {
143
- number := headers [index ].Number .Uint64 ()
144
- if prev := atomic .LoadUint64 (& badblock ); prev == 0 || prev > number {
145
- // This two step atomic op isn't thread-safe in that `badblock` might end
146
- // up slightly higher than the block number of the first failure (if many
147
- // workers try to write at the same time), but it's fine as we're mostly
148
- // interested to avoid large useless work, we don't care about 1-2 extra
149
- // runs. Doing "full thread safety" would involve mutexes, which would be
150
- // a noticeable sync overhead on the fast spinning worker routines.
151
- atomic .StoreUint64 (& badblock , number )
152
- }
153
- }
115
+ errors [index ] = ethash .verifyHeaderWorker (chain , headers , seals , index )
116
+ done <- index
154
117
}
155
118
}()
156
119
}
157
- // Feed item indices to the workers until done, sorting and feeding the results to the caller
158
- dones := make ([]bool , len (headers ))
159
- errors := make ([]error , len (headers ))
160
-
161
- abort := make (chan struct {})
162
- returns := make (chan error , len (headers ))
163
120
121
+ errorsOut := make (chan error , len (headers ))
164
122
go func () {
165
123
defer close (inputs )
166
-
167
- input , output := 0 , 0
168
- for i := 0 ; i < len (headers )* 2 ; i ++ {
169
- var res result
170
-
171
- // If there are tasks left, push to workers
172
- if input < len (headers ) {
173
- select {
174
- case inputs <- input :
175
- input ++
176
- continue
177
- case <- abort :
178
- return
179
- case res = <- outputs :
124
+ var (
125
+ in , out = 0 , 0
126
+ checked = make ([]bool , len (headers ))
127
+ inputs = inputs
128
+ )
129
+ for {
130
+ select {
131
+ case inputs <- in :
132
+ if in ++ ; in == len (headers ) {
133
+ // Reached end of headers. Stop sending to workers.
134
+ inputs = nil
180
135
}
181
- } else {
182
- // Otherwise keep waiting for results
183
- select {
184
- case <- abort :
185
- return
186
- case res = <- outputs :
136
+ case index := <- done :
137
+ for checked [ index ] = true ; checked [ out ]; out ++ {
138
+ errorsOut <- errors [ out ]
139
+ if out == len ( headers ) - 1 {
140
+ return
141
+ }
187
142
}
188
- }
189
- // A result arrived, save and propagate if next
190
- dones [res .index ], errors [res .index ] = true , res .err
191
- for output < len (headers ) && dones [output ] {
192
- returns <- errors [output ]
193
- output ++
143
+ case <- abort :
144
+ return
194
145
}
195
146
}
196
147
}()
197
- return abort , returns
148
+ return abort , errorsOut
149
+ }
150
+
151
+ func (ethash * Ethash ) verifyHeaderWorker (chain consensus.ChainReader , headers []* types.Header , seals []bool , index int ) error {
152
+ var parent * types.Header
153
+ if index == 0 {
154
+ parent = chain .GetHeader (headers [0 ].ParentHash , headers [0 ].Number .Uint64 ()- 1 )
155
+ } else if headers [index - 1 ].Hash () == headers [index ].ParentHash {
156
+ parent = headers [index - 1 ]
157
+ }
158
+ if parent == nil {
159
+ return consensus .ErrUnknownAncestor
160
+ }
161
+ if chain .GetHeader (headers [index ].Hash (), headers [index ].Number .Uint64 ()) != nil {
162
+ return nil // known block
163
+ }
164
+ return ethash .verifyHeader (chain , headers [index ], parent , false , seals [index ])
198
165
}
199
166
200
167
// VerifyUncles verifies that the given block's uncles conform to the consensus
0 commit comments