mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
miner: commit state which is relative with sealing result
This commit is contained in:
parent
67d6d0bb7d
commit
4ff1e47127
3 changed files with 56 additions and 23 deletions
|
|
@ -135,14 +135,14 @@ var (
|
||||||
// backing account.
|
// backing account.
|
||||||
type SignerFn func(accounts.Account, []byte) ([]byte, error)
|
type SignerFn func(accounts.Account, []byte) ([]byte, error)
|
||||||
|
|
||||||
// sigHash returns the hash which is used as input for the proof-of-authority
|
// SigHash returns the hash which is used as input for the proof-of-authority
|
||||||
// signing. It is the hash of the entire header apart from the 65 byte signature
|
// signing. It is the hash of the entire header apart from the 65 byte signature
|
||||||
// contained at the end of the extra data.
|
// contained at the end of the extra data.
|
||||||
//
|
//
|
||||||
// Note, the method requires the extra data to be at least 65 bytes, otherwise it
|
// Note, the method requires the extra data to be at least 65 bytes, otherwise it
|
||||||
// panics. This is done to avoid accidentally using both forms (signature present
|
// panics. This is done to avoid accidentally using both forms (signature present
|
||||||
// or not), which could be abused to produce different hashes for the same header.
|
// or not), which could be abused to produce different hashes for the same header.
|
||||||
func sigHash(header *types.Header) (hash common.Hash) {
|
func SigHash(header *types.Header) (hash common.Hash) {
|
||||||
hasher := sha3.NewKeccak256()
|
hasher := sha3.NewKeccak256()
|
||||||
|
|
||||||
rlp.Encode(hasher, []interface{}{
|
rlp.Encode(hasher, []interface{}{
|
||||||
|
|
@ -180,7 +180,7 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er
|
||||||
signature := header.Extra[len(header.Extra)-extraSeal:]
|
signature := header.Extra[len(header.Extra)-extraSeal:]
|
||||||
|
|
||||||
// Recover the public key and the Ethereum address
|
// Recover the public key and the Ethereum address
|
||||||
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature)
|
pubkey, err := crypto.Ecrecover(SigHash(header).Bytes(), signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -643,7 +643,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
|
||||||
case <-time.After(delay):
|
case <-time.After(delay):
|
||||||
}
|
}
|
||||||
// Sign all the things!
|
// Sign all the things!
|
||||||
sighash, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes())
|
sighash, err := signFn(accounts.Account{Address: signer}, SigHash(header).Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func (ap *testerAccountPool) sign(header *types.Header, signer string) {
|
||||||
ap.accounts[signer], _ = crypto.GenerateKey()
|
ap.accounts[signer], _ = crypto.GenerateKey()
|
||||||
}
|
}
|
||||||
// Sign the header and embed the signature in extra data
|
// Sign the header and embed the signature in extra data
|
||||||
sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer])
|
sig, _ := crypto.Sign(SigHash(header).Bytes(), ap.accounts[signer])
|
||||||
copy(header.Extra[len(header.Extra)-65:], sig)
|
copy(header.Extra[len(header.Extra)-65:], sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
mapset "github.com/deckarep/golang-set"
|
mapset "github.com/deckarep/golang-set"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -150,6 +151,9 @@ type worker struct {
|
||||||
coinbase common.Address
|
coinbase common.Address
|
||||||
extra []byte
|
extra []byte
|
||||||
|
|
||||||
|
pendingMu sync.RWMutex
|
||||||
|
pendingTasks map[common.Hash]*task
|
||||||
|
|
||||||
snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot
|
snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot
|
||||||
snapshotBlock *types.Block
|
snapshotBlock *types.Block
|
||||||
snapshotState *state.StateDB
|
snapshotState *state.StateDB
|
||||||
|
|
@ -174,6 +178,7 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend,
|
||||||
chain: eth.BlockChain(),
|
chain: eth.BlockChain(),
|
||||||
possibleUncles: make(map[common.Hash]*types.Block),
|
possibleUncles: make(map[common.Hash]*types.Block),
|
||||||
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),
|
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),
|
||||||
|
pendingTasks: make(map[common.Hash]*task),
|
||||||
txsCh: make(chan core.NewTxsEvent, txChanSize),
|
txsCh: make(chan core.NewTxsEvent, txChanSize),
|
||||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||||
chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
|
chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
|
||||||
|
|
@ -317,13 +322,25 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
||||||
}
|
}
|
||||||
recommit = time.Duration(int64(next))
|
recommit = time.Duration(int64(next))
|
||||||
}
|
}
|
||||||
|
// clearPending cleans the stale pending tasks.
|
||||||
|
clearPending := func(number uint64) {
|
||||||
|
w.pendingMu.Lock()
|
||||||
|
for h, t := range w.pendingTasks {
|
||||||
|
if t.block.NumberU64() <= number {
|
||||||
|
delete(w.pendingTasks, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.pendingMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-w.startCh:
|
case <-w.startCh:
|
||||||
|
clearPending(w.chain.CurrentBlock().NumberU64())
|
||||||
commit(false, commitInterruptNewHead)
|
commit(false, commitInterruptNewHead)
|
||||||
|
|
||||||
case <-w.chainHeadCh:
|
case head := <-w.chainHeadCh:
|
||||||
|
clearPending(head.Block.NumberU64())
|
||||||
commit(false, commitInterruptNewHead)
|
commit(false, commitInterruptNewHead)
|
||||||
|
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
|
|
@ -454,29 +471,45 @@ func (w *worker) mainLoop() {
|
||||||
|
|
||||||
// seal pushes a sealing task to consensus engine and submits the result.
|
// seal pushes a sealing task to consensus engine and submits the result.
|
||||||
func (w *worker) seal(t *task, stop <-chan struct{}) {
|
func (w *worker) seal(t *task, stop <-chan struct{}) {
|
||||||
var (
|
|
||||||
err error
|
|
||||||
res *task
|
|
||||||
)
|
|
||||||
|
|
||||||
if w.skipSealHook != nil && w.skipSealHook(t) {
|
if w.skipSealHook != nil && w.skipSealHook(t) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// makeId creates an id for pending task based on consensus engine type.
|
||||||
|
makeId := func(block *types.Block) common.Hash {
|
||||||
|
hash := t.block.HashNoNonce()
|
||||||
|
if _, ok := w.engine.(*clique.Clique); ok {
|
||||||
|
hash = clique.SigHash(t.block.Header())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
// The reason for caching task first is:
|
||||||
|
// A previous sealing action will be canceled by subsequent actions,
|
||||||
|
// however, remote miner may submit a result based on the cancelled task.
|
||||||
|
// So we should only submit the pending state corresponding to the seal result.
|
||||||
|
// TODO(rjl493456442) Replace the seal-wait logic structure
|
||||||
|
w.pendingMu.Lock()
|
||||||
|
w.pendingTasks[makeId(t.block)] = t
|
||||||
|
w.pendingMu.Unlock()
|
||||||
|
|
||||||
if t.block, err = w.engine.Seal(w.chain, t.block, stop); t.block != nil {
|
if block, err := w.engine.Seal(w.chain, t.block, stop); block != nil {
|
||||||
log.Info("Successfully sealed new block", "number", t.block.Number(), "hash", t.block.Hash(),
|
w.pendingMu.RLock()
|
||||||
"elapsed", common.PrettyDuration(time.Since(t.createdAt)))
|
task, exist := w.pendingTasks[makeId(block)]
|
||||||
res = t
|
w.pendingMu.RUnlock()
|
||||||
} else {
|
if !exist {
|
||||||
if err != nil {
|
log.Error("Block found but no relative pending task", "number", block.Number(), "hash", block.Hash())
|
||||||
log.Warn("Block sealing failed", "err", err)
|
return
|
||||||
}
|
|
||||||
res = nil
|
|
||||||
}
|
}
|
||||||
|
// Assemble sealing result
|
||||||
|
task.block = block
|
||||||
|
log.Info("Successfully sealed new block", "number", block.Number(), "hash", block.Hash(),
|
||||||
|
"elapsed", common.PrettyDuration(time.Since(task.createdAt)))
|
||||||
select {
|
select {
|
||||||
case w.resultCh <- res:
|
case w.resultCh <- task:
|
||||||
case <-w.exitCh:
|
case <-w.exitCh:
|
||||||
}
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
log.Warn("Block sealing failed", "err", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// taskLoop is a standalone goroutine to fetch sealing task from the generator and
|
// taskLoop is a standalone goroutine to fetch sealing task from the generator and
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue