Skip to content

Commit 12c0afe

Browse files
committed
Merge pull request ethereum#1822 from karalabe/contain-pow
core: separate and contain POW verifier, extensive tests
2 parents e40b447 + 399c920 commit 12c0afe

File tree

4 files changed

+327
-62
lines changed

4 files changed

+327
-62
lines changed

core/chain_manager.go

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"fmt"
2323
"io"
2424
"math/big"
25-
"runtime"
2625
"sync"
2726
"sync/atomic"
2827
"time"
@@ -616,14 +615,12 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
616615
stats struct{ queued, processed, ignored int }
617616
tstart = time.Now()
618617

619-
nonceDone = make(chan nonceResult, len(chain))
620-
nonceQuit = make(chan struct{})
621618
nonceChecked = make([]bool, len(chain))
622619
)
623620

624621
// Start the parallel nonce verifier.
625-
go verifyNonces(self.pow, chain, nonceQuit, nonceDone)
626-
defer close(nonceQuit)
622+
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
623+
defer close(nonceAbort)
627624

628625
txcount := 0
629626
for i, block := range chain {
@@ -636,11 +633,11 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
636633
// Wait for block i's nonce to be verified before processing
637634
// its state transition.
638635
for !nonceChecked[i] {
639-
r := <-nonceDone
640-
nonceChecked[r.i] = true
636+
r := <-nonceResults
637+
nonceChecked[r.index] = true
641638
if !r.valid {
642-
block := chain[r.i]
643-
return r.i, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
639+
block := chain[r.index]
640+
return r.index, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
644641
}
645642
}
646643

@@ -843,40 +840,3 @@ func blockErr(block *types.Block, err error) {
843840
glog.V(logger.Error).Infoln(err)
844841
glog.V(logger.Debug).Infoln(verifyNonces)
845842
}
846-
847-
type nonceResult struct {
848-
i int
849-
valid bool
850-
}
851-
852-
// block verifies nonces of the given blocks in parallel and returns
853-
// an error if one of the blocks nonce verifications failed.
854-
func verifyNonces(pow pow.PoW, blocks []*types.Block, quit <-chan struct{}, done chan<- nonceResult) {
855-
// Spawn a few workers. They listen for blocks on the in channel
856-
// and send results on done. The workers will exit in the
857-
// background when in is closed.
858-
var (
859-
in = make(chan int)
860-
nworkers = runtime.GOMAXPROCS(0)
861-
)
862-
defer close(in)
863-
if len(blocks) < nworkers {
864-
nworkers = len(blocks)
865-
}
866-
for i := 0; i < nworkers; i++ {
867-
go func() {
868-
for i := range in {
869-
done <- nonceResult{i: i, valid: pow.Verify(blocks[i])}
870-
}
871-
}()
872-
}
873-
// Feed block indices to the workers.
874-
for i := range blocks {
875-
select {
876-
case in <- i:
877-
continue
878-
case <-quit:
879-
return
880-
}
881-
}
882-
}

core/chain_manager_test.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ func TestInsertNonceError(t *testing.T) {
456456

457457
fail := rand.Int() % len(blocks)
458458
failblock := blocks[fail]
459-
bc.pow = failpow{failblock.NumberU64()}
459+
bc.pow = failPow{failblock.NumberU64()}
460460
n, err := bc.InsertChain(blocks)
461461

462462
// Check that the returned error indicates the nonce failure.
@@ -499,18 +499,3 @@ func TestGenesisMismatch(t *testing.T) {
499499
}
500500
}
501501
*/
502-
503-
// failpow returns false from Verify for a certain block number.
504-
type failpow struct{ num uint64 }
505-
506-
func (pow failpow) Search(pow.Block, <-chan struct{}) (nonce uint64, mixHash []byte) {
507-
return 0, nil
508-
}
509-
func (pow failpow) Verify(b pow.Block) bool {
510-
return b.NumberU64() != pow.num
511-
}
512-
func (pow failpow) GetHashrate() int64 {
513-
return 0
514-
}
515-
func (pow failpow) Turbo(bool) {
516-
}

core/chain_pow.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package core
18+
19+
import (
20+
"runtime"
21+
22+
"github.com/ethereum/go-ethereum/core/types"
23+
"github.com/ethereum/go-ethereum/pow"
24+
)
25+
26+
// nonceCheckResult contains the result of a nonce verification.
27+
type nonceCheckResult struct {
28+
index int // Index of the item verified from an input array
29+
valid bool // Result of the nonce verification
30+
}
31+
32+
// verifyNoncesFromHeaders starts a concurrent header nonce verification,
33+
// returning a quit channel to abort the operations and a results channel
34+
// to retrieve the async verifications.
35+
func verifyNoncesFromHeaders(checker pow.PoW, headers []*types.Header) (chan<- struct{}, <-chan nonceCheckResult) {
36+
items := make([]pow.Block, len(headers))
37+
for i, header := range headers {
38+
items[i] = types.NewBlockWithHeader(header)
39+
}
40+
return verifyNonces(checker, items)
41+
}
42+
43+
// verifyNoncesFromBlocks starts a concurrent block nonce verification,
44+
// returning a quit channel to abort the operations and a results channel
45+
// to retrieve the async verifications.
46+
func verifyNoncesFromBlocks(checker pow.PoW, blocks []*types.Block) (chan<- struct{}, <-chan nonceCheckResult) {
47+
items := make([]pow.Block, len(blocks))
48+
for i, block := range blocks {
49+
items[i] = block
50+
}
51+
return verifyNonces(checker, items)
52+
}
53+
54+
// verifyNonces starts a concurrent nonce verification, returning a quit channel
55+
// to abort the operations and a results channel to retrieve the async checks.
56+
func verifyNonces(checker pow.PoW, items []pow.Block) (chan<- struct{}, <-chan nonceCheckResult) {
57+
// Spawn as many workers as allowed threads
58+
workers := runtime.GOMAXPROCS(0)
59+
if len(items) < workers {
60+
workers = len(items)
61+
}
62+
// Create a task channel and spawn the verifiers
63+
tasks := make(chan int, workers)
64+
results := make(chan nonceCheckResult, len(items)) // Buffered to make sure all workers stop
65+
for i := 0; i < workers; i++ {
66+
go func() {
67+
for index := range tasks {
68+
results <- nonceCheckResult{index: index, valid: checker.Verify(items[index])}
69+
}
70+
}()
71+
}
72+
// Feed item indices to the workers until done or aborted
73+
abort := make(chan struct{})
74+
go func() {
75+
defer close(tasks)
76+
77+
for i := range items {
78+
select {
79+
case tasks <- i:
80+
continue
81+
case <-abort:
82+
return
83+
}
84+
}
85+
}()
86+
return abort, results
87+
}

0 commit comments

Comments
 (0)