forked from cerc-io/plugeth
Merge pull request #1055 from ethersphere/autodag
automatic DAG pregeneration for smooth epoch transitions
This commit is contained in:
commit
f1cc3619f5
@ -8,6 +8,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/ethash"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -73,6 +74,9 @@ func (js *jsre) adminBindings() {
|
|||||||
miner.Set("hashrate", js.hashrate)
|
miner.Set("hashrate", js.hashrate)
|
||||||
miner.Set("setExtra", js.setExtra)
|
miner.Set("setExtra", js.setExtra)
|
||||||
miner.Set("setGasPrice", js.setGasPrice)
|
miner.Set("setGasPrice", js.setGasPrice)
|
||||||
|
miner.Set("startAutoDAG", js.startAutoDAG)
|
||||||
|
miner.Set("stopAutoDAG", js.stopAutoDAG)
|
||||||
|
miner.Set("makeDAG", js.makeDAG)
|
||||||
|
|
||||||
admin.Set("debug", struct{}{})
|
admin.Set("debug", struct{}{})
|
||||||
t, _ = admin.Get("debug")
|
t, _ = admin.Get("debug")
|
||||||
@ -278,6 +282,30 @@ func (js *jsre) hashrate(otto.FunctionCall) otto.Value {
|
|||||||
return js.re.ToVal(js.ethereum.Miner().HashRate())
|
return js.re.ToVal(js.ethereum.Miner().HashRate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
|
||||||
|
blockNumber, err := call.Argument(1).ToInteger()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ethash.MakeDAG(uint64(blockNumber), "")
|
||||||
|
if err != nil {
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
return otto.TrueValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (js *jsre) startAutoDAG(otto.FunctionCall) otto.Value {
|
||||||
|
js.ethereum.StartAutoDAG()
|
||||||
|
return otto.TrueValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (js *jsre) stopAutoDAG(otto.FunctionCall) otto.Value {
|
||||||
|
js.ethereum.StopAutoDAG()
|
||||||
|
return otto.TrueValue()
|
||||||
|
}
|
||||||
|
|
||||||
func (js *jsre) backtrace(call otto.FunctionCall) otto.Value {
|
func (js *jsre) backtrace(call otto.FunctionCall) otto.Value {
|
||||||
tracestr, err := call.Argument(0).ToString()
|
tracestr, err := call.Argument(0).ToString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -316,6 +344,9 @@ func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
|
|||||||
threads = int64(js.ethereum.MinerThreads)
|
threads = int64(js.ethereum.MinerThreads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// switch on DAG autogeneration when miner starts
|
||||||
|
js.ethereum.StartAutoDAG()
|
||||||
|
|
||||||
err = js.ethereum.StartMining(int(threads))
|
err = js.ethereum.StartMining(int(threads))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -327,6 +358,7 @@ func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
|
|||||||
|
|
||||||
func (js *jsre) stopMining(call otto.FunctionCall) otto.Value {
|
func (js *jsre) stopMining(call otto.FunctionCall) otto.Value {
|
||||||
js.ethereum.StopMining()
|
js.ethereum.StopMining()
|
||||||
|
js.ethereum.StopAutoDAG()
|
||||||
return otto.TrueValue()
|
return otto.TrueValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ passwordfile as argument containing the wallet password in plaintext.
|
|||||||
Manage accounts lets you create new accounts, list all existing accounts,
|
Manage accounts lets you create new accounts, list all existing accounts,
|
||||||
import a private key into a new account.
|
import a private key into a new account.
|
||||||
|
|
||||||
'account help' shows a list of subcommands or help for one subcommand.
|
' help' shows a list of subcommands or help for one subcommand.
|
||||||
|
|
||||||
It supports interactive mode, when you are prompted for password as well as
|
It supports interactive mode, when you are prompted for password as well as
|
||||||
non-interactive mode where passwords are supplied via a given password file.
|
non-interactive mode where passwords are supplied via a given password file.
|
||||||
@ -257,6 +257,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
|
|||||||
utils.GasPriceFlag,
|
utils.GasPriceFlag,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
|
utils.AutoDAGFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
utils.NatspecEnabledFlag,
|
utils.NatspecEnabledFlag,
|
||||||
utils.NodeKeyFileFlag,
|
utils.NodeKeyFileFlag,
|
||||||
|
@ -112,6 +112,10 @@ var (
|
|||||||
Name: "mine",
|
Name: "mine",
|
||||||
Usage: "Enable mining",
|
Usage: "Enable mining",
|
||||||
}
|
}
|
||||||
|
AutoDAGFlag = cli.BoolFlag{
|
||||||
|
Name: "autodag",
|
||||||
|
Usage: "Enable automatic DAG pregeneration",
|
||||||
|
}
|
||||||
EtherbaseFlag = cli.StringFlag{
|
EtherbaseFlag = cli.StringFlag{
|
||||||
Name: "etherbase",
|
Name: "etherbase",
|
||||||
Usage: "Public address for block mining rewards. By default the address of your primary account is used",
|
Usage: "Public address for block mining rewards. By default the address of your primary account is used",
|
||||||
@ -314,6 +318,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
|
|||||||
BootNodes: ctx.GlobalString(BootnodesFlag.Name),
|
BootNodes: ctx.GlobalString(BootnodesFlag.Name),
|
||||||
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
||||||
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
||||||
|
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,14 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/whisper"
|
"github.com/ethereum/go-ethereum/whisper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
epochLength = 30000
|
||||||
|
ethashRevision = 23
|
||||||
|
|
||||||
|
autoDAGcheckInterval = 10 * time.Hour
|
||||||
|
autoDAGepochHeight = epochLength / 2
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
jsonlogger = logger.NewJsonLogger()
|
jsonlogger = logger.NewJsonLogger()
|
||||||
|
|
||||||
@ -60,6 +68,7 @@ type Config struct {
|
|||||||
LogJSON string
|
LogJSON string
|
||||||
VmDebug bool
|
VmDebug bool
|
||||||
NatSpec bool
|
NatSpec bool
|
||||||
|
AutoDAG bool
|
||||||
|
|
||||||
MaxPeers int
|
MaxPeers int
|
||||||
MaxPendingPeers int
|
MaxPendingPeers int
|
||||||
@ -197,6 +206,8 @@ type Ethereum struct {
|
|||||||
MinerThreads int
|
MinerThreads int
|
||||||
NatSpec bool
|
NatSpec bool
|
||||||
DataDir string
|
DataDir string
|
||||||
|
AutoDAG bool
|
||||||
|
autodagquit chan bool
|
||||||
etherbase common.Address
|
etherbase common.Address
|
||||||
clientVersion string
|
clientVersion string
|
||||||
ethVersionId int
|
ethVersionId int
|
||||||
@ -269,6 +280,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
NatSpec: config.NatSpec,
|
NatSpec: config.NatSpec,
|
||||||
MinerThreads: config.MinerThreads,
|
MinerThreads: config.MinerThreads,
|
||||||
SolcPath: config.SolcPath,
|
SolcPath: config.SolcPath,
|
||||||
|
AutoDAG: config.AutoDAG,
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.pow = ethash.New()
|
eth.pow = ethash.New()
|
||||||
@ -448,6 +460,10 @@ func (s *Ethereum) Start() error {
|
|||||||
// periodically flush databases
|
// periodically flush databases
|
||||||
go s.syncDatabases()
|
go s.syncDatabases()
|
||||||
|
|
||||||
|
if s.AutoDAG {
|
||||||
|
s.StartAutoDAG()
|
||||||
|
}
|
||||||
|
|
||||||
// Start services
|
// Start services
|
||||||
go s.txPool.Start()
|
go s.txPool.Start()
|
||||||
s.protocolManager.Start()
|
s.protocolManager.Start()
|
||||||
@ -526,6 +542,7 @@ func (s *Ethereum) Stop() {
|
|||||||
if s.whisper != nil {
|
if s.whisper != nil {
|
||||||
s.whisper.Stop()
|
s.whisper.Stop()
|
||||||
}
|
}
|
||||||
|
s.StopAutoDAG()
|
||||||
|
|
||||||
glog.V(logger.Info).Infoln("Server stopped")
|
glog.V(logger.Info).Infoln("Server stopped")
|
||||||
close(s.shutdownChan)
|
close(s.shutdownChan)
|
||||||
@ -559,6 +576,77 @@ func (self *Ethereum) syncAccounts(tx *types.Transaction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartAutoDAG() spawns a go routine that checks the DAG every autoDAGcheckInterval
|
||||||
|
// by default that is 10 times per epoch
|
||||||
|
// in epoch n, if we past autoDAGepochHeight within-epoch blocks,
|
||||||
|
// it calls ethash.MakeDAG to pregenerate the DAG for the next epoch n+1
|
||||||
|
// if it does not exist yet as well as remove the DAG for epoch n-1
|
||||||
|
// the loop quits if autodagquit channel is closed, it can safely restart and
|
||||||
|
// stop any number of times.
|
||||||
|
// For any more sophisticated pattern of DAG generation, use CLI subcommand
|
||||||
|
// makedag
|
||||||
|
func (self *Ethereum) StartAutoDAG() {
|
||||||
|
if self.autodagquit != nil {
|
||||||
|
return // already started
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG ON (ethash dir: %s)", ethash.DefaultDir)
|
||||||
|
var nextEpoch uint64
|
||||||
|
timer := time.After(0)
|
||||||
|
self.autodagquit = make(chan bool)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer:
|
||||||
|
glog.V(logger.Info).Infof("checking DAG (ethash dir: %s)", ethash.DefaultDir)
|
||||||
|
currentBlock := self.ChainManager().CurrentBlock().NumberU64()
|
||||||
|
thisEpoch := currentBlock / epochLength
|
||||||
|
if nextEpoch <= thisEpoch {
|
||||||
|
if currentBlock%epochLength > autoDAGepochHeight {
|
||||||
|
if thisEpoch > 0 {
|
||||||
|
previousDag, previousDagFull := dagFiles(thisEpoch - 1)
|
||||||
|
os.Remove(filepath.Join(ethash.DefaultDir, previousDag))
|
||||||
|
os.Remove(filepath.Join(ethash.DefaultDir, previousDagFull))
|
||||||
|
glog.V(logger.Info).Infof("removed DAG for epoch %d (%s)", thisEpoch-1, previousDag)
|
||||||
|
}
|
||||||
|
nextEpoch = thisEpoch + 1
|
||||||
|
dag, _ := dagFiles(nextEpoch)
|
||||||
|
if _, err := os.Stat(dag); os.IsNotExist(err) {
|
||||||
|
glog.V(logger.Info).Infof("Pregenerating DAG for epoch %d (%s)", nextEpoch, dag)
|
||||||
|
err := ethash.MakeDAG(nextEpoch*epochLength, "") // "" -> ethash.DefaultDir
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Error).Infof("Error generating DAG for epoch %d (%s)", nextEpoch, dag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glog.V(logger.Error).Infof("DAG for epoch %d (%s)", nextEpoch, dag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer = time.After(autoDAGcheckInterval)
|
||||||
|
case <-self.autodagquit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
|
||||||
|
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||||
|
func dagFiles(epoch uint64) (string, string) {
|
||||||
|
seedHash, _ := ethash.GetSeedHash(epoch * epochLength)
|
||||||
|
dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8])
|
||||||
|
return dag, "full-R" + dag
|
||||||
|
}
|
||||||
|
|
||||||
|
// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
|
||||||
|
func (self *Ethereum) StopAutoDAG() {
|
||||||
|
if self.autodagquit != nil {
|
||||||
|
close(self.autodagquit)
|
||||||
|
self.autodagquit = nil
|
||||||
|
}
|
||||||
|
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
|
||||||
|
}
|
||||||
|
|
||||||
func saveProtocolVersion(db common.Database, protov int) {
|
func saveProtocolVersion(db common.Database, protov int) {
|
||||||
d, _ := db.Get([]byte("ProtocolVersion"))
|
d, _ := db.Get([]byte("ProtocolVersion"))
|
||||||
protocolVersion := common.NewValue(d).Uint()
|
protocolVersion := common.NewValue(d).Uint()
|
||||||
|
Loading…
Reference in New Issue
Block a user