From faabfa8955a28f9d65296dcb0937117acf4ad103 Mon Sep 17 00:00:00 2001 From: Peter Pratscher Date: Thu, 26 Jul 2018 10:27:28 +0200 Subject: [PATCH] cmd/geth, miner: Added HTTP push notifications for new work packages --- cmd/geth/main.go | 1 + cmd/geth/usage.go | 1 + cmd/utils/flags.go | 7 ++ eth/backend.go | 5 ++ eth/config.go | 1 + miner/notification_agent.go | 123 ++++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 miner/notification_agent.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 1c618de352..92c13fce7c 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -122,6 +122,7 @@ var ( utils.GpoBlocksFlag, utils.GpoPercentileFlag, utils.ExtraDataFlag, + utils.NotifyWorkFlag, configFileFlag, } diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 6a12a66cc2..c6d8d98008 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -189,6 +189,7 @@ var AppHelpFlagGroups = []flagGroup{ utils.TargetGasLimitFlag, utils.GasPriceFlag, utils.ExtraDataFlag, + utils.NotifyWorkFlag, }, }, { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fb12213651..fb6e351a0c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -341,6 +341,10 @@ var ( Name: "extradata", Usage: "Block extra data set by the miner (default = client version)", } + NotifyWorkFlag = cli.StringFlag{ + Name: "notifywork", + Usage: "URLs to which work package notifications are pushed. URLS should be a comma-delimited list of HTTP URLs.", + } // Account settings UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", @@ -1100,6 +1104,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if ctx.GlobalIsSet(ExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name)) } + if ctx.GlobalIsSet(NotifyWorkFlag.Name) { + cfg.NotifyWork = ctx.GlobalString(NotifyWorkFlag.Name) + } if ctx.GlobalIsSet(GasPriceFlag.Name) { cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name) } diff --git a/eth/backend.go b/eth/backend.go index a18abdfb56..28d11204f8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -169,6 +169,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine) eth.miner.SetExtra(makeExtraData(config.ExtraData)) + // Set up mining work push notifications if requested + if len(config.NotifyWork) > 0 { + eth.miner.Register(miner.NewNotificationAgent(eth.blockchain, eth.engine, config.NotifyWork)) + } + eth.APIBackend = &EthAPIBackend{eth, nil} gpoParams := config.GPO if gpoParams.Default == nil { diff --git a/eth/config.go b/eth/config.go index 426d2bf1ef..0400ebb2f4 100644 --- a/eth/config.go +++ b/eth/config.go @@ -98,6 +98,7 @@ type Config struct { Etherbase common.Address `toml:",omitempty"` MinerThreads int `toml:",omitempty"` ExtraData []byte `toml:",omitempty"` + NotifyWork string `toml:",omitempty"` GasPrice *big.Int // Ethash options diff --git a/miner/notification_agent.go b/miner/notification_agent.go new file mode 100644 index 0000000000..250106ff7c --- /dev/null +++ b/miner/notification_agent.go @@ -0,0 +1,123 @@ +// 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 . + +package miner + +import ( + "sync" + + "bytes" + "encoding/json" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/log" + "math/big" + "net/http" + "strings" + "time" +) + +// The notification agent pushes new work packages to a preset list of HTTP endpoints +type NotificationAgent struct { + wg sync.WaitGroup + workCh chan *Work + stop chan struct{} + returnCh chan<- *Result + targets []string + client *http.Client + + chain consensus.ChainReader + engine consensus.Engine +} + +func NewNotificationAgent(chain consensus.ChainReader, engine consensus.Engine, targets string) *NotificationAgent { + miner := &NotificationAgent{ + chain: chain, + engine: engine, + stop: make(chan struct{}, 1), + workCh: make(chan *Work, 1), + targets: strings.Split(targets, ","), + client: &http.Client{Timeout: time.Second * 10}, + } + return miner +} + +func (self *NotificationAgent) Work() chan<- *Work { return self.workCh } +func (self *NotificationAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch } + +func (self *NotificationAgent) Stop() { + self.stop <- struct{}{} +done: + // Empty work channel + for { + select { + case <-self.workCh: + default: + break done + } + } +} + +func (self *NotificationAgent) Start() { + go self.update() +} + +func (self *NotificationAgent) update() { +out: + for { + select { + case work := <-self.workCh: + var res [3]string + + block := work.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() + + resJson, err := json.Marshal(res) + if err != nil { + log.Error("Unable to marshal work package into JSON string") + continue + } + + self.wg.Add(len(self.targets)) + for _, target := range self.targets { + func(t string) { + _, err := self.client.Post(t, "application/json", bytes.NewBuffer(resJson)) + if err != nil { + log.Error("Error invoking work notification handler", "handler", t, "err", err) + } + self.wg.Done() + }(target) + } + self.wg.Wait() + case <-self.stop: + break out + } + } +} + +func (self *NotificationAgent) GetHashRate() int64 { + return 0 +}