Merge pull request #1055 from ethersphere/autodag

automatic DAG pregeneration for smooth epoch transitions
This commit is contained in:
Jeffrey Wilcke 2015-05-21 09:01:57 -07:00
commit f1cc3619f5
4 changed files with 127 additions and 1 deletions

View File

@ -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()
} }

View File

@ -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,

View File

@ -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),
} }
} }

View File

@ -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()