mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 21:56:43 +00:00
core: implemented parallel nonce checking & tx pub key derivation
The blockchain switched over to the generic tasks handler / load balancer to check for block nonce, uncle nonce and transaction public key gerivation.
This commit is contained in:
parent
966326f888
commit
61ec3107ed
4 changed files with 218 additions and 8 deletions
|
|
@ -2,7 +2,7 @@ package balancer
|
||||||
|
|
||||||
import "container/heap"
|
import "container/heap"
|
||||||
|
|
||||||
const maxTaskBuffer = 3000 // maximum amount of work a worker can have in its buffer
|
var maxTaskBuffer = 3000 // maximum amount of work a worker can have in its buffer
|
||||||
|
|
||||||
// Task repsents a single batch of work offered to a worker.
|
// Task repsents a single batch of work offered to a worker.
|
||||||
type Task struct {
|
type Task struct {
|
||||||
|
|
@ -158,13 +158,14 @@ func (b *Balancer) dispatch(task Task) {
|
||||||
if len(w.tasks) == cap(w.tasks) {
|
if len(w.tasks) == cap(w.tasks) {
|
||||||
heap.Push(&b.pool, w) // push full worker back to heap
|
heap.Push(&b.pool, w) // push full worker back to heap
|
||||||
// set the pending state (i.e. high load)
|
// set the pending state (i.e. high load)
|
||||||
pending := (len(b.pool) - b.poolSize) * maxTaskBuffer
|
pending := (len(b.pool) - b.poolSize + 1) * maxTaskBuffer
|
||||||
// create the new temporary worker
|
// create the new temporary worker
|
||||||
worker := &Worker{
|
worker := &Worker{
|
||||||
tasks: make(chan Task, maxTaskBuffer),
|
tasks: make(chan Task, maxTaskBuffer),
|
||||||
temp: true,
|
temp: true,
|
||||||
start: pending,
|
start: pending,
|
||||||
pending: pending,
|
pending: pending,
|
||||||
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
go worker.work(b.done) // spawn worker process
|
go worker.work(b.done) // spawn worker process
|
||||||
w = worker // set new worker
|
w = worker // set new worker
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package balancer
|
package balancer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/heap"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -9,6 +10,113 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestTemporaryWorker(t *testing.T) {
|
||||||
|
size := 5
|
||||||
|
maxTaskBuffer = 1
|
||||||
|
balancer := New(4)
|
||||||
|
|
||||||
|
qchan := make([]chan struct{}, size)
|
||||||
|
errch := make(chan error, size)
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
i := i
|
||||||
|
|
||||||
|
qchan[i] = make(chan struct{})
|
||||||
|
balancer.dispatch(NewTask(func() error {
|
||||||
|
<-qchan[i] // blocks
|
||||||
|
return nil
|
||||||
|
}, errch))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(balancer.pool) != balancer.poolSize+1 {
|
||||||
|
t.Fatal("expected pool to be size", balancer.poolSize+1, "got", len(balancer.pool))
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWorker := balancer.pool[len(balancer.pool)-1]
|
||||||
|
if !lastWorker.temp {
|
||||||
|
t.Error("expected last worker to be temp worker")
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastWorker.start != (len(balancer.pool)-balancer.poolSize)*maxTaskBuffer {
|
||||||
|
t.Error("expected temp worker start", balancer.poolSize, "got", lastWorker.start)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
for i := 0; i < cap(qchan); i++ {
|
||||||
|
close(qchan[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad(t *testing.T) {
|
||||||
|
maxTaskBuffer = 2
|
||||||
|
size := 5
|
||||||
|
balancer := New(4)
|
||||||
|
|
||||||
|
qchan := make([]chan struct{}, size)
|
||||||
|
errch := make(chan error, size)
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
i := i
|
||||||
|
|
||||||
|
qchan[i] = make(chan struct{})
|
||||||
|
balancer.dispatch(NewTask(func() error {
|
||||||
|
<-qchan[i] // blocks
|
||||||
|
return nil
|
||||||
|
}, errch))
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundTwo bool
|
||||||
|
for _, worker := range balancer.pool {
|
||||||
|
if worker.pending == 2 {
|
||||||
|
foundTwo = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundTwo {
|
||||||
|
t.Error("expected to have at least one item with pending 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
for i := 0; i < cap(qchan); i++ {
|
||||||
|
close(qchan[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPool(t *testing.T) {
|
||||||
|
pool := make(Pool, 0)
|
||||||
|
heap.Init(&pool)
|
||||||
|
|
||||||
|
var workers = []*Worker{
|
||||||
|
&Worker{id: 0, pending: 0},
|
||||||
|
&Worker{id: 1, pending: 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, worker := range workers {
|
||||||
|
heap.Push(&pool, worker)
|
||||||
|
}
|
||||||
|
|
||||||
|
worker := heap.Pop(&pool).(*Worker)
|
||||||
|
if worker.id != 0 {
|
||||||
|
t.Error("expected worker 0 to be popped")
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.pending = 2
|
||||||
|
heap.Push(&pool, worker)
|
||||||
|
|
||||||
|
if pool[worker.index] != worker {
|
||||||
|
t.Error("expected", worker.index, "to be at index but got different worker")
|
||||||
|
}
|
||||||
|
|
||||||
|
worker = heap.Pop(&pool).(*Worker)
|
||||||
|
if worker.id != 1 {
|
||||||
|
t.Error("expected worker 1 to be popped")
|
||||||
|
}
|
||||||
|
heap.Push(&pool, worker)
|
||||||
|
|
||||||
|
if pool[worker.index] != worker {
|
||||||
|
t.Error("expected", worker.index, "to be at index but got different worker")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeTxs(size int, b *testing.B) []types.Transactions {
|
func makeTxs(size int, b *testing.B) []types.Transactions {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
|
|
|
||||||
99
core/block_work.go
Normal file
99
core/block_work.go
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/balancer"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nonceResult returns whether the nonce was valid or not.
|
||||||
|
type nonceResult struct {
|
||||||
|
index int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBlockTasks creates new work for the load balancer and returns a
|
||||||
|
// channel on which the results of each block is written.
|
||||||
|
func CreateBlockTasks(b *balancer.Balancer, blocks []*types.Block, checker pow.PoW) chan nonceResult {
|
||||||
|
if len(blocks) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Nonce result channel used to post the result of each nonce
|
||||||
|
// checked by the task.
|
||||||
|
nonceResults = make(chan nonceResult, len(blocks))
|
||||||
|
// Error channel (ignored, see below)
|
||||||
|
errch = make(chan error, len(blocks))
|
||||||
|
)
|
||||||
|
for i, block := range blocks {
|
||||||
|
i := i
|
||||||
|
task := balancer.NewTask(func() error {
|
||||||
|
var err error
|
||||||
|
// verify the block's nonce...
|
||||||
|
if !checker.Verify(block) {
|
||||||
|
err = &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...verify the block uncle's nonces...
|
||||||
|
for _, u := range block.Uncles() {
|
||||||
|
if !checker.Verify(types.NewBlockWithHeader(u)) {
|
||||||
|
err = fmt.Errorf("uncle: %v", BlockNonceErr{Hash: u.Hash(), Number: u.Number, Nonce: block.Nonce()})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...and write the result on the results chan
|
||||||
|
nonceResults <- nonceResult{i, err}
|
||||||
|
// return nil, ignore error handling by the balancer
|
||||||
|
return nil
|
||||||
|
}, errch)
|
||||||
|
// push task to the balancer
|
||||||
|
b.Push(task)
|
||||||
|
// create transaction tasks for this block
|
||||||
|
CreateTxWork(b, block.Transactions())
|
||||||
|
}
|
||||||
|
|
||||||
|
// we aren't at all interested in the errors
|
||||||
|
// since we handle errors ourself.
|
||||||
|
go func() {
|
||||||
|
// empty out the error channel
|
||||||
|
for i := 0; i < len(blocks); i++ {
|
||||||
|
<-errch
|
||||||
|
}
|
||||||
|
close(errch)
|
||||||
|
}()
|
||||||
|
return nonceResults
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateTxWork creates new tasks for the load balancer and derives the public
|
||||||
|
// key for each transaction.
|
||||||
|
func CreateTxWork(b *balancer.Balancer, txs types.Transactions) {
|
||||||
|
if len(txs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// error channel (ignored, see below)
|
||||||
|
errch := make(chan error, len(txs))
|
||||||
|
// create a new tasks for each transaction and derive the public
|
||||||
|
// key from the sender signature.
|
||||||
|
for i := 0; i < len(txs); i++ {
|
||||||
|
i := i
|
||||||
|
// create new tasks
|
||||||
|
task := balancer.NewTask(func() error {
|
||||||
|
txs[i].FromFrontier()
|
||||||
|
return nil
|
||||||
|
}, errch)
|
||||||
|
// push task to the balancer
|
||||||
|
b.Push(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we aren't at all interested in the errors
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < cap(errch); i++ {
|
||||||
|
<-errch
|
||||||
|
}
|
||||||
|
close(errch)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/balancer"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -113,6 +114,7 @@ type BlockChain struct {
|
||||||
rand *mrand.Rand
|
rand *mrand.Rand
|
||||||
processor Processor
|
processor Processor
|
||||||
validator Validator
|
validator Validator
|
||||||
|
balancer *balancer.Balancer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockChain returns a fully initialised block chain using information
|
// NewBlockChain returns a fully initialised block chain using information
|
||||||
|
|
@ -137,6 +139,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
|
||||||
blockCache: blockCache,
|
blockCache: blockCache,
|
||||||
futureBlocks: futureBlocks,
|
futureBlocks: futureBlocks,
|
||||||
pow: pow,
|
pow: pow,
|
||||||
|
balancer: balancer.New(runtime.GOMAXPROCS(0)),
|
||||||
}
|
}
|
||||||
// Seed a fast but crypto originating random generator
|
// Seed a fast but crypto originating random generator
|
||||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
||||||
|
|
@ -1120,10 +1123,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
|
|
||||||
nonceChecked = make([]bool, len(chain))
|
nonceChecked = make([]bool, len(chain))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start the parallel nonce verifier.
|
// Start the parallel nonce verifier.
|
||||||
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
|
nonceResults := CreateBlockTasks(self.balancer, chain, self.pow)
|
||||||
defer close(nonceAbort)
|
|
||||||
|
|
||||||
txcount := 0
|
txcount := 0
|
||||||
for i, block := range chain {
|
for i, block := range chain {
|
||||||
|
|
@ -1138,9 +1139,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
for !nonceChecked[i] {
|
for !nonceChecked[i] {
|
||||||
r := <-nonceResults
|
r := <-nonceResults
|
||||||
nonceChecked[r.index] = true
|
nonceChecked[r.index] = true
|
||||||
if !r.valid {
|
if r.err != nil {
|
||||||
block := chain[r.index]
|
return r.index, r.err
|
||||||
return r.index, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1252,6 +1252,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
}
|
}
|
||||||
stats.processed++
|
stats.processed++
|
||||||
}
|
}
|
||||||
|
// close the bogus errch. errors are delivered using owned channel
|
||||||
|
//close(errch) // we don't need to bother with checking
|
||||||
|
|
||||||
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
|
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
|
||||||
tend := time.Since(tstart)
|
tend := time.Since(tstart)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue