mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
miner, ethash: push empty block to sealer without waiting execution
This commit is contained in:
parent
d2d3b84fa7
commit
dd83e3d40a
12 changed files with 338 additions and 202 deletions
|
|
@ -31,7 +31,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
|
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
|
||||||
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -672,6 +672,11 @@ func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
|
||||||
return new(big.Int).Set(diffNoTurn)
|
return new(big.Int).Set(diffNoTurn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close implements consensus.Engine, returning internal error and close the clique.
|
||||||
|
func (c *Clique) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// APIs implements consensus.Engine, returning the user facing RPC API to allow
|
// APIs implements consensus.Engine, returning the user facing RPC API to allow
|
||||||
// controlling the signer voting.
|
// controlling the signer voting.
|
||||||
func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API {
|
func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API {
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ type Engine interface {
|
||||||
|
|
||||||
// APIs returns the RPC APIs this consensus engine provides.
|
// APIs returns the RPC APIs this consensus engine provides.
|
||||||
APIs(chain ChainReader) []rpc.API
|
APIs(chain ChainReader) []rpc.API
|
||||||
|
|
||||||
|
// Close closes the consensus engine.
|
||||||
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// PoW is a consensus engine based on proof-of-work.
|
// PoW is a consensus engine based on proof-of-work.
|
||||||
|
|
|
||||||
|
|
@ -730,6 +730,7 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
|
||||||
go func(idx int) {
|
go func(idx int) {
|
||||||
defer pend.Done()
|
defer pend.Done()
|
||||||
ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal})
|
ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal})
|
||||||
|
defer ethash.Close()
|
||||||
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
|
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
|
||||||
t.Errorf("proc %d: block verification failed: %v", idx, err)
|
t.Errorf("proc %d: block verification failed: %v", idx, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,78 +13,87 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package ethash
|
package ethash
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// API exposes ethash related methods for the RPC interface
|
var errEthashStopped = errors.New("ethash stopped")
|
||||||
|
|
||||||
|
// API exposes ethash related methods for the RPC interface.
|
||||||
type API struct {
|
type API struct {
|
||||||
ethash *Ethash
|
ethash *Ethash
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWork returns a work package for external miner. The work package consists of 3 strings
|
// GetWork returns a work package for external miner.
|
||||||
// result[0], 32 bytes hex encoded current block header pow-hash
|
//
|
||||||
// result[1], 32 bytes hex encoded seed hash used for DAG
|
// The work package consists of 3 strings:
|
||||||
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
// result[0] - 32 bytes hex encoded current block header pow-hash
|
||||||
func (s *API) GetWork() ([3]string, error) {
|
// result[1] - 32 bytes hex encoded seed hash used for DAG
|
||||||
|
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
||||||
|
func (api *API) GetWork() ([3]string, error) {
|
||||||
var (
|
var (
|
||||||
workCh = make(chan [3]string)
|
workCh = make(chan [3]string, 1)
|
||||||
errCh = make(chan error)
|
errCh = make(chan error, 1)
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
op := &remoteOp{
|
|
||||||
typ: opGetWork,
|
select {
|
||||||
workCh: workCh,
|
case api.ethash.fetchWorkCh <- &sealWork{errCh: errCh, resCh: workCh}:
|
||||||
errCh: errCh,
|
case <-api.ethash.exitCh:
|
||||||
|
return [3]string{}, errEthashStopped
|
||||||
}
|
}
|
||||||
s.ethash.remoteOp <- op
|
|
||||||
if err := <-errCh; err == nil {
|
if err = <-errCh; err == nil {
|
||||||
return <-workCh, nil
|
return <-workCh, nil
|
||||||
} else {
|
|
||||||
return [3]string{}, err
|
|
||||||
}
|
}
|
||||||
|
return [3]string{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was
|
// SubmitWork can be used by external miner to submit their POW solution.
|
||||||
// accepted. Note, this is not an indication if the provided work was valid!
|
// It returns an indication if the work was accepted.
|
||||||
func (s *API) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool {
|
// Note either an invalid solution, a stale work a non-existent work will return false.
|
||||||
var errCh = make(chan error)
|
func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) bool {
|
||||||
op := &remoteOp{
|
var errCh = make(chan error, 1)
|
||||||
typ: opSubmitWork,
|
|
||||||
result: mineResult{
|
select {
|
||||||
|
case api.ethash.submitWorkCh <- &mineResult{
|
||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
mixDigest: digest,
|
mixDigest: digest,
|
||||||
hash: solution,
|
hash: hash,
|
||||||
},
|
|
||||||
errCh: errCh,
|
errCh: errCh,
|
||||||
}
|
}:
|
||||||
s.ethash.remoteOp <- op
|
case <-api.ethash.exitCh:
|
||||||
if err := <-errCh; err == nil {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := <-errCh
|
||||||
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
|
// SubmitHashrate can be used for remote miners to submit their hash rate.
|
||||||
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
|
// This enables the node to report the combined hash rate of all miners
|
||||||
// must be unique between nodes.
|
// which submit work through this node.
|
||||||
func (s *API) SubmitHashRate(r hexutil.Uint64, id common.Hash) bool {
|
//
|
||||||
submit := func(rate map[common.Hash]hashrate) {
|
// It accepts the miner hash rate and an identifier which must be unique
|
||||||
rate[id] = hashrate{rate: uint64(r), ping: time.Now()}
|
// between nodes.
|
||||||
|
func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool {
|
||||||
|
var doneCh = make(chan struct{}, 1)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case api.ethash.submitRateCh <- &hashrate{done: doneCh, rate: uint64(rate), id: id}:
|
||||||
|
case <-api.ethash.exitCh:
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
errCh := make(chan error)
|
|
||||||
op := &remoteOp{
|
// Block until hash rate submitted successfully.
|
||||||
typ: opHashrate,
|
<-doneCh
|
||||||
rateOp: submit,
|
|
||||||
errCh: errCh,
|
|
||||||
}
|
|
||||||
s.ethash.remoteOp <- op
|
|
||||||
<-errCh
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -391,38 +391,28 @@ type Config struct {
|
||||||
PowMode Mode
|
PowMode Mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// remoteOpType defines all the types of operations related to remote sealer.
|
|
||||||
type remoteOpType uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
opGetWork remoteOpType = iota
|
|
||||||
opSubmitWork
|
|
||||||
opHashrate
|
|
||||||
)
|
|
||||||
|
|
||||||
// mineResult wraps the pow solution parameters for the specified block.
|
// mineResult wraps the pow solution parameters for the specified block.
|
||||||
type mineResult struct {
|
type mineResult struct {
|
||||||
nonce types.BlockNonce
|
nonce types.BlockNonce
|
||||||
mixDigest common.Hash
|
mixDigest common.Hash
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
|
|
||||||
|
errCh chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hashrate wraps the hash rate submitted by the remote sealer.
|
||||||
type hashrate struct {
|
type hashrate struct {
|
||||||
id common.Hash
|
id common.Hash
|
||||||
ping time.Time
|
ping time.Time
|
||||||
rate uint64
|
rate uint64
|
||||||
|
|
||||||
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type hashrateOp func(map[common.Hash]hashrate)
|
// sealWork wraps a seal work package for remote sealer.
|
||||||
|
type sealWork struct {
|
||||||
// remoteOp wraps a mining operation sent by remote miner.
|
|
||||||
type remoteOp struct {
|
|
||||||
typ remoteOpType
|
|
||||||
result mineResult
|
|
||||||
errCh chan error
|
errCh chan error
|
||||||
workCh chan [3]string
|
resCh chan [3]string
|
||||||
|
|
||||||
rateOp hashrateOp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethash is a consensus engine based on proof-of-work implementing the ethash
|
// Ethash is a consensus engine based on proof-of-work implementing the ethash
|
||||||
|
|
@ -433,8 +423,6 @@ type Ethash struct {
|
||||||
caches *lru // In memory caches to avoid regenerating too often
|
caches *lru // In memory caches to avoid regenerating too often
|
||||||
datasets *lru // In memory datasets to avoid regenerating too often
|
datasets *lru // In memory datasets to avoid regenerating too often
|
||||||
|
|
||||||
exitCh chan struct{} // Notification channel to exiting backend threads
|
|
||||||
|
|
||||||
// Mining related fields
|
// Mining related fields
|
||||||
rand *rand.Rand // Properly seeded random source for nonces
|
rand *rand.Rand // Properly seeded random source for nonces
|
||||||
threads int // Number of threads to mine on if mining
|
threads int // Number of threads to mine on if mining
|
||||||
|
|
@ -444,13 +432,19 @@ type Ethash struct {
|
||||||
// Remote sealer related fields
|
// Remote sealer related fields
|
||||||
workCh chan *types.Block // Notification channel to push new work to remote sealer
|
workCh chan *types.Block // Notification channel to push new work to remote sealer
|
||||||
resultCh chan *types.Block // Channel used by mining threads to return result
|
resultCh chan *types.Block // Channel used by mining threads to return result
|
||||||
remoteOp chan *remoteOp // Channel used to receive all mining operations from api
|
fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
|
||||||
|
submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
|
||||||
|
fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
|
||||||
|
submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate
|
||||||
|
|
||||||
// The fields below are hooks for testing
|
// The fields below are hooks for testing
|
||||||
shared *Ethash // Shared PoW verifier to avoid cache regeneration
|
shared *Ethash // Shared PoW verifier to avoid cache regeneration
|
||||||
fakeFail uint64 // Block number which fails PoW check even in fake mode
|
fakeFail uint64 // Block number which fails PoW check even in fake mode
|
||||||
fakeDelay time.Duration // Time delay to sleep for before returning from verify
|
fakeDelay time.Duration // Time delay to sleep for before returning from verify
|
||||||
|
|
||||||
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
|
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
|
||||||
|
closeOnce sync.Once // Ensures exit channel will not be closed twice.
|
||||||
|
exitCh chan chan error // Notification channel to exiting backend threads
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a full sized ethash PoW scheme.
|
// New creates a full sized ethash PoW scheme.
|
||||||
|
|
@ -471,13 +465,15 @@ func New(config Config) *Ethash {
|
||||||
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
|
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
|
||||||
update: make(chan struct{}),
|
update: make(chan struct{}),
|
||||||
hashrate: metrics.NewMeter(),
|
hashrate: metrics.NewMeter(),
|
||||||
exitCh: make(chan struct{}),
|
|
||||||
workCh: make(chan *types.Block),
|
workCh: make(chan *types.Block),
|
||||||
resultCh: make(chan *types.Block),
|
resultCh: make(chan *types.Block),
|
||||||
remoteOp: make(chan *remoteOp),
|
fetchWorkCh: make(chan *sealWork),
|
||||||
|
submitWorkCh: make(chan *mineResult),
|
||||||
|
fetchRateCh: make(chan chan uint64),
|
||||||
|
submitRateCh: make(chan *hashrate),
|
||||||
|
exitCh: make(chan chan error),
|
||||||
}
|
}
|
||||||
go ethash.remote(ethash.resultCh)
|
go ethash.remote()
|
||||||
runtime.SetFinalizer(ethash, (*Ethash).close)
|
|
||||||
return ethash
|
return ethash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -490,13 +486,15 @@ func NewTester() *Ethash {
|
||||||
datasets: newlru("dataset", 1, newDataset),
|
datasets: newlru("dataset", 1, newDataset),
|
||||||
update: make(chan struct{}),
|
update: make(chan struct{}),
|
||||||
hashrate: metrics.NewMeter(),
|
hashrate: metrics.NewMeter(),
|
||||||
exitCh: make(chan struct{}),
|
|
||||||
workCh: make(chan *types.Block),
|
workCh: make(chan *types.Block),
|
||||||
resultCh: make(chan *types.Block),
|
resultCh: make(chan *types.Block),
|
||||||
remoteOp: make(chan *remoteOp),
|
fetchWorkCh: make(chan *sealWork),
|
||||||
|
submitWorkCh: make(chan *mineResult),
|
||||||
|
fetchRateCh: make(chan chan uint64),
|
||||||
|
submitRateCh: make(chan *hashrate),
|
||||||
|
exitCh: make(chan chan error),
|
||||||
}
|
}
|
||||||
go ethash.remote(ethash.resultCh)
|
go ethash.remote()
|
||||||
runtime.SetFinalizer(ethash, (*Ethash).close)
|
|
||||||
return ethash
|
return ethash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -508,6 +506,7 @@ func NewFaker() *Ethash {
|
||||||
config: Config{
|
config: Config{
|
||||||
PowMode: ModeFake,
|
PowMode: ModeFake,
|
||||||
},
|
},
|
||||||
|
exitCh: make(chan chan error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -520,6 +519,7 @@ func NewFakeFailer(fail uint64) *Ethash {
|
||||||
PowMode: ModeFake,
|
PowMode: ModeFake,
|
||||||
},
|
},
|
||||||
fakeFail: fail,
|
fakeFail: fail,
|
||||||
|
exitCh: make(chan chan error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -532,6 +532,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
|
||||||
PowMode: ModeFake,
|
PowMode: ModeFake,
|
||||||
},
|
},
|
||||||
fakeDelay: delay,
|
fakeDelay: delay,
|
||||||
|
exitCh: make(chan chan error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -542,19 +543,32 @@ func NewFullFaker() *Ethash {
|
||||||
config: Config{
|
config: Config{
|
||||||
PowMode: ModeFullFake,
|
PowMode: ModeFullFake,
|
||||||
},
|
},
|
||||||
|
exitCh: make(chan chan error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewShared creates a full sized ethash PoW shared between all requesters running
|
// NewShared creates a full sized ethash PoW shared between all requesters running
|
||||||
// in the same process.
|
// in the same process.
|
||||||
func NewShared() *Ethash {
|
func NewShared() *Ethash {
|
||||||
return &Ethash{shared: sharedEthash}
|
return &Ethash{
|
||||||
|
shared: sharedEthash,
|
||||||
|
exitCh: make(chan chan error),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// close closes the exit channel to notify all backend threads exiting.
|
// Close closes the exit channel to notify all backend threads exiting.
|
||||||
func (ethash *Ethash) close() {
|
func (ethash *Ethash) Close() error {
|
||||||
runtime.SetFinalizer(ethash, nil)
|
var err error
|
||||||
|
ethash.closeOnce.Do(func() {
|
||||||
|
var errCh = make(chan error)
|
||||||
|
select {
|
||||||
|
case ethash.exitCh <- errCh:
|
||||||
|
err = <-errCh
|
||||||
close(ethash.exitCh)
|
close(ethash.exitCh)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache tries to retrieve a verification cache for the specified block number
|
// cache tries to retrieve a verification cache for the specified block number
|
||||||
|
|
@ -632,37 +646,38 @@ func (ethash *Ethash) SetThreads(threads int) {
|
||||||
// Note the returned hashrate includes local hashrate, but also includes the total
|
// Note the returned hashrate includes local hashrate, but also includes the total
|
||||||
// hashrate of all remote miner.
|
// hashrate of all remote miner.
|
||||||
func (ethash *Ethash) Hashrate() float64 {
|
func (ethash *Ethash) Hashrate() float64 {
|
||||||
var (
|
var resCh = make(chan uint64, 1)
|
||||||
total uint64
|
|
||||||
errCh = make(chan error)
|
|
||||||
)
|
|
||||||
// getHashrate gathers all remote miner's hashrate.
|
|
||||||
getHashrate := func(hashrate map[common.Hash]hashrate) {
|
|
||||||
for _, rate := range hashrate {
|
|
||||||
// this could overflow
|
|
||||||
total += rate.rate
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
op := &remoteOp{
|
|
||||||
typ: opHashrate,
|
|
||||||
rateOp: getHashrate,
|
|
||||||
errCh: errCh,
|
|
||||||
}
|
|
||||||
ethash.remoteOp <- op
|
|
||||||
<-errCh
|
|
||||||
|
|
||||||
|
select {
|
||||||
|
case ethash.fetchRateCh <- resCh:
|
||||||
|
case <-ethash.exitCh:
|
||||||
|
// Return local hashrate only if ethash is stopped.
|
||||||
|
return ethash.hashrate.Rate1()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather total submitted hash rate of remote sealers.
|
||||||
|
total := <-resCh
|
||||||
return ethash.hashrate.Rate1() + float64(total)
|
return ethash.hashrate.Rate1() + float64(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIs implements consensus.Engine, returning the user facing RPC APIs.
|
// APIs implements consensus.Engine, returning the user facing RPC APIs.
|
||||||
func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API {
|
func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API {
|
||||||
return []rpc.API{{
|
// In order to ensure backward compatibility, we exposes ethash RPC APIs
|
||||||
|
// to both eth and ethash namespaces.
|
||||||
|
return []rpc.API{
|
||||||
|
{
|
||||||
Namespace: "eth",
|
Namespace: "eth",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: &API{ethash},
|
Service: &API{ethash},
|
||||||
Public: true,
|
Public: true,
|
||||||
}}
|
},
|
||||||
|
{
|
||||||
|
Namespace: "ethash",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: &API{ethash},
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeedHash is the seed to use for generating a verification cache and the mining
|
// SeedHash is the seed to use for generating a verification cache and the mining
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -34,6 +35,7 @@ func TestTestMode(t *testing.T) {
|
||||||
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
||||||
|
|
||||||
ethash := NewTester()
|
ethash := NewTester()
|
||||||
|
defer ethash.Close()
|
||||||
block, err := ethash.Seal(nil, types.NewBlockWithHeader(head), nil)
|
block, err := ethash.Seal(nil, types.NewBlockWithHeader(head), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to seal block: %v", err)
|
t.Fatalf("failed to seal block: %v", err)
|
||||||
|
|
@ -54,6 +56,7 @@ func TestCacheFileEvict(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpdir)
|
defer os.RemoveAll(tmpdir)
|
||||||
e := New(Config{CachesInMem: 3, CachesOnDisk: 10, CacheDir: tmpdir, PowMode: ModeTest})
|
e := New(Config{CachesInMem: 3, CachesOnDisk: 10, CacheDir: tmpdir, PowMode: ModeTest})
|
||||||
|
defer e.Close()
|
||||||
|
|
||||||
workers := 8
|
workers := 8
|
||||||
epochs := 100
|
epochs := 100
|
||||||
|
|
@ -71,7 +74,7 @@ func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) {
|
||||||
const wiggle = 4 * epochLength
|
const wiggle = 4 * epochLength
|
||||||
r := rand.New(rand.NewSource(int64(workerIndex)))
|
r := rand.New(rand.NewSource(int64(workerIndex)))
|
||||||
for epoch := 0; epoch < epochs; epoch++ {
|
for epoch := 0; epoch < epochs; epoch++ {
|
||||||
block := int64(epoch) * epochLength - wiggle / 2 + r.Int63n(wiggle)
|
block := int64(epoch)*epochLength - wiggle/2 + r.Int63n(wiggle)
|
||||||
if block < 0 {
|
if block < 0 {
|
||||||
block = 0
|
block = 0
|
||||||
}
|
}
|
||||||
|
|
@ -82,14 +85,16 @@ func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) {
|
||||||
|
|
||||||
func TestRemoteSealer(t *testing.T) {
|
func TestRemoteSealer(t *testing.T) {
|
||||||
ethash := NewTester()
|
ethash := NewTester()
|
||||||
|
defer ethash.Close()
|
||||||
api := &API{ethash}
|
api := &API{ethash}
|
||||||
if _, err := api.GetWork(); err != errNoMineWork {
|
if _, err := api.GetWork(); err != errNoMiningWork {
|
||||||
t.Error("expected to return an error indicate there is no mining work")
|
t.Error("expect to return an error indicate there is no mining work")
|
||||||
}
|
}
|
||||||
|
|
||||||
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
|
||||||
block := types.NewBlockWithHeader(head)
|
block := types.NewBlockWithHeader(head)
|
||||||
// Push new work
|
|
||||||
|
// Push new work.
|
||||||
ethash.Seal(nil, block, nil)
|
ethash.Seal(nil, block, nil)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -97,11 +102,29 @@ func TestRemoteSealer(t *testing.T) {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if work, err = api.GetWork(); err != nil || work[0] != block.HashNoNonce().Hex() {
|
if work, err = api.GetWork(); err != nil || work[0] != block.HashNoNonce().Hex() {
|
||||||
t.Error("expected to return a mining work has same hash")
|
t.Error("expect to return a mining work has same hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
if res := api.SubmitWork(types.BlockNonce{}, block.HashNoNonce(), common.Hash{}); res {
|
if res := api.SubmitWork(types.BlockNonce{}, block.HashNoNonce(), common.Hash{}); res {
|
||||||
t.Error("expected to return false when submit a fake solution")
|
t.Error("expect to return false when submit a fake solution")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push new block with same block number to replace the original one.
|
||||||
|
head = &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(1000)}
|
||||||
|
block = types.NewBlockWithHeader(head)
|
||||||
|
ethash.Seal(nil, block, nil)
|
||||||
|
|
||||||
|
if work, err = api.GetWork(); err != nil || work[0] != block.HashNoNonce().Hex() {
|
||||||
|
t.Error("expect to return the latest pushed work")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push block with higher block number.
|
||||||
|
newHead := &types.Header{Number: big.NewInt(2), Difficulty: big.NewInt(100)}
|
||||||
|
newBlock := types.NewBlockWithHeader(newHead)
|
||||||
|
ethash.Seal(nil, newBlock, nil)
|
||||||
|
|
||||||
|
if res := api.SubmitWork(types.BlockNonce{}, block.HashNoNonce(), common.Hash{}); res {
|
||||||
|
t.Error("expect to return false when submit a stale solution")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +136,13 @@ func TestHashRate(t *testing.T) {
|
||||||
expect uint64
|
expect uint64
|
||||||
ids = []common.Hash{common.HexToHash("a"), common.HexToHash("b"), common.HexToHash("c")}
|
ids = []common.Hash{common.HexToHash("a"), common.HexToHash("b"), common.HexToHash("c")}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defer ethash.Close()
|
||||||
|
|
||||||
|
if tot := ethash.Hashrate(); tot != 0 {
|
||||||
|
t.Error("expect the result should be zero")
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < len(hashrate); i += 1 {
|
for i := 0; i < len(hashrate); i += 1 {
|
||||||
if res := api.SubmitHashRate(hashrate[i], ids[i]); !res {
|
if res := api.SubmitHashRate(hashrate[i], ids[i]); !res {
|
||||||
t.Error("remote miner submit hashrate failed")
|
t.Error("remote miner submit hashrate failed")
|
||||||
|
|
@ -123,3 +153,19 @@ func TestHashRate(t *testing.T) {
|
||||||
t.Error("expect total hashrate should be same")
|
t.Error("expect total hashrate should be same")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClosedRemoteSealer(t *testing.T) {
|
||||||
|
ethash := NewTester()
|
||||||
|
// Make sure exit channel has been listened
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
ethash.Close()
|
||||||
|
|
||||||
|
api := &API{ethash}
|
||||||
|
if _, err := api.GetWork(); err != errEthashStopped {
|
||||||
|
t.Error("expect to return an error to indicate ethash is stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
if res := api.SubmitHashRate(hexutil.Uint64(100), common.HexToHash("a")); res {
|
||||||
|
t.Error("expect to return false when submit hashrate to a stopped ethash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNoMineWork = errors.New("No mining work available yet, don't panic.")
|
errNoMiningWork = errors.New("no mining work available yet, don't panic")
|
||||||
errInvalidSealResult = errors.New("Invalid or stale proof-of-work solution.")
|
errInvalidSealResult = errors.New("invalid or stale proof-of-work solution")
|
||||||
errUndefinedRemoteOp = errors.New("Undefined remote mining operation.")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
|
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
|
||||||
|
|
@ -51,7 +50,6 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
|
||||||
if ethash.shared != nil {
|
if ethash.shared != nil {
|
||||||
return ethash.shared.Seal(chain, block, stop)
|
return ethash.shared.Seal(chain, block, stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a runner and the multiple search threads it directs
|
// Create a runner and the multiple search threads it directs
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
|
|
||||||
|
|
@ -72,10 +70,10 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
|
||||||
if threads < 0 {
|
if threads < 0 {
|
||||||
threads = 0 // Allows disabling local mining without extra logic around local/remote
|
threads = 0 // Allows disabling local mining without extra logic around local/remote
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push new work to remote sealer
|
// Push new work to remote sealer
|
||||||
|
if ethash.workCh != nil {
|
||||||
ethash.workCh <- block
|
ethash.workCh <- block
|
||||||
|
}
|
||||||
var pend sync.WaitGroup
|
var pend sync.WaitGroup
|
||||||
for i := 0; i < threads; i++ {
|
for i := 0; i < threads; i++ {
|
||||||
pend.Add(1)
|
pend.Add(1)
|
||||||
|
|
@ -164,61 +162,72 @@ search:
|
||||||
}
|
}
|
||||||
|
|
||||||
// remote starts a standalone goroutine to handle remote mining related stuff.
|
// remote starts a standalone goroutine to handle remote mining related stuff.
|
||||||
func (ethash *Ethash) remote(resultCh chan *types.Block) {
|
func (ethash *Ethash) remote() {
|
||||||
var (
|
var (
|
||||||
works = make(map[common.Hash]*types.Block)
|
works = make(map[common.Hash]*types.Block)
|
||||||
hashrate = make(map[common.Hash]hashrate)
|
rates = make(map[common.Hash]hashrate)
|
||||||
currentWork *types.Block
|
currentWork *types.Block
|
||||||
)
|
)
|
||||||
|
|
||||||
// getWork returns a work package for external miner. The work package consists of 3 strings
|
// getWork returns a work package for external miner.
|
||||||
|
//
|
||||||
|
// The work package consists of 3 strings:
|
||||||
// result[0], 32 bytes hex encoded current block header pow-hash
|
// result[0], 32 bytes hex encoded current block header pow-hash
|
||||||
// result[1], 32 bytes hex encoded seed hash used for DAG
|
// result[1], 32 bytes hex encoded seed hash used for DAG
|
||||||
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
||||||
getWork := func() ([3]string, error) {
|
getWork := func() ([3]string, error) {
|
||||||
var res [3]string
|
var res [3]string
|
||||||
if currentWork == nil {
|
if currentWork == nil {
|
||||||
return res, errNoMineWork
|
return res, errNoMiningWork
|
||||||
}
|
}
|
||||||
res[0] = currentWork.HashNoNonce().Hex()
|
res[0] = currentWork.HashNoNonce().Hex()
|
||||||
res[1] = common.BytesToHash(SeedHash(currentWork.NumberU64())).Hex()
|
res[1] = common.BytesToHash(SeedHash(currentWork.NumberU64())).Hex()
|
||||||
// Calculate the "target" to be returned to the external sealer
|
|
||||||
|
// Calculate the "target" to be returned to the external sealer.
|
||||||
n := big.NewInt(1)
|
n := big.NewInt(1)
|
||||||
n.Lsh(n, 255)
|
n.Lsh(n, 255)
|
||||||
n.Div(n, currentWork.Difficulty())
|
n.Div(n, currentWork.Difficulty())
|
||||||
n.Lsh(n, 1)
|
n.Lsh(n, 1)
|
||||||
res[2] = common.BytesToHash(n.Bytes()).Hex()
|
res[2] = common.BytesToHash(n.Bytes()).Hex()
|
||||||
|
|
||||||
|
// Trace the seal work fetched by remote sealer.
|
||||||
works[currentWork.HashNoNonce()] = currentWork
|
works[currentWork.HashNoNonce()] = currentWork
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// submitWork verifies the submitted pow solution, returning
|
// submitWork verifies the submitted pow solution, returning
|
||||||
// whether the solution was accepted or not (not can be both a bad pow as well as
|
// whether the solution was accepted or not (not can be both a bad pow as well as
|
||||||
// any other error, like no work pending).
|
// any other error, like no pending work or stale mining result).
|
||||||
submitWork := func(result mineResult) bool {
|
submitWork := func(nonce types.BlockNonce, mixDigest common.Hash, hash common.Hash) bool {
|
||||||
// Make sure the work submitted is present
|
// Make sure the work submitted is present
|
||||||
block := works[result.hash]
|
block := works[hash]
|
||||||
if block == nil {
|
if block == nil {
|
||||||
log.Info("Work submitted but none pending", "hash", result.hash)
|
log.Info("Work submitted but none pending", "hash", hash)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Make sure the Engine solutions is indeed valid
|
|
||||||
|
// Verify the correctness of submitted result.
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
header.Nonce = result.nonce
|
header.Nonce = nonce
|
||||||
header.MixDigest = result.mixDigest
|
header.MixDigest = mixDigest
|
||||||
|
|
||||||
if err := ethash.VerifySeal(nil, header); err != nil {
|
if err := ethash.VerifySeal(nil, header); err != nil {
|
||||||
log.Warn("Invalid proof-of-work submitted", "hash", result.hash, "err", err)
|
log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solutions seems to be valid, return to the miner and notify acceptance
|
// Make sure the result channel is created.
|
||||||
|
if ethash.resultCh == nil {
|
||||||
|
log.Warn("Ethash result channel is empty, submitted mining result is rejected")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solutions seems to be valid, return to the miner and notify acceptance.
|
||||||
select {
|
select {
|
||||||
case resultCh <- block.WithSeal(header):
|
case ethash.resultCh <- block.WithSeal(header):
|
||||||
delete(works, result.hash)
|
delete(works, hash)
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
log.Info("Work submitted is stale", "hash", result.hash)
|
log.Info("Work submitted is stale", "hash", hash)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -234,48 +243,53 @@ running:
|
||||||
// Start new round mining, throw out all previous work.
|
// Start new round mining, throw out all previous work.
|
||||||
works = make(map[common.Hash]*types.Block)
|
works = make(map[common.Hash]*types.Block)
|
||||||
}
|
}
|
||||||
// Update current work with new received block
|
// Update current work with new received block.
|
||||||
// Note same work can be past twice, happens when changing CPU threads.
|
// Note same work can be past twice, happens when changing CPU threads.
|
||||||
currentWork = block
|
currentWork = block
|
||||||
|
|
||||||
case op := <-ethash.remoteOp:
|
case work := <-ethash.fetchWorkCh:
|
||||||
switch op.typ {
|
// Return current mining work to remote miner.
|
||||||
case opGetWork:
|
miningWork, err := getWork()
|
||||||
// Return current mining work to remote miner
|
|
||||||
work, err := getWork()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
op.errCh <- err
|
work.errCh <- err
|
||||||
} else {
|
} else {
|
||||||
close(op.errCh)
|
close(work.errCh)
|
||||||
op.workCh <- work
|
work.resCh <- miningWork
|
||||||
}
|
}
|
||||||
case opSubmitWork:
|
|
||||||
// Verify submitted PoW solution based on maintained mining blocks
|
case result := <-ethash.submitWorkCh:
|
||||||
if submitWork(op.result) {
|
// Verify submitted PoW solution based on maintained mining blocks.
|
||||||
close(op.errCh)
|
if submitWork(result.nonce, result.mixDigest, result.hash) {
|
||||||
|
close(result.errCh)
|
||||||
} else {
|
} else {
|
||||||
op.errCh <- errInvalidSealResult
|
result.errCh <- errInvalidSealResult
|
||||||
}
|
}
|
||||||
case opHashrate:
|
|
||||||
// This case is used by remote miner hashrate operation
|
case result := <-ethash.submitRateCh:
|
||||||
if op.rateOp != nil {
|
// Trace remote sealer's hash rate by submitted value.
|
||||||
op.rateOp(hashrate)
|
rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
|
||||||
close(op.errCh)
|
close(result.done)
|
||||||
}
|
|
||||||
default:
|
case req := <-ethash.fetchRateCh:
|
||||||
op.errCh <- errUndefinedRemoteOp
|
// Gather all hash rate submitted by remote sealer.
|
||||||
|
var total uint64
|
||||||
|
for _, rate := range rates {
|
||||||
|
// this could overflow
|
||||||
|
total += rate.rate
|
||||||
}
|
}
|
||||||
|
req <- total
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
// Clear stale submited hashrate
|
// Clear stale submitted hash rate.
|
||||||
for id, rate := range hashrate {
|
for id, rate := range rates {
|
||||||
if time.Since(rate.ping) > 10*time.Second {
|
if time.Since(rate.ping) > 10*time.Second {
|
||||||
delete(hashrate, id)
|
delete(rates, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-ethash.exitCh:
|
case errCh := <-ethash.exitCh:
|
||||||
// Exit remote loop if ethash object is cleared by GC
|
// Exit remote loop if ethash is closed and return relevant error.
|
||||||
|
errCh <- nil
|
||||||
break running
|
break running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -411,6 +411,7 @@ func (s *Ethereum) Start(srvr *p2p.Server) error {
|
||||||
func (s *Ethereum) Stop() error {
|
func (s *Ethereum) Stop() error {
|
||||||
s.bloomIndexer.Close()
|
s.bloomIndexer.Close()
|
||||||
s.blockchain.Stop()
|
s.blockchain.Stop()
|
||||||
|
s.engine.Close()
|
||||||
s.protocolManager.Stop()
|
s.protocolManager.Stop()
|
||||||
if s.lesServer != nil {
|
if s.lesServer != nil {
|
||||||
s.lesServer.Stop()
|
s.lesServer.Stop()
|
||||||
|
|
@ -421,6 +422,5 @@ func (s *Ethereum) Stop() error {
|
||||||
|
|
||||||
s.chainDb.Close()
|
s.chainDb.Close()
|
||||||
close(s.shutdownChan)
|
close(s.shutdownChan)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ var Modules = map[string]string{
|
||||||
"admin": Admin_JS,
|
"admin": Admin_JS,
|
||||||
"chequebook": Chequebook_JS,
|
"chequebook": Chequebook_JS,
|
||||||
"clique": Clique_JS,
|
"clique": Clique_JS,
|
||||||
|
"ethash": Ethash_JS,
|
||||||
"debug": Debug_JS,
|
"debug": Debug_JS,
|
||||||
"eth": Eth_JS,
|
"eth": Eth_JS,
|
||||||
"miner": Miner_JS,
|
"miner": Miner_JS,
|
||||||
|
|
@ -109,6 +110,29 @@ web3._extend({
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const Ethash_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'ethash',
|
||||||
|
methods: [
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'getWork',
|
||||||
|
call: 'ethash_getWork',
|
||||||
|
params: 0
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'submitWork',
|
||||||
|
call: 'ethash_submitWork',
|
||||||
|
params: 3,
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'submitHashRate',
|
||||||
|
call: 'ethash_submitHashRate',
|
||||||
|
params: 2,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
const Admin_JS = `
|
const Admin_JS = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'admin',
|
property: 'admin',
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ func (s *LightEthereum) Stop() error {
|
||||||
s.blockchain.Stop()
|
s.blockchain.Stop()
|
||||||
s.protocolManager.Stop()
|
s.protocolManager.Stop()
|
||||||
s.txPool.Stop()
|
s.txPool.Stop()
|
||||||
|
s.engine.Close()
|
||||||
|
|
||||||
s.eventMux.Stop()
|
s.eventMux.Stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -447,13 +447,6 @@ func (self *worker) commitNewWork() {
|
||||||
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
||||||
misc.ApplyDAOHardFork(work.state)
|
misc.ApplyDAOHardFork(work.state)
|
||||||
}
|
}
|
||||||
pending, err := self.eth.TxPool().Pending()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to fetch pending transactions", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
|
|
||||||
work.commitTransactions(self.mux, txs, self.chain, self.coinbase)
|
|
||||||
|
|
||||||
// compute uncles for the new block.
|
// compute uncles for the new block.
|
||||||
var (
|
var (
|
||||||
|
|
@ -477,16 +470,41 @@ func (self *worker) commitNewWork() {
|
||||||
for _, hash := range badUncles {
|
for _, hash := range badUncles {
|
||||||
delete(self.possibleUncles, hash)
|
delete(self.possibleUncles, hash)
|
||||||
}
|
}
|
||||||
// Create the new block to seal with the consensus engine
|
|
||||||
|
// Create an empty block based on temporary copied state for sealing in advance without waiting block
|
||||||
|
// execution finished.
|
||||||
|
if work.Block, err = self.engine.Finalize(self.chain, header, work.state.Copy(), nil, uncles, nil); err != nil {
|
||||||
|
log.Error("Failed to finalize block for temporary sealing", "err", err)
|
||||||
|
} else {
|
||||||
|
// Push empty work in advance without applying pending transaction.
|
||||||
|
// The reason is transactions execution can cost a lot and sealer need to
|
||||||
|
// take advantage of this part time.
|
||||||
|
if atomic.LoadInt32(&self.mining) == 1 {
|
||||||
|
log.Info("Commit new empty mining work", "number", work.Block.Number(), "uncles", len(uncles))
|
||||||
|
}
|
||||||
|
self.push(work)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the block with all available pending transactions.
|
||||||
|
pending, err := self.eth.TxPool().Pending()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to fetch pending transactions", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
|
||||||
|
work.commitTransactions(self.mux, txs, self.chain, self.coinbase)
|
||||||
|
|
||||||
|
// Create the full block to seal with the consensus engine
|
||||||
if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil {
|
if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil {
|
||||||
log.Error("Failed to finalize block for sealing", "err", err)
|
log.Error("Failed to finalize block for sealing", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// We only care about logging if we're actually mining.
|
// We only care about logging if we're actually mining.
|
||||||
if atomic.LoadInt32(&self.mining) == 1 {
|
if atomic.LoadInt32(&self.mining) == 1 {
|
||||||
log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart)))
|
log.Info("Commit new full mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart)))
|
||||||
self.unconfirmed.Shift(work.Block.NumberU64() - 1)
|
self.unconfirmed.Shift(work.Block.NumberU64() - 1)
|
||||||
}
|
}
|
||||||
|
// Push full work to sealer, which will replace the empty work sent before automatically.
|
||||||
self.push(work)
|
self.push(work)
|
||||||
self.updateSnapshot()
|
self.updateSnapshot()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue