aboutsummaryrefslogtreecommitdiff
path: root/consensus
diff options
context:
space:
mode:
authorTed Yin <tederminant@gmail.com>2020-09-18 13:14:29 -0400
committerGitHub <noreply@github.com>2020-09-18 13:14:29 -0400
commitd048937c48753d9eaef771bf71820cf95d79df26 (patch)
tree1a7f65fcd72e77092525ab01625b8b9d365e3e40 /consensus
parent7d1388c743b4ec8f4a86bea95bfada785dee83f7 (diff)
parent7d8c85cf8895b0f998d8eafb02f99d5b689fcd59 (diff)
Merge pull request #34 from ava-labs/devv0.3.0-rc.5
Dev
Diffstat (limited to 'consensus')
-rw-r--r--consensus/clique/api.go62
-rw-r--r--consensus/clique/clique.go58
-rw-r--r--consensus/clique/snapshot.go6
-rw-r--r--consensus/consensus.go32
-rw-r--r--consensus/dummy/consensus.go33
-rw-r--r--consensus/errors.go2
-rw-r--r--consensus/ethash/algorithm.go8
-rw-r--r--consensus/ethash/api.go30
-rw-r--r--consensus/ethash/consensus.go44
-rw-r--r--consensus/ethash/ethash.go175
-rw-r--r--consensus/ethash/sealer.go357
-rw-r--r--consensus/misc/dao.go2
-rw-r--r--consensus/misc/forks.go2
13 files changed, 456 insertions, 355 deletions
diff --git a/consensus/clique/api.go b/consensus/clique/api.go
index 04e74eb..4776b97 100644
--- a/consensus/clique/api.go
+++ b/consensus/clique/api.go
@@ -17,16 +17,18 @@
package clique
import (
+ "fmt"
+
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common"
)
// API is a user facing RPC API to allow controlling the signer and voting
// mechanisms of the proof-of-authority scheme.
type API struct {
- chain consensus.ChainReader
+ chain consensus.ChainHeaderReader
clique *Clique
}
@@ -117,3 +119,59 @@ func (api *API) Discard(address common.Address) {
delete(api.clique.proposals, address)
}
+
+type status struct {
+ InturnPercent float64 `json:"inturnPercent"`
+ SigningStatus map[common.Address]int `json:"sealerActivity"`
+ NumBlocks uint64 `json:"numBlocks"`
+}
+
+// Status returns the status of the last N blocks,
+// - the number of active signers,
+// - the number of signers,
+// - the percentage of in-turn blocks
+func (api *API) Status() (*status, error) {
+ var (
+ numBlocks = uint64(64)
+ header = api.chain.CurrentHeader()
+ diff = uint64(0)
+ optimals = 0
+ )
+ snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ signers = snap.signers()
+ end = header.Number.Uint64()
+ start = end - numBlocks
+ )
+ if numBlocks > end {
+ start = 1
+ numBlocks = end - start
+ }
+ signStatus := make(map[common.Address]int)
+ for _, s := range signers {
+ signStatus[s] = 0
+ }
+ for n := start; n < end; n++ {
+ h := api.chain.GetHeaderByNumber(n)
+ if h == nil {
+ return nil, fmt.Errorf("missing block %d", n)
+ }
+ if h.Difficulty.Cmp(diffInTurn) == 0 {
+ optimals++
+ }
+ diff += h.Difficulty.Uint64()
+ sealer, err := api.clique.Author(h)
+ if err != nil {
+ return nil, err
+ }
+ signStatus[sealer]++
+ }
+ return &status{
+ InturnPercent: float64(100*optimals) / float64(numBlocks),
+ SigningStatus: signStatus,
+ NumBlocks: numBlocks,
+ }, nil
+}
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index 3714733..b27525b 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -33,12 +33,13 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/ethdb"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3"
)
@@ -121,9 +122,9 @@ var (
// turn of the signer.
errWrongDifficulty = errors.New("wrong difficulty")
- // ErrInvalidTimestamp is returned if the timestamp of a block is lower than
+ // errInvalidTimestamp is returned if the timestamp of a block is lower than
// the previous block's timestamp + the minimum block period.
- ErrInvalidTimestamp = errors.New("invalid timestamp")
+ errInvalidTimestamp = errors.New("invalid timestamp")
// errInvalidVotingChain is returned if an authorization list is attempted to
// be modified via out-of-range or non-contiguous headers.
@@ -137,9 +138,8 @@ var (
errRecentlySigned = errors.New("recently signed")
)
-// SignerFn is a signer callback function to request a header to be signed by a
-// backing account.
-type SignerFn func(accounts.Account, string, []byte) ([]byte, error)
+// SignerFn hashes and signs the data to be signed by a backing account.
+type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error)
// ecrecover extracts the Ethereum account address from a signed header.
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
@@ -213,14 +213,14 @@ func (c *Clique) Author(header *types.Header) (common.Address, error) {
}
// VerifyHeader checks whether a header conforms to the consensus rules.
-func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
+func (c *Clique) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return c.verifyHeader(chain, header, nil)
}
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
// method returns a quit channel to abort the operations and a results channel to
// retrieve the async verifications (the order is that of the input slice).
-func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
+func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
abort := make(chan struct{})
results := make(chan error, len(headers))
@@ -242,7 +242,7 @@ func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Hea
// caller may optionally pass in a batch of parents (ascending order) to avoid
// looking those up from the database. This is useful for concurrently verifying
// a batch of new headers.
-func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
+func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
if header.Number == nil {
return errUnknownBlock
}
@@ -305,13 +305,13 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
// rather depend on a batch of previous headers. The caller may optionally pass
// in a batch of parents (ascending order) to avoid looking those up from the
// database. This is useful for concurrently verifying a batch of new headers.
-func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
+func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// The genesis block is the always valid dead-end
number := header.Number.Uint64()
if number == 0 {
return nil
}
- // Ensure that the block's timestamp isn't too close to it's parent
+ // Ensure that the block's timestamp isn't too close to its parent
var parent *types.Header
if len(parents) > 0 {
parent = parents[len(parents)-1]
@@ -322,7 +322,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
return consensus.ErrUnknownAncestor
}
if parent.Time+c.config.Period > header.Time {
- return ErrInvalidTimestamp
+ return errInvalidTimestamp
}
// Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
@@ -345,7 +345,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
}
// snapshot retrieves the authorization snapshot at a given point in time.
-func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
+func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
// Search for a snapshot in memory or on disk for checkpoints
var (
headers []*types.Header
@@ -369,7 +369,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
// at a checkpoint block without a parent (light client CHT), or we have piled
// up more headers than allowed to be reorged (chain reinit from a freezer),
// consider the checkpoint trusted and snapshot it.
- if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.ImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
+ if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
checkpoint := chain.GetHeaderByNumber(number)
if checkpoint != nil {
hash := checkpoint.Hash()
@@ -436,7 +436,7 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e
// VerifySeal implements consensus.Engine, checking whether the signature contained
// in the header satisfies the consensus protocol requirements.
-func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
+func (c *Clique) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return c.verifySeal(chain, header, nil)
}
@@ -444,7 +444,7 @@ func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) e
// consensus protocol requirements. The method accepts an optional list of parent
// headers that aren't yet part of the local blockchain to generate the snapshots
// from.
-func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
+func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// Verifying the genesis block is not supported
number := header.Number.Uint64()
if number == 0 {
@@ -487,7 +487,7 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
// Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top.
-func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) error {
+func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
// If the block isn't a checkpoint, cast a random vote (good enough for now)
header.Coinbase = common.Address{}
header.Nonce = types.BlockNonce{}
@@ -522,7 +522,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
// Set the correct difficulty
header.Difficulty = CalcDifficulty(snap, c.signer)
- // Ensure the extra data has all it's components
+ // Ensure the extra data has all its components
if len(header.Extra) < extraVanity {
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
}
@@ -552,7 +552,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given.
-func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
+func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil)
@@ -560,13 +560,13 @@ func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, sta
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
-func (c *Clique) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
+func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil)
// Assemble and return the final block for sealing
- return types.NewBlock(header, txs, nil, receipts, nil), nil
+ return types.NewBlock(header, txs, nil, receipts, new(trie.Trie), nil), nil
}
// Authorize injects a private key into the consensus engine to mint new blocks
@@ -581,7 +581,7 @@ func (c *Clique) Authorize(signer common.Address, signFn SignerFn) {
// Seal implements consensus.Engine, attempting to create a sealed block using
// the local signing credentials.
-func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
+func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
header := block.Header()
// Sealing the genesis block is not supported
@@ -654,7 +654,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have based on the previous blocks in the chain and the
// current signer.
-func (c *Clique) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
+func (c *Clique) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
if err != nil {
return nil
@@ -684,7 +684,7 @@ func (c *Clique) Close() error {
// APIs implements consensus.Engine, returning the user facing RPC API to allow
// controlling the signer voting.
-func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API {
+func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return []rpc.API{{
Namespace: "clique",
Version: "1.0",
diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go
index 6660c0f..2bf7430 100644
--- a/consensus/clique/snapshot.go
+++ b/consensus/clique/snapshot.go
@@ -24,9 +24,9 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/ethdb"
- "github.com/ava-labs/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
lru "github.com/hashicorp/golang-lru"
)
diff --git a/consensus/consensus.go b/consensus/consensus.go
index 603a3e9..b29abac 100644
--- a/consensus/consensus.go
+++ b/consensus/consensus.go
@@ -24,12 +24,12 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common"
)
-// ChainReader defines a small collection of methods needed to access the local
-// blockchain during header and/or uncle verification.
-type ChainReader interface {
+// ChainHeaderReader defines a small collection of methods needed to access the local
+// blockchain during header verification.
+type ChainHeaderReader interface {
// Config retrieves the blockchain's chain configuration.
Config() *params.ChainConfig
@@ -44,6 +44,12 @@ type ChainReader interface {
// GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash common.Hash) *types.Header
+}
+
+// ChainReader defines a small collection of methods needed to access the local
+// blockchain during header and/or uncle verification.
+type ChainReader interface {
+ ChainHeaderReader
// GetBlock retrieves a block from the database by hash and number.
GetBlock(hash common.Hash, number uint64) *types.Block
@@ -59,13 +65,13 @@ type Engine interface {
// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
- VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
+ VerifyHeader(chain ChainHeaderReader, header *types.Header, seal bool) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of
// the input slice).
- VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
+ VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
// VerifyUncles verifies that the given block's uncles conform to the consensus
// rules of a given engine.
@@ -73,18 +79,18 @@ type Engine interface {
// VerifySeal checks whether the crypto seal on a header is valid according to
// the consensus rules of the given engine.
- VerifySeal(chain ChainReader, header *types.Header) error
+ VerifySeal(chain ChainHeaderReader, header *types.Header) error
// Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline.
- Prepare(chain ChainReader, header *types.Header) error
+ Prepare(chain ChainHeaderReader, header *types.Header) error
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// but does not assemble the block.
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
- Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
+ Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header)
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
@@ -92,7 +98,7 @@ type Engine interface {
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
- FinalizeAndAssemble(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
+ FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes
@@ -100,17 +106,17 @@ type Engine interface {
//
// Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm.
- Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
+ Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
// SealHash returns the hash of a block prior to it being sealed.
SealHash(header *types.Header) common.Hash
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have.
- CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
+ CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int
// APIs returns the RPC APIs this consensus engine provides.
- APIs(chain ChainReader) []rpc.API
+ APIs(chain ChainHeaderReader) []rpc.API
// Close terminates any background threads maintained by the consensus engine.
Close() error
diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go
index 494e4be..da63673 100644
--- a/consensus/dummy/consensus.go
+++ b/consensus/dummy/consensus.go
@@ -13,14 +13,15 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/rlp"
mapset "github.com/deckarep/golang-set"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
)
-type OnFinalizeCallbackType = func(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header)
+type OnFinalizeCallbackType = func(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header)
type OnFinalizeAndAssembleCallbackType = func(state *state.StateDB, txs []*types.Transaction) ([]byte, error)
-type OnAPIsCallbackType = func(consensus.ChainReader) []rpc.API
+type OnAPIsCallbackType = func(consensus.ChainHeaderReader) []rpc.API
type OnExtraStateChangeType = func(block *types.Block, statedb *state.StateDB) error
type ConsensusCallbacks struct {
@@ -55,7 +56,7 @@ var (
)
// modified from consensus.go
-func (self *DummyEngine) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
+func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool) error {
// Ensure that the header's extra-data section is of a reasonable size
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
@@ -103,7 +104,7 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainReader, header, paren
return nil
}
-func (self *DummyEngine) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, seals []bool, index int) error {
+func (self *DummyEngine) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int) error {
var parent *types.Header
if index == 0 {
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
@@ -123,7 +124,7 @@ func (self *DummyEngine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
-func (self *DummyEngine) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
+func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
// Short circuit if the header is known, or it's parent not
number := header.Number.Uint64()
if chain.GetHeader(header.Hash(), number) != nil {
@@ -137,7 +138,7 @@ func (self *DummyEngine) VerifyHeader(chain consensus.ChainReader, header *types
return self.verifyHeader(chain, header, parent, false, seal)
}
-func (self *DummyEngine) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
+func (self *DummyEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
// Spawn as many workers as allowed threads
workers := runtime.GOMAXPROCS(0)
if len(headers) < workers {
@@ -239,17 +240,17 @@ func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.
return nil
}
-func (self *DummyEngine) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
+func (self *DummyEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return nil
}
-func (self *DummyEngine) Prepare(chain consensus.ChainReader, header *types.Header) error {
+func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
header.Difficulty = big.NewInt(1)
return nil
}
func (self *DummyEngine) Finalize(
- chain consensus.ChainReader, header *types.Header,
+ chain consensus.ChainHeaderReader, header *types.Header,
state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header) {
if self.cb.OnFinalize != nil {
@@ -259,7 +260,7 @@ func (self *DummyEngine) Finalize(
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
}
-func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
+func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
var extdata []byte
if self.cb.OnFinalizeAndAssemble != nil {
@@ -273,10 +274,10 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainReader, header
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return
- return types.NewBlock(header, txs, uncles, receipts, extdata), nil
+ return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie), extdata), nil
}
-func (self *DummyEngine) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) (err error) {
+func (self *DummyEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) (err error) {
if self.cb.OnSeal != nil {
err = self.cb.OnSeal(block)
} else {
@@ -314,11 +315,11 @@ func (self *DummyEngine) SealHash(header *types.Header) (hash common.Hash) {
return hash
}
-func (self *DummyEngine) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
+func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return big.NewInt(1)
}
-func (self *DummyEngine) APIs(chain consensus.ChainReader) (res []rpc.API) {
+func (self *DummyEngine) APIs(chain consensus.ChainHeaderReader) (res []rpc.API) {
res = nil
if self.cb.OnAPIs != nil {
res = self.cb.OnAPIs(chain)
diff --git a/consensus/errors.go b/consensus/errors.go
index a005c5f..ac5242f 100644
--- a/consensus/errors.go
+++ b/consensus/errors.go
@@ -31,7 +31,7 @@ var (
// to the current node.
ErrFutureBlock = errors.New("block in the future")
- // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's
+ // ErrInvalidNumber is returned if a block's number doesn't equal its parent's
// plus one.
ErrInvalidNumber = errors.New("invalid block number")
)
diff --git a/consensus/ethash/algorithm.go b/consensus/ethash/algorithm.go
index 2b66c33..d6c8710 100644
--- a/consensus/ethash/algorithm.go
+++ b/consensus/ethash/algorithm.go
@@ -27,10 +27,10 @@ import (
"time"
"unsafe"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/bitutil"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/bitutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/log"
"golang.org/x/crypto/sha3"
)
diff --git a/consensus/ethash/api.go b/consensus/ethash/api.go
index 34ed386..c983504 100644
--- a/consensus/ethash/api.go
+++ b/consensus/ethash/api.go
@@ -20,15 +20,15 @@ import (
"errors"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
)
var errEthashStopped = errors.New("ethash stopped")
// API exposes ethash related methods for the RPC interface.
type API struct {
- ethash *Ethash // Make sure the mode of ethash is normal.
+ ethash *Ethash
}
// GetWork returns a work package for external miner.
@@ -39,7 +39,7 @@ type API struct {
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
// result[3] - hex encoded block number
func (api *API) GetWork() ([4]string, error) {
- if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
+ if api.ethash.remote == nil {
return [4]string{}, errors.New("not supported")
}
@@ -47,13 +47,11 @@ func (api *API) GetWork() ([4]string, error) {
workCh = make(chan [4]string, 1)
errc = make(chan error, 1)
)
-
select {
- case api.ethash.fetchWorkCh <- &sealWork{errc: errc, res: workCh}:
- case <-api.ethash.exitCh:
+ case api.ethash.remote.fetchWorkCh <- &sealWork{errc: errc, res: workCh}:
+ case <-api.ethash.remote.exitCh:
return [4]string{}, errEthashStopped
}
-
select {
case work := <-workCh:
return work, nil
@@ -66,23 +64,21 @@ func (api *API) GetWork() ([4]string, error) {
// It returns an indication if the work was accepted.
// Note either an invalid solution, a stale work a non-existent work will return false.
func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) bool {
- if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
+ if api.ethash.remote == nil {
return false
}
var errc = make(chan error, 1)
-
select {
- case api.ethash.submitWorkCh <- &mineResult{
+ case api.ethash.remote.submitWorkCh <- &mineResult{
nonce: nonce,
mixDigest: digest,
hash: hash,
errc: errc,
}:
- case <-api.ethash.exitCh:
+ case <-api.ethash.remote.exitCh:
return false
}
-
err := <-errc
return err == nil
}
@@ -94,21 +90,19 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo
// It accepts the miner hash rate and an identifier which must be unique
// between nodes.
func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool {
- if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
+ if api.ethash.remote == nil {
return false
}
var done = make(chan struct{}, 1)
-
select {
- case api.ethash.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
- case <-api.ethash.exitCh:
+ case api.ethash.remote.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
+ case <-api.ethash.remote.exitCh:
return false
}
// Block until hash rate submitted successfully.
<-done
-
return true
}
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index dc88a79..dc56b6f 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -29,10 +29,11 @@ import (
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
- "github.com/ava-labs/go-ethereum/rlp"
mapset "github.com/deckarep/golang-set"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3"
)
@@ -44,6 +45,11 @@ var (
maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks
+ // calcDifficultyEip2384 is the difficulty adjustment algorithm as specified by EIP 2384.
+ // It offsets the bomb 4M blocks from Constantinople, so in total 9M blocks.
+ // Specification EIP-2384: https://eips.ethereum.org/EIPS/eip-2384
+ calcDifficultyEip2384 = makeDifficultyCalculator(big.NewInt(9000000))
+
// calcDifficultyConstantinople is the difficulty adjustment algorithm for Constantinople.
// It returns the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules, but with
@@ -63,7 +69,7 @@ var (
// codebase, inherently breaking if the engine is swapped out. Please put common
// error types into the consensus package.
var (
- errZeroBlockTime = errors.New("timestamp equals parent's")
+ errOlderBlockTime = errors.New("timestamp older than parent")
errTooManyUncles = errors.New("too many uncles")
errDuplicateUncle = errors.New("duplicate uncle")
errUncleIsAncestor = errors.New("uncle is ancestor")
@@ -81,12 +87,12 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
// VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
-func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
+func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
// If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake {
return nil
}
- // Short circuit if the header is known, or it's parent not
+ // Short circuit if the header is known, or its parent not
number := header.Number.Uint64()
if chain.GetHeader(header.Hash(), number) != nil {
return nil
@@ -102,7 +108,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications.
-func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
+func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
// If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake || len(headers) == 0 {
abort, results := make(chan struct{}), make(chan error, len(headers))
@@ -164,7 +170,7 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type
return abort, errorsOut
}
-func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, seals []bool, index int) error {
+func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int) error {
var parent *types.Header
if index == 0 {
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
@@ -238,7 +244,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
// verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
// See YP section 4.3.4. "Block Header Validity"
-func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
+func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool) error {
// Ensure that the header's extra-data section is of a reasonable size
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
@@ -250,9 +256,9 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
}
}
if header.Time <= parent.Time {
- return errZeroBlockTime
+ return errOlderBlockTime
}
- // Verify the block's difficulty based in it's timestamp and parent's difficulty
+ // Verify the block's difficulty based on its timestamp and parent's difficulty
expected := ethash.CalcDifficulty(chain, header.Time, parent)
if expected.Cmp(header.Difficulty) != 0 {
@@ -301,7 +307,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
-func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
+func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return CalcDifficulty(chain.Config(), time, parent)
}
@@ -311,6 +317,8 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, p
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1)
switch {
+ case config.IsMuirGlacier(next):
+ return calcDifficultyEip2384(time, parent)
case config.IsConstantinople(next):
return calcDifficultyConstantinople(time, parent)
case config.IsByzantium(next):
@@ -479,14 +487,14 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
// VerifySeal implements consensus.Engine, checking whether the given block satisfies
// the PoW difficulty requirements.
-func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
+func (ethash *Ethash) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return ethash.verifySeal(chain, header, false)
}
// verifySeal checks whether a block satisfies the PoW difficulty requirements,
// either using the usual ethash cache for it, or alternatively using a full DAG
// to make remote mining fast.
-func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Header, fulldag bool) error {
+func (ethash *Ethash) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, fulldag bool) error {
// If we're running a fake PoW, accept any seal as valid
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
time.Sleep(ethash.fakeDelay)
@@ -551,7 +559,7 @@ func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Head
// Prepare implements consensus.Engine, initializing the difficulty field of a
// header to conform to the ethash protocol. The changes are done inline.
-func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error {
+func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil {
return consensus.ErrUnknownAncestor
@@ -562,7 +570,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state on the header
-func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
+func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
@@ -570,13 +578,13 @@ func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block.
-func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
+func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return
- return types.NewBlock(header, txs, uncles, receipts, nil), nil
+ return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie), nil), nil
}
// SealHash returns the hash of a block prior to it being sealed.
diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go
index 53420d0..4a3912d 100644
--- a/consensus/ethash/ethash.go
+++ b/consensus/ethash/ethash.go
@@ -34,12 +34,10 @@ import (
"unsafe"
"github.com/ava-labs/coreth/consensus"
- "github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/metrics"
mmap "github.com/edsrzf/mmap-go"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
"github.com/hashicorp/golang-lru/simplelru"
)
@@ -50,7 +48,7 @@ var (
two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
// sharedEthash is a full instance that can be shared between multiple users.
- sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal}, nil, false)
+ sharedEthash = New(Config{"", 3, 0, false, "", 1, 0, false, ModeNormal, nil}, nil, false)
// algorithmRevision is the data structure version used for file naming.
algorithmRevision = 23
@@ -67,7 +65,7 @@ func isLittleEndian() bool {
}
// memoryMap tries to memory map a file of uint32s for read only access.
-func memoryMap(path string) (*os.File, mmap.MMap, []uint32, error) {
+func memoryMap(path string, lock bool) (*os.File, mmap.MMap, []uint32, error) {
file, err := os.OpenFile(path, os.O_RDONLY, 0644)
if err != nil {
return nil, nil, nil, err
@@ -84,6 +82,13 @@ func memoryMap(path string) (*os.File, mmap.MMap, []uint32, error) {
return nil, nil, nil, ErrInvalidDumpMagic
}
}
+ if lock {
+ if err := mem.Lock(); err != nil {
+ mem.Unmap()
+ file.Close()
+ return nil, nil, nil, err
+ }
+ }
return file, mem, buffer[len(dumpMagic):], err
}
@@ -109,7 +114,7 @@ func memoryMapFile(file *os.File, write bool) (mmap.MMap, []uint32, error) {
// memoryMapAndGenerate tries to memory map a temporary file of uint32s for write
// access, fill it with the data from a generator and then move it into the final
// path requested.
-func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) {
+func memoryMapAndGenerate(path string, size uint64, lock bool, generator func(buffer []uint32)) (*os.File, mmap.MMap, []uint32, error) {
// Ensure the data folder exists
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return nil, nil, nil, err
@@ -144,7 +149,7 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint
if err := os.Rename(temp, path); err != nil {
return nil, nil, nil, err
}
- return memoryMap(path)
+ return memoryMap(path, lock)
}
// lru tracks caches or datasets by their last use time, keeping at most N of them.
@@ -215,7 +220,7 @@ func newCache(epoch uint64) interface{} {
}
// generate ensures that the cache content is generated before use.
-func (c *cache) generate(dir string, limit int, test bool) {
+func (c *cache) generate(dir string, limit int, lock bool, test bool) {
c.once.Do(func() {
size := cacheSize(c.epoch*epochLength + 1)
seed := seedHash(c.epoch*epochLength + 1)
@@ -242,7 +247,7 @@ func (c *cache) generate(dir string, limit int, test bool) {
// Try to load the file from disk and memory map it
var err error
- c.dump, c.mmap, c.cache, err = memoryMap(path)
+ c.dump, c.mmap, c.cache, err = memoryMap(path, lock)
if err == nil {
logger.Debug("Loaded old ethash cache from disk")
return
@@ -250,7 +255,7 @@ func (c *cache) generate(dir string, limit int, test bool) {
logger.Debug("Failed to load old ethash cache", "err", err)
// No previous cache available, create a new cache file to fill
- c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) })
+ c.dump, c.mmap, c.cache, err = memoryMapAndGenerate(path, size, lock, func(buffer []uint32) { generateCache(buffer, c.epoch, seed) })
if err != nil {
logger.Error("Failed to generate mapped ethash cache", "err", err)
@@ -292,7 +297,7 @@ func newDataset(epoch uint64) interface{} {
}
// generate ensures that the dataset content is generated before use.
-func (d *dataset) generate(dir string, limit int, test bool) {
+func (d *dataset) generate(dir string, limit int, lock bool, test bool) {
d.once.Do(func() {
// Mark the dataset generated after we're done. This is needed for remote
defer atomic.StoreUint32(&d.done, 1)
@@ -328,7 +333,7 @@ func (d *dataset) generate(dir string, limit int, test bool) {
// Try to load the file from disk and memory map it
var err error
- d.dump, d.mmap, d.dataset, err = memoryMap(path)
+ d.dump, d.mmap, d.dataset, err = memoryMap(path, lock)
if err == nil {
logger.Debug("Loaded old ethash dataset from disk")
return
@@ -339,7 +344,7 @@ func (d *dataset) generate(dir string, limit int, test bool) {
cache := make([]uint32, csize/4)
generateCache(cache, d.epoch, seed)
- d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) })
+ d.dump, d.mmap, d.dataset, err = memoryMapAndGenerate(path, dsize, lock, func(buffer []uint32) { generateDataset(buffer, d.epoch, cache) })
if err != nil {
logger.Error("Failed to generate mapped ethash dataset", "err", err)
@@ -374,13 +379,13 @@ func (d *dataset) finalizer() {
// MakeCache generates a new ethash cache and optionally stores it to disk.
func MakeCache(block uint64, dir string) {
c := cache{epoch: block / epochLength}
- c.generate(dir, math.MaxInt32, false)
+ c.generate(dir, math.MaxInt32, false, false)
}
// MakeDataset generates a new ethash dataset and optionally stores it to disk.
func MakeDataset(block uint64, dir string) {
d := dataset{epoch: block / epochLength}
- d.generate(dir, math.MaxInt32, false)
+ d.generate(dir, math.MaxInt32, false, false)
}
// Mode defines the type and amount of PoW verification an ethash engine makes.
@@ -396,43 +401,17 @@ const (
// Config are the configuration parameters of the ethash.
type Config struct {
- CacheDir string
- CachesInMem int
- CachesOnDisk int
- DatasetDir string
- DatasetsInMem int
- DatasetsOnDisk int
- PowMode Mode
-}
+ CacheDir string
+ CachesInMem int
+ CachesOnDisk int
+ CachesLockMmap bool
+ DatasetDir string
+ DatasetsInMem int
+ DatasetsOnDisk int
+ DatasetsLockMmap bool
+ PowMode Mode
-// sealTask wraps a seal block with relative result channel for remote sealer thread.
-type sealTask struct {
- block *types.Block
- results chan<- *types.Block
-}
-
-// mineResult wraps the pow solution parameters for the specified block.
-type mineResult struct {
- nonce types.BlockNonce
- mixDigest common.Hash
- hash common.Hash
-
- errc chan error
-}
-
-// hashrate wraps the hash rate submitted by the remote sealer.
-type hashrate struct {
- id common.Hash
- ping time.Time
- rate uint64
-
- done chan struct{}
-}
-
-// sealWork wraps a seal work package for remote sealer.
-type sealWork struct {
- errc chan error
- res chan [4]string
+ Log log.Logger `toml:"-"`
}
// Ethash is a consensus engine based on proof-of-work implementing the ethash
@@ -448,52 +427,42 @@ type Ethash struct {
threads int // Number of threads to mine on if mining
update chan struct{} // Notification channel to update mining parameters
hashrate metrics.Meter // Meter tracking the average hashrate
-
- // Remote sealer related fields
- workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
- 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
+ remote *remoteSealer
// The fields below are hooks for testing
shared *Ethash // Shared PoW verifier to avoid cache regeneration
fakeFail uint64 // Block number which fails PoW check even in fake mode
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
- closeOnce sync.Once // Ensures exit channel will not be closed twice.
- exitCh chan chan error // Notification channel to exiting backend threads
+ 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.
}
// New creates a full sized ethash PoW scheme and starts a background thread for
// remote mining, also optionally notifying a batch of remote services of new work
// packages.
func New(config Config, notify []string, noverify bool) *Ethash {
+ if config.Log == nil {
+ config.Log = log.Root()
+ }
if config.CachesInMem <= 0 {
- log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
+ config.Log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
config.CachesInMem = 1
}
if config.CacheDir != "" && config.CachesOnDisk > 0 {
- log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
+ config.Log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
}
if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
- log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
+ config.Log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
}
ethash := &Ethash{
- config: config,
- caches: newlru("cache", config.CachesInMem, newCache),
- datasets: newlru("dataset", config.DatasetsInMem, newDataset),
- update: make(chan struct{}),
- hashrate: metrics.NewMeterForced(),
- workCh: make(chan *sealTask),
- 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(notify, noverify)
+ config: config,
+ caches: newlru("cache", config.CachesInMem, newCache),
+ datasets: newlru("dataset", config.DatasetsInMem, newDataset),
+ update: make(chan struct{}),
+ hashrate: metrics.NewMeterForced(),
+ }
+ ethash.remote = startRemoteSealer(ethash, notify, noverify)
return ethash
}
@@ -501,19 +470,13 @@ func New(config Config, notify []string, noverify bool) *Ethash {
// purposes.
func NewTester(notify []string, noverify bool) *Ethash {
ethash := &Ethash{
- config: Config{PowMode: ModeTest},
- caches: newlru("cache", 1, newCache),
- datasets: newlru("dataset", 1, newDataset),
- update: make(chan struct{}),
- hashrate: metrics.NewMeterForced(),
- workCh: make(chan *sealTask),
- 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(notify, noverify)
+ config: Config{PowMode: ModeTest, Log: log.Root()},
+ caches: newlru("cache", 1, newCache),
+ datasets: newlru("dataset", 1, newDataset),
+ update: make(chan struct{}),
+ hashrate: metrics.NewMeterForced(),
+ }
+ ethash.remote = startRemoteSealer(ethash, notify, noverify)
return ethash
}
@@ -524,6 +487,7 @@ func NewFaker() *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
+ Log: log.Root(),
},
}
}
@@ -535,6 +499,7 @@ func NewFakeFailer(fail uint64) *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
+ Log: log.Root(),
},
fakeFail: fail,
}
@@ -547,6 +512,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
+ Log: log.Root(),
},
fakeDelay: delay,
}
@@ -558,6 +524,7 @@ func NewFullFaker() *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFullFake,
+ Log: log.Root(),
},
}
}
@@ -573,13 +540,11 @@ func (ethash *Ethash) Close() error {
var err error
ethash.closeOnce.Do(func() {
// Short circuit if the exit channel is not allocated.
- if ethash.exitCh == nil {
+ if ethash.remote == nil {
return
}
- errc := make(chan error)
- ethash.exitCh <- errc
- err = <-errc
- close(ethash.exitCh)
+ close(ethash.remote.requestExit)
+ <-ethash.remote.exitCh
})
return err
}
@@ -593,12 +558,12 @@ func (ethash *Ethash) cache(block uint64) *cache {
current := currentI.(*cache)
// Wait for generation finish.
- current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
+ current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest)
// If we need a new future cache, now's a good time to regenerate it.
if futureI != nil {
future := futureI.(*cache)
- go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
+ go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.CachesLockMmap, ethash.config.PowMode == ModeTest)
}
return current
}
@@ -618,20 +583,20 @@ func (ethash *Ethash) dataset(block uint64, async bool) *dataset {
// If async is specified, generate everything in a background thread
if async && !current.generated() {
go func() {
- current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
if futureI != nil {
future := futureI.(*dataset)
- future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
}
}()
} else {
// Either blocking generation was requested, or already done
- current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
if futureI != nil {
future := futureI.(*dataset)
- go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
+ go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.DatasetsLockMmap, ethash.config.PowMode == ModeTest)
}
}
return current
@@ -680,8 +645,8 @@ func (ethash *Ethash) Hashrate() float64 {
var res = make(chan uint64, 1)
select {
- case ethash.fetchRateCh <- res:
- case <-ethash.exitCh:
+ case ethash.remote.fetchRateCh <- res:
+ case <-ethash.remote.exitCh:
// Return local hashrate only if ethash is stopped.
return ethash.hashrate.Rate1()
}
@@ -691,7 +656,7 @@ func (ethash *Ethash) Hashrate() float64 {
}
// 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.ChainHeaderReader) []rpc.API {
// In order to ensure backward compatibility, we exposes ethash RPC APIs
// to both eth and ethash namespaces.
return []rpc.API{
diff --git a/consensus/ethash/sealer.go b/consensus/ethash/sealer.go
index 799be05..cb8eed7 100644
--- a/consensus/ethash/sealer.go
+++ b/consensus/ethash/sealer.go
@@ -18,6 +18,7 @@ package ethash
import (
"bytes"
+ "context"
crand "crypto/rand"
"encoding/json"
"errors"
@@ -31,9 +32,8 @@ import (
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
)
const (
@@ -48,7 +48,7 @@ var (
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements.
-func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
+func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
// If we're running a fake PoW, simply return a 0 nonce immediately
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
header := block.Header()
@@ -56,7 +56,7 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
select {
case results <- block.WithSeal(header):
default:
- log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
+ ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
}
return nil
}
@@ -85,8 +85,8 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
threads = 0 // Allows disabling local mining without extra logic around local/remote
}
// Push new work to remote sealer
- if ethash.workCh != nil {
- ethash.workCh <- &sealTask{block: block, results: results}
+ if ethash.remote != nil {
+ ethash.remote.workCh <- &sealTask{block: block, results: results}
}
var (
pend sync.WaitGroup
@@ -111,14 +111,14 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
select {
case results <- result:
default:
- log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
+ ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
}
close(abort)
case <-ethash.update:
// Thread count was changed on user request, restart
close(abort)
if err := ethash.Seal(chain, block, results, stop); err != nil {
- log.Error("Failed to restart sealing after update", "err", err)
+ ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
}
}
// Wait for all miners to terminate and return the block
@@ -143,7 +143,7 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
attempts = int64(0)
nonce = seed
)
- logger := log.New("miner", id)
+ logger := ethash.config.Log.New("miner", id)
logger.Trace("Started ethash search for new nonces", "seed", seed)
search:
for {
@@ -186,160 +186,128 @@ search:
runtime.KeepAlive(dataset)
}
-// remote is a standalone goroutine to handle remote mining related stuff.
-func (ethash *Ethash) remote(notify []string, noverify bool) {
- var (
- works = make(map[common.Hash]*types.Block)
- rates = make(map[common.Hash]hashrate)
+// This is the timeout for HTTP requests to notify external miners.
+const remoteSealerTimeout = 1 * time.Second
- results chan<- *types.Block
- currentBlock *types.Block
- currentWork [4]string
+type remoteSealer struct {
+ works map[common.Hash]*types.Block
+ rates map[common.Hash]hashrate
+ currentBlock *types.Block
+ currentWork [4]string
+ notifyCtx context.Context
+ cancelNotify context.CancelFunc // cancels all notification requests
+ reqWG sync.WaitGroup // tracks notification request goroutines
- notifyTransport = &http.Transport{}
- notifyClient = &http.Client{
- Transport: notifyTransport,
- Timeout: time.Second,
- }
- notifyReqs = make([]*http.Request, len(notify))
- )
- // notifyWork notifies all the specified mining endpoints of the availability of
- // new work to be processed.
- notifyWork := func() {
- work := currentWork
- blob, _ := json.Marshal(work)
-
- for i, url := range notify {
- // Terminate any previously pending request and create the new work
- if notifyReqs[i] != nil {
- notifyTransport.CancelRequest(notifyReqs[i])
- }
- notifyReqs[i], _ = http.NewRequest("POST", url, bytes.NewReader(blob))
- notifyReqs[i].Header.Set("Content-Type", "application/json")
-
- // Push the new work concurrently to all the remote nodes
- go func(req *http.Request, url string) {
- res, err := notifyClient.Do(req)
- if err != nil {
- log.Warn("Failed to notify remote miner", "err", err)
- } else {
- log.Trace("Notified remote miner", "miner", url, "hash", log.Lazy{Fn: func() common.Hash { return common.HexToHash(work[0]) }}, "target", work[2])
- res.Body.Close()
- }
- }(notifyReqs[i], url)
- }
- }
- // makeWork creates 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[1], 32 bytes hex encoded seed hash used for DAG
- // result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
- // result[3], hex encoded block number
- makeWork := func(block *types.Block) {
- hash := ethash.SealHash(block.Header())
-
- currentWork[0] = hash.Hex()
- currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
- currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
- currentWork[3] = hexutil.EncodeBig(block.Number())
-
- // Trace the seal work fetched by remote sealer.
- currentBlock = block
- works[hash] = block
- }
- // submitWork verifies the submitted pow solution, returning
- // whether the solution was accepted or not (not can be both a bad pow as well as
- // any other error, like no pending work or stale mining result).
- submitWork := func(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
- if currentBlock == nil {
- log.Error("Pending work without block", "sealhash", sealhash)
- return false
- }
- // Make sure the work submitted is present
- block := works[sealhash]
- if block == nil {
- log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", currentBlock.NumberU64())
- return false
- }
- // Verify the correctness of submitted result.
- header := block.Header()
- header.Nonce = nonce
- header.MixDigest = mixDigest
-
- start := time.Now()
- if !noverify {
- if err := ethash.verifySeal(nil, header, true); err != nil {
- log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
- return false
- }
- }
- // Make sure the result channel is assigned.
- if results == nil {
- log.Warn("Ethash result channel is empty, submitted mining result is rejected")
- return false
- }
- log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
+ ethash *Ethash
+ noverify bool
+ notifyURLs []string
+ results chan<- *types.Block
+ workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
+ 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
+ requestExit chan struct{}
+ exitCh chan struct{}
+}
- // Solutions seems to be valid, return to the miner and notify acceptance.
- solution := block.WithSeal(header)
+// sealTask wraps a seal block with relative result channel for remote sealer thread.
+type sealTask struct {
+ block *types.Block
+ results chan<- *types.Block
+}
- // The submitted solution is within the scope of acceptance.
- if solution.NumberU64()+staleThreshold > currentBlock.NumberU64() {
- select {
- case results <- solution:
- log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
- return true
- default:
- log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
- return false
- }
- }
- // The submitted block is too old to accept, drop it.
- log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
- return false
+// mineResult wraps the pow solution parameters for the specified block.
+type mineResult struct {
+ nonce types.BlockNonce
+ mixDigest common.Hash
+ hash common.Hash
+
+ errc chan error
+}
+
+// hashrate wraps the hash rate submitted by the remote sealer.
+type hashrate struct {
+ id common.Hash
+ ping time.Time
+ rate uint64
+
+ done chan struct{}
+}
+
+// sealWork wraps a seal work package for remote sealer.
+type sealWork struct {
+ errc chan error
+ res chan [4]string
+}
+
+func startRemoteSealer(ethash *Ethash, urls []string, noverify bool) *remoteSealer {
+ ctx, cancel := context.WithCancel(context.Background())
+ s := &remoteSealer{
+ ethash: ethash,
+ noverify: noverify,
+ notifyURLs: urls,
+ notifyCtx: ctx,
+ cancelNotify: cancel,
+ works: make(map[common.Hash]*types.Block),
+ rates: make(map[common.Hash]hashrate),
+ workCh: make(chan *sealTask),
+ fetchWorkCh: make(chan *sealWork),
+ submitWorkCh: make(chan *mineResult),
+ fetchRateCh: make(chan chan uint64),
+ submitRateCh: make(chan *hashrate),
+ requestExit: make(chan struct{}),
+ exitCh: make(chan struct{}),
}
+ go s.loop()
+ return s
+}
+
+func (s *remoteSealer) loop() {
+ defer func() {
+ s.ethash.config.Log.Trace("Ethash remote sealer is exiting")
+ s.cancelNotify()
+ s.reqWG.Wait()
+ close(s.exitCh)
+ }()
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
- case work := <-ethash.workCh:
+ case work := <-s.workCh:
// Update current work with new received block.
// Note same work can be past twice, happens when changing CPU threads.
- results = work.results
+ s.results = work.results
+ s.makeWork(work.block)
+ s.notifyWork()
- makeWork(work.block)
-
- // Notify and requested URLs of the new work availability
- notifyWork()
-
- case work := <-ethash.fetchWorkCh:
+ case work := <-s.fetchWorkCh:
// Return current mining work to remote miner.
- if currentBlock == nil {
+ if s.currentBlock == nil {
work.errc <- errNoMiningWork
} else {
- work.res <- currentWork
+ work.res <- s.currentWork
}
- case result := <-ethash.submitWorkCh:
+ case result := <-s.submitWorkCh:
// Verify submitted PoW solution based on maintained mining blocks.
- if submitWork(result.nonce, result.mixDigest, result.hash) {
+ if s.submitWork(result.nonce, result.mixDigest, result.hash) {
result.errc <- nil
} else {
result.errc <- errInvalidSealResult
}
- case result := <-ethash.submitRateCh:
+ case result := <-s.submitRateCh:
// Trace remote sealer's hash rate by submitted value.
- rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
+ s.rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
close(result.done)
- case req := <-ethash.fetchRateCh:
+ case req := <-s.fetchRateCh:
// Gather all hash rate submitted by remote sealer.
var total uint64
- for _, rate := range rates {
+ for _, rate := range s.rates {
// this could overflow
total += rate.rate
}
@@ -347,25 +315,126 @@ func (ethash *Ethash) remote(notify []string, noverify bool) {
case <-ticker.C:
// Clear stale submitted hash rate.
- for id, rate := range rates {
+ for id, rate := range s.rates {
if time.Since(rate.ping) > 10*time.Second {
- delete(rates, id)
+ delete(s.rates, id)
}
}
// Clear stale pending blocks
- if currentBlock != nil {
- for hash, block := range works {
- if block.NumberU64()+staleThreshold <= currentBlock.NumberU64() {
- delete(works, hash)
+ if s.currentBlock != nil {
+ for hash, block := range s.works {
+ if block.NumberU64()+staleThreshold <= s.currentBlock.NumberU64() {
+ delete(s.works, hash)
}
}
}
- case errc := <-ethash.exitCh:
- // Exit remote loop if ethash is closed and return relevant error.
- errc <- nil
- log.Trace("Ethash remote sealer is exiting")
+ case <-s.requestExit:
return
}
}
}
+
+// makeWork creates 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[1], 32 bytes hex encoded seed hash used for DAG
+// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
+// result[3], hex encoded block number
+func (s *remoteSealer) makeWork(block *types.Block) {
+ hash := s.ethash.SealHash(block.Header())
+ s.currentWork[0] = hash.Hex()
+ s.currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
+ s.currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
+ s.currentWork[3] = hexutil.EncodeBig(block.Number())
+
+ // Trace the seal work fetched by remote sealer.
+ s.currentBlock = block
+ s.works[hash] = block
+}
+
+// notifyWork notifies all the specified mining endpoints of the availability of
+// new work to be processed.
+func (s *remoteSealer) notifyWork() {
+ work := s.currentWork
+ blob, _ := json.Marshal(work)
+ s.reqWG.Add(len(s.notifyURLs))
+ for _, url := range s.notifyURLs {
+ go s.sendNotification(s.notifyCtx, url, blob, work)
+ }
+}
+
+func (s *remoteSealer) sendNotification(ctx context.Context, url string, json []byte, work [4]string) {
+ defer s.reqWG.Done()
+
+ req, err := http.NewRequest("POST", url, bytes.NewReader(json))
+ if err != nil {
+ s.ethash.config.Log.Warn("Can't create remote miner notification", "err", err)
+ return
+ }
+ ctx, cancel := context.WithTimeout(ctx, remoteSealerTimeout)
+ defer cancel()
+ req = req.WithContext(ctx)
+ req.Header.Set("Content-Type", "application/json")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ s.ethash.config.Log.Warn("Failed to notify remote miner", "err", err)
+ } else {
+ s.ethash.config.Log.Trace("Notified remote miner", "miner", url, "hash", work[0], "target", work[2])
+ resp.Body.Close()
+ }
+}
+
+// submitWork verifies the submitted pow solution, returning
+// whether the solution was accepted or not (not can be both a bad pow as well as
+// any other error, like no pending work or stale mining result).
+func (s *remoteSealer) submitWork(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
+ if s.currentBlock == nil {
+ s.ethash.config.Log.Error("Pending work without block", "sealhash", sealhash)
+ return false
+ }
+ // Make sure the work submitted is present
+ block := s.works[sealhash]
+ if block == nil {
+ s.ethash.config.Log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", s.currentBlock.NumberU64())
+ return false
+ }
+ // Verify the correctness of submitted result.
+ header := block.Header()
+ header.Nonce = nonce
+ header.MixDigest = mixDigest
+
+ start := time.Now()
+ if !s.noverify {
+ if err := s.ethash.verifySeal(nil, header, true); err != nil {
+ s.ethash.config.Log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
+ return false
+ }
+ }
+ // Make sure the result channel is assigned.
+ if s.results == nil {
+ s.ethash.config.Log.Warn("Ethash result channel is empty, submitted mining result is rejected")
+ return false
+ }
+ s.ethash.config.Log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
+
+ // Solutions seems to be valid, return to the miner and notify acceptance.
+ solution := block.WithSeal(header)
+
+ // The submitted solution is within the scope of acceptance.
+ if solution.NumberU64()+staleThreshold > s.currentBlock.NumberU64() {
+ select {
+ case s.results <- solution:
+ s.ethash.config.Log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
+ return true
+ default:
+ s.ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
+ return false
+ }
+ }
+ // The submitted block is too old to accept, drop it.
+ s.ethash.config.Log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
+ return false
+}
diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go
index 8ce79a2..4c80a59 100644
--- a/consensus/misc/dao.go
+++ b/consensus/misc/dao.go
@@ -27,7 +27,7 @@ import (
)
var (
- // ErrBadProDAOExtra is returned if a header doens't support the DAO fork on a
+ // ErrBadProDAOExtra is returned if a header doesn't support the DAO fork on a
// pro-fork client.
ErrBadProDAOExtra = errors.New("bad DAO pro-fork extra-data")
diff --git a/consensus/misc/forks.go b/consensus/misc/forks.go
index 06792b0..72b7124 100644
--- a/consensus/misc/forks.go
+++ b/consensus/misc/forks.go
@@ -21,7 +21,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common"
)
// VerifyForkHashes verifies that blocks conforming to network hard-forks do have
8' href='#n1288'>1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914