consensus/ethash: expose submitHashrate api

This commit is contained in:
rjl493456442 2018-01-12 17:54:34 +08:00
parent 9833ccd3f6
commit d2d3b84fa7
9 changed files with 179 additions and 308 deletions

View file

@ -1,4 +1,4 @@
// Copyright 2017 The go-ethereum Authors // Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
@ -16,7 +16,10 @@
package ethash package ethash
import ( import (
"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/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
@ -35,13 +38,12 @@ func (s *API) GetWork() ([3]string, error) {
errCh = make(chan error) errCh = make(chan error)
) )
op := &remoteOp{ op := &remoteOp{
typ: getWork, typ: opGetWork,
workCh: workCh, workCh: workCh,
errCh: errCh, errCh: errCh,
} }
s.ethash.remoteOp <- op s.ethash.remoteOp <- op
err := <-errCh if err := <-errCh; err == nil {
if err == nil {
return <-workCh, nil return <-workCh, nil
} else { } else {
return [3]string{}, err return [3]string{}, err
@ -52,14 +54,13 @@ func (s *API) GetWork() ([3]string, error) {
// accepted. Note, this is not an indication if the provided work was valid! // accepted. Note, this is not an indication if the provided work was valid!
func (s *API) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool { func (s *API) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool {
var errCh = make(chan error) var errCh = make(chan error)
result := sealResult{ op := &remoteOp{
typ: opSubmitWork,
result: mineResult{
nonce: nonce, nonce: nonce,
mixDigest: digest, mixDigest: digest,
hash: solution, hash: solution,
} },
op := &remoteOp{
typ: submitWork,
result: result,
errCh: errCh, errCh: errCh,
} }
s.ethash.remoteOp <- op s.ethash.remoteOp <- op
@ -69,3 +70,21 @@ func (s *API) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) b
return false return false
} }
} }
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
// must be unique between nodes.
func (s *API) SubmitHashRate(r hexutil.Uint64, id common.Hash) bool {
submit := func(rate map[common.Hash]hashrate) {
rate[id] = hashrate{rate: uint64(r), ping: time.Now()}
}
errCh := make(chan error)
op := &remoteOp{
typ: opHashrate,
rateOp: submit,
errCh: errCh,
}
s.ethash.remoteOp <- op
<-errCh
return true
}

View file

@ -395,23 +395,34 @@ type Config struct {
type remoteOpType uint type remoteOpType uint
const ( const (
getWork remoteOpType = iota opGetWork remoteOpType = iota
submitWork opSubmitWork
opHashrate
) )
// sealResult wraps the pow solution parameters for the specified block. // mineResult wraps the pow solution parameters for the specified block.
type sealResult struct { type mineResult struct {
nonce types.BlockNonce nonce types.BlockNonce
mixDigest common.Hash mixDigest common.Hash
hash common.Hash hash common.Hash
} }
type hashrate struct {
id common.Hash
ping time.Time
rate uint64
}
type hashrateOp func(map[common.Hash]hashrate)
// remoteOp wraps a mining operation sent by remote miner. // remoteOp wraps a mining operation sent by remote miner.
type remoteOp struct { type remoteOp struct {
typ remoteOpType typ remoteOpType
result sealResult result mineResult
errCh chan error errCh chan error
workCh chan [3]string workCh 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
@ -434,7 +445,6 @@ type Ethash struct {
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 remoteOp chan *remoteOp // Channel used to receive all mining operations from api
// 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
@ -474,7 +484,20 @@ func New(config Config) *Ethash {
// NewTester creates a small sized ethash PoW scheme useful only for testing // NewTester creates a small sized ethash PoW scheme useful only for testing
// purposes. // purposes.
func NewTester() *Ethash { func NewTester() *Ethash {
return New(Config{CachesInMem: 1, PowMode: ModeTest}) ethash := &Ethash{
config: Config{PowMode: ModeTest},
caches: newlru("cache", 1, newCache),
datasets: newlru("dataset", 1, newDataset),
update: make(chan struct{}),
hashrate: metrics.NewMeter(),
exitCh: make(chan struct{}),
workCh: make(chan *types.Block),
resultCh: make(chan *types.Block),
remoteOp: make(chan *remoteOp),
}
go ethash.remote(ethash.resultCh)
runtime.SetFinalizer(ethash, (*Ethash).close)
return ethash
} }
// NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts // NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts
@ -606,14 +629,36 @@ func (ethash *Ethash) SetThreads(threads int) {
// Hashrate implements PoW, returning the measured rate of the search invocations // Hashrate implements PoW, returning the measured rate of the search invocations
// per second over the last minute. // per second over the last minute.
// Note the returned hashrate includes local hashrate, but also includes the total
// hashrate of all remote miner.
func (ethash *Ethash) Hashrate() float64 { func (ethash *Ethash) Hashrate() float64 {
return ethash.hashrate.Rate1() var (
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
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{{ return []rpc.API{{
Namespace: "ethash", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: &API{ethash}, Service: &API{ethash},
Public: true, Public: true,

View file

@ -24,6 +24,8 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
@ -77,3 +79,47 @@ func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) {
e.VerifySeal(nil, head) e.VerifySeal(nil, head)
} }
} }
func TestRemoteSealer(t *testing.T) {
ethash := NewTester()
api := &API{ethash}
if _, err := api.GetWork(); err != errNoMineWork {
t.Error("expected to return an error indicate there is no mining work")
}
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(head)
// Push new work
ethash.Seal(nil, block, nil)
var (
work [3]string
err error
)
if work, err = api.GetWork(); err != nil || work[0] != block.HashNoNonce().Hex() {
t.Error("expected to return a mining work has same hash")
}
if res := api.SubmitWork(types.BlockNonce{}, block.HashNoNonce(), common.Hash{}); res {
t.Error("expected to return false when submit a fake solution")
}
}
func TestHashRate(t *testing.T) {
var (
ethash = NewTester()
api = &API{ethash}
hashrate = []hexutil.Uint64{100, 200, 300}
expect uint64
ids = []common.Hash{common.HexToHash("a"), common.HexToHash("b"), common.HexToHash("c")}
)
for i := 0; i < len(hashrate); i += 1 {
if res := api.SubmitHashRate(hashrate[i], ids[i]); !res {
t.Error("remote miner submit hashrate failed")
}
expect += uint64(hashrate[i])
}
if tot := ethash.Hashrate(); tot != float64(expect) {
t.Error("expect total hashrate should be same")
}
}

View file

@ -18,13 +18,14 @@ package ethash
import ( import (
crand "crypto/rand" crand "crypto/rand"
"errors"
"math" "math"
"math/big" "math/big"
"math/rand" "math/rand"
"runtime" "runtime"
"sync" "sync"
"time"
"errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -32,9 +33,9 @@ import (
) )
var ( var (
errNoSealWork = errors.New("No work available yet, don't panic.") errNoMineWork = errors.New("No mining work available yet, don't panic.")
errInvalidSealResult = errors.New("Invalid proof-of-work solution.") errInvalidSealResult = errors.New("Invalid or stale proof-of-work solution.")
errNonDefinedRemoteOp = errors.New("Non-defined remote mining operation.") 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
@ -72,15 +73,6 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
threads = 0 // Allows disabling local mining without extra logic around local/remote threads = 0 // Allows disabling local mining without extra logic around local/remote
} }
// Clear up result channel before new round mining start
for empty := false; !empty; {
select {
case <-ethash.resultCh:
default:
empty = true
}
}
// Push new work to remote sealer // Push new work to remote sealer
ethash.workCh <- block ethash.workCh <- block
@ -175,17 +167,18 @@ search:
func (ethash *Ethash) remote(resultCh chan *types.Block) { func (ethash *Ethash) remote(resultCh chan *types.Block) {
var ( var (
works = make(map[common.Hash]*types.Block) works = make(map[common.Hash]*types.Block)
hashrate = make(map[common.Hash]hashrate)
currentWork *types.Block currentWork *types.Block
) )
// makeWork returns a work package for external sealer. 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
makeWork := 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, errNoSealWork return res, errNoMineWork
} }
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()
@ -199,10 +192,10 @@ func (ethash *Ethash) remote(resultCh chan *types.Block) {
return res, nil return res, nil
} }
// verifyWork 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 work pending).
verifyWork := func(result sealResult) bool { submitWork := func(result mineResult) bool {
// Make sure the work submitted is present // Make sure the work submitted is present
block := works[result.hash] block := works[result.hash]
if block == nil { if block == nil {
@ -220,42 +213,65 @@ func (ethash *Ethash) remote(resultCh chan *types.Block) {
} }
// Solutions seems to be valid, return to the miner and notify acceptance // Solutions seems to be valid, return to the miner and notify acceptance
resultCh <- block.WithSeal(header) select {
case resultCh <- block.WithSeal(header):
delete(works, result.hash) delete(works, result.hash)
return true return true
default:
log.Info("Work submitted is stale", "hash", result.hash)
return false
} }
}
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
running: running:
for { for {
select { select {
case block := <-ethash.workCh: case block := <-ethash.workCh:
if block.ParentHash() != currentWork.ParentHash() { if currentWork != nil && block.ParentHash() != currentWork.ParentHash() {
// 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.
currentWork = block currentWork = block
case op := <-ethash.remoteOp: case op := <-ethash.remoteOp:
switch op.typ { switch op.typ {
case getWork: case opGetWork:
// Push current mining work to return channel // Return current mining work to remote miner
work, err := makeWork() work, err := getWork()
if err != nil { if err != nil {
op.errCh <- err op.errCh <- err
} else { } else {
op.workCh <- work
close(op.errCh) close(op.errCh)
op.workCh <- work
} }
case submitWork: case opSubmitWork:
// Verify submitted PoW solution based on maintained mining blocks // Verify submitted PoW solution based on maintained mining blocks
if verifyWork(op.result) { if submitWork(op.result) {
close(op.errCh) close(op.errCh)
} else { } else {
op.errCh <- errInvalidSealResult op.errCh <- errInvalidSealResult
} }
case opHashrate:
// This case is used by remote miner hashrate operation
if op.rateOp != nil {
op.rateOp(hashrate)
close(op.errCh)
}
default: default:
op.errCh <- errNonDefinedRemoteOp op.errCh <- errUndefinedRemoteOp
}
case <-ticker.C:
// Clear stale submited hashrate
for id, rate := range hashrate {
if time.Since(rate.ping) > 10*time.Second {
delete(hashrate, id)
}
} }
case <-ethash.exitCh: case <-ethash.exitCh:

View file

@ -34,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
@ -71,15 +70,11 @@ func (api *PublicEthereumAPI) Hashrate() hexutil.Uint64 {
// It offers only methods that operate on data that pose no security risk when it is publicly accessible. // It offers only methods that operate on data that pose no security risk when it is publicly accessible.
type PublicMinerAPI struct { type PublicMinerAPI struct {
e *Ethereum e *Ethereum
agent *miner.RemoteAgent
} }
// NewPublicMinerAPI create a new PublicMinerAPI instance. // NewPublicMinerAPI create a new PublicMinerAPI instance.
func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI { func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI {
agent := miner.NewRemoteAgent(e.BlockChain(), e.Engine()) return &PublicMinerAPI{e}
e.Miner().Register(agent)
return &PublicMinerAPI{e, agent}
} }
// Mining returns an indication if this node is currently mining. // Mining returns an indication if this node is currently mining.
@ -87,37 +82,6 @@ func (api *PublicMinerAPI) Mining() bool {
return api.e.IsMining() return api.e.IsMining()
} }
// SubmitWork can be used by external miner to submit their POW solution. It returns an indication if the work was
// accepted. Note, this is not an indication if the provided work was valid!
func (api *PublicMinerAPI) SubmitWork(nonce types.BlockNonce, solution, digest common.Hash) bool {
return api.agent.SubmitWork(nonce, digest, solution)
}
// 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[1], 32 bytes hex encoded seed hash used for DAG
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
func (api *PublicMinerAPI) GetWork() ([3]string, error) {
if !api.e.IsMining() {
if err := api.e.StartMining(false); err != nil {
return [3]string{}, err
}
}
work, err := api.agent.GetWork()
if err != nil {
return work, fmt.Errorf("mining not ready: %v", err)
}
return work, nil
}
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
// hash rate of all miners which submit work through this node. It accepts the miner hash rate and an identifier which
// must be unique between nodes.
func (api *PublicMinerAPI) SubmitHashrate(hashrate hexutil.Uint64, id common.Hash) bool {
api.agent.SubmitHashrate(id, uint64(hashrate))
return true
}
// PrivateMinerAPI provides private RPC methods to control the miner. // PrivateMinerAPI provides private RPC methods to control the miner.
// These methods can be abused by external users and must be considered insecure for use by untrusted users. // These methods can be abused by external users and must be considered insecure for use by untrusted users.
type PrivateMinerAPI struct { type PrivateMinerAPI struct {

View file

@ -18,7 +18,6 @@ package miner
import ( import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
@ -110,10 +109,3 @@ func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
self.returnCh <- nil self.returnCh <- nil
} }
} }
func (self *CpuAgent) GetHashRate() int64 {
if pow, ok := self.engine.(consensus.PoW); ok {
return int64(pow.Hashrate())
}
return 0
}

View file

@ -143,14 +143,6 @@ func (self *Miner) HashRate() (tot int64) {
if pow, ok := self.engine.(consensus.PoW); ok { if pow, ok := self.engine.(consensus.PoW); ok {
tot += int64(pow.Hashrate()) tot += int64(pow.Hashrate())
} }
// do we care this might race? is it worth we're rewriting some
// aspects of the worker/locking up agents so we can get an accurate
// hashrate?
for agent := range self.worker.agents {
if _, ok := agent.(*CpuAgent); !ok {
tot += agent.GetHashRate()
}
}
return return
} }

View file

@ -1,202 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// 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/>.
package miner
import (
"errors"
"math/big"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
type hashrate struct {
ping time.Time
rate uint64
}
type RemoteAgent struct {
mu sync.Mutex
quitCh chan struct{}
workCh chan *Work
returnCh chan<- *Result
chain consensus.ChainReader
engine consensus.Engine
currentWork *Work
work map[common.Hash]*Work
hashrateMu sync.RWMutex
hashrate map[common.Hash]hashrate
running int32 // running indicates whether the agent is active. Call atomically
}
func NewRemoteAgent(chain consensus.ChainReader, engine consensus.Engine) *RemoteAgent {
return &RemoteAgent{
chain: chain,
engine: engine,
work: make(map[common.Hash]*Work),
hashrate: make(map[common.Hash]hashrate),
}
}
func (a *RemoteAgent) SubmitHashrate(id common.Hash, rate uint64) {
a.hashrateMu.Lock()
defer a.hashrateMu.Unlock()
a.hashrate[id] = hashrate{time.Now(), rate}
}
func (a *RemoteAgent) Work() chan<- *Work {
return a.workCh
}
func (a *RemoteAgent) SetReturnCh(returnCh chan<- *Result) {
a.returnCh = returnCh
}
func (a *RemoteAgent) Start() {
if !atomic.CompareAndSwapInt32(&a.running, 0, 1) {
return
}
a.quitCh = make(chan struct{})
a.workCh = make(chan *Work, 1)
go a.loop(a.workCh, a.quitCh)
}
func (a *RemoteAgent) Stop() {
if !atomic.CompareAndSwapInt32(&a.running, 1, 0) {
return
}
close(a.quitCh)
close(a.workCh)
}
// GetHashRate returns the accumulated hashrate of all identifier combined
func (a *RemoteAgent) GetHashRate() (tot int64) {
a.hashrateMu.RLock()
defer a.hashrateMu.RUnlock()
// this could overflow
for _, hashrate := range a.hashrate {
tot += int64(hashrate.rate)
}
return
}
func (a *RemoteAgent) GetWork() ([3]string, error) {
a.mu.Lock()
defer a.mu.Unlock()
var res [3]string
if a.currentWork != nil {
block := a.currentWork.Block
res[0] = block.HashNoNonce().Hex()
seedHash := ethash.SeedHash(block.NumberU64())
res[1] = common.BytesToHash(seedHash).Hex()
// Calculate the "target" to be returned to the external miner
n := big.NewInt(1)
n.Lsh(n, 255)
n.Div(n, block.Difficulty())
n.Lsh(n, 1)
res[2] = common.BytesToHash(n.Bytes()).Hex()
a.work[block.HashNoNonce()] = a.currentWork
return res, nil
}
return res, errors.New("No work available yet, don't panic.")
}
// SubmitWork tries to inject a pow solution into the remote agent, returning
// whether the solution was accepted or not (not can be both a bad pow as well as
// any other error, like no work pending).
func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool {
a.mu.Lock()
defer a.mu.Unlock()
// Make sure the work submitted is present
work := a.work[hash]
if work == nil {
log.Info("Work submitted but none pending", "hash", hash)
return false
}
// Make sure the Engine solutions is indeed valid
result := work.Block.Header()
result.Nonce = nonce
result.MixDigest = mixDigest
if err := a.engine.VerifySeal(a.chain, result); err != nil {
log.Warn("Invalid proof-of-work submitted", "hash", hash, "err", err)
return false
}
block := work.Block.WithSeal(result)
// Solutions seems to be valid, return to the miner and notify acceptance
a.returnCh <- &Result{work, block}
delete(a.work, hash)
return true
}
// loop monitors mining events on the work and quit channels, updating the internal
// state of the remote miner until a termination is requested.
//
// Note, the reason the work and quit channels are passed as parameters is because
// RemoteAgent.Start() constantly recreates these channels, so the loop code cannot
// assume data stability in these member fields.
func (a *RemoteAgent) loop(workCh chan *Work, quitCh chan struct{}) {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-quitCh:
return
case work := <-workCh:
a.mu.Lock()
a.currentWork = work
a.mu.Unlock()
case <-ticker.C:
// cleanup
a.mu.Lock()
for hash, work := range a.work {
if time.Since(work.createdAt) > 7*(12*time.Second) {
delete(a.work, hash)
}
}
a.mu.Unlock()
a.hashrateMu.Lock()
for id, hashrate := range a.hashrate {
if time.Since(hashrate.ping) > 10*time.Second {
delete(a.hashrate, id)
}
}
a.hashrateMu.Unlock()
}
}
}

View file

@ -57,7 +57,6 @@ type Agent interface {
SetReturnCh(chan<- *Result) SetReturnCh(chan<- *Result)
Stop() Stop()
Start() Start()
GetHashRate() int64
} }
// Work is the workers current environment and holds // Work is the workers current environment and holds