From dc62da5c8cbec5997d3c0abf460a95f822f9d006 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Mon, 17 Sep 2018 13:28:10 +0800 Subject: [PATCH] consensus: customized getWork endpoint --- consensus/ethash/api.go | 21 ++++++++++++++++++--- consensus/ethash/ethash.go | 5 +++-- consensus/ethash/ethash_test.go | 26 ++++++++++++++++++++++++++ consensus/ethash/sealer.go | 27 +++++++++++++++++++++++++-- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/consensus/ethash/api.go b/consensus/ethash/api.go index a04ea235d9..03378bab51 100644 --- a/consensus/ethash/api.go +++ b/consensus/ethash/api.go @@ -31,13 +31,16 @@ type API struct { ethash *Ethash // Make sure the mode of ethash is normal. } -// GetWork returns a work package for external miner. +// getWork returns a work package for external miner. +// +// User can specify a customized extra to replace the extra field +// in the block header when the given string is not empty. // // 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 *API) GetWork() ([3]string, error) { +func (api *API) getWork(extra string) ([3]string, error) { if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest { return [3]string{}, errors.New("not supported") } @@ -48,7 +51,7 @@ func (api *API) GetWork() ([3]string, error) { ) select { - case api.ethash.fetchWorkCh <- &sealWork{errc: errc, res: workCh}: + case api.ethash.fetchWorkCh <- &sealWork{extra: extra, errc: errc, res: workCh}: case <-api.ethash.exitCh: return [3]string{}, errEthashStopped } @@ -61,6 +64,18 @@ func (api *API) GetWork() ([3]string, error) { } } +// GetWork returns a default work package for external miner. +// +// Note this function is a legacy for backward compatibility. +func (api *API) GetWork() ([3]string, error) { + return api.getWork("") +} + +// GetCustomizedWork returns a customized work package for external miner. +func (api *API) GetCustomizedWork(extra string) ([3]string, error) { + return api.getWork(extra) +} + // SubmitWork can be used by external miner to submit their POW solution. // It returns an indication if the work was accepted. // Note either an invalid solution, a stale work a non-existent work will return false. diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index b4819ca38b..34e95e158a 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -431,8 +431,9 @@ type hashrate struct { // sealWork wraps a seal work package for remote sealer. type sealWork struct { - errc chan error - res chan [3]string + extra string // The specified string used to fill the block extra field + errc chan error + res chan [3]string } // Ethash is a consensus engine based on proof-of-work implementing the ethash diff --git a/consensus/ethash/ethash_test.go b/consensus/ethash/ethash_test.go index 8eded2ca81..29d0d5853a 100644 --- a/consensus/ethash/ethash_test.go +++ b/consensus/ethash/ethash_test.go @@ -117,6 +117,7 @@ func TestRemoteSealer(t *testing.T) { if res := api.SubmitWork(types.BlockNonce{}, sealhash, common.Hash{}); res { t.Error("expect to return false when submit a fake solution") } + // Push new block with same block number to replace the original one. header = &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(1000)} block = types.NewBlockWithHeader(header) @@ -128,6 +129,31 @@ func TestRemoteSealer(t *testing.T) { } } +func TestCustomizedWork(t *testing.T) { + ethash := NewTester(nil, true) + defer ethash.Close() + + api := &API{ethash} + header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} + block := types.NewBlockWithHeader(header) + + // Push new work. + results := make(chan *types.Block, 1) + ethash.Seal(nil, block, results, nil) + + // Get customized mining work. + expect := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100), Extra: []byte("extra")} + expectSealhash := ethash.SealHash(expect) + if work, err := api.GetCustomizedWork("extra"); err != nil || work[0] != expectSealhash.Hex() { + t.Errorf("expect to return a customized work, have %s, want %s", work[0], expectSealhash.Hex()) + } + + // Submit fake solution for customized work + if !api.SubmitWork(types.BlockNonce{}, expectSealhash, common.Hash{}) { + t.Error("submit solution for customized work failed") + } +} + func TestHashRate(t *testing.T) { var ( hashrate = []hexutil.Uint64{100, 200, 300} diff --git a/consensus/ethash/sealer.go b/consensus/ethash/sealer.go index 06c98a7811..9f856c8843 100644 --- a/consensus/ethash/sealer.go +++ b/consensus/ethash/sealer.go @@ -245,6 +245,24 @@ func (ethash *Ethash) remote(notify []string, noverify bool) { currentBlock = block works[hash] = block } + // customizeWork creates a work package for external miner + // with customized extra data. + customizeWork := func(extra string) [3]string { + newHeader := currentBlock.Header() + newHeader.Extra = []byte(extra) + newBlock := types.NewBlockWithHeader(newHeader) + + // Recalculate mining work + var work [3]string + hash := ethash.SealHash(newHeader) + work[0] = hash.Hex() + work[1] = currentWork[1] + work[2] = currentWork[2] + + // Trace the seal work fetched by remote sealer. + works[hash] = newBlock + return work + } // 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). @@ -313,11 +331,16 @@ func (ethash *Ethash) remote(notify []string, noverify bool) { notifyWork() case work := <-ethash.fetchWorkCh: - // Return current mining work to remote miner. if currentBlock == nil { + // Return error message if there is no available mining work yet. work.errc <- errNoMiningWork - } else { + } else if work.extra == "" { + // Return the default current mining work if no extra customized data + // specified. work.res <- currentWork + } else { + // Return the customized mining work. + work.res <- customizeWork(work.extra) } case result := <-ethash.submitWorkCh: