[NOD-820] When the node isn't synced, make getBlockTemplate return a boolean isSynced instead of an error (#716)

* [NOD-820] Add IsSynced to GetBlockTemplateResult.

* [NOD-820] Add isSynced to the help file.

* [NOD-820] Add MineWhenNotSynced to the kaspaminer config.

* [NOD-820] Implement miner MineWhenNotSynced logic.

* [NOD-820] Fixed capitalization in an error message.
This commit is contained in:
stasatdaglabs 2020-05-12 15:08:24 +03:00 committed by GitHub
parent 585510d76c
commit 806eab817c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 27 deletions

View File

@ -30,16 +30,17 @@ var (
)
type configFlags struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
DisableTLS bool `long:"notls" description:"Disable TLS"`
Verbose bool `long:"verbose" short:"v" description:"Enable logging of RPC requests"`
NumberOfBlocks uint64 `short:"n" long:"numblocks" description:"Number of blocks to mine. If omitted, will mine until the process is interrupted."`
BlockDelay uint64 `long:"block-delay" description:"Delay for block submission (in milliseconds). This is used only for testing purposes."`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
DisableTLS bool `long:"notls" description:"Disable TLS"`
Verbose bool `long:"verbose" short:"v" description:"Enable logging of RPC requests"`
NumberOfBlocks uint64 `short:"n" long:"numblocks" description:"Number of blocks to mine. If omitted, will mine until the process is interrupted."`
BlockDelay uint64 `long:"block-delay" description:"Delay for block submission (in milliseconds). This is used only for testing purposes."`
MineWhenNotSynced bool `long:"mine-when-not-synced" description:"Mine even if the node is not synced with the rest of the network."`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
config.NetworkFlags
}

View File

@ -45,7 +45,7 @@ func main() {
doneChan := make(chan struct{})
spawn(func() {
err = mineLoop(client, cfg.NumberOfBlocks, cfg.BlockDelay)
err = mineLoop(client, cfg.NumberOfBlocks, cfg.BlockDelay, cfg.MineWhenNotSynced)
if err != nil {
panic(errors.Errorf("Error in mine loop: %s", err))
}

View File

@ -25,7 +25,7 @@ var hashesTried uint64
const logHashRateInterval = 10 * time.Second
func mineLoop(client *minerClient, numberOfBlocks uint64, blockDelay uint64) error {
func mineLoop(client *minerClient, numberOfBlocks uint64, blockDelay uint64, mineWhenNotSynced bool) error {
errChan := make(chan error)
templateStopChan := make(chan struct{})
@ -35,7 +35,7 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, blockDelay uint64) err
wg := sync.WaitGroup{}
for i := uint64(0); numberOfBlocks == 0 || i < numberOfBlocks; i++ {
foundBlock := make(chan *util.Block)
mineNextBlock(client, foundBlock, templateStopChan, errChan)
mineNextBlock(client, foundBlock, mineWhenNotSynced, templateStopChan, errChan)
block := <-foundBlock
templateStopChan <- struct{}{}
wg.Add(1)
@ -80,13 +80,15 @@ func logHashRate() {
})
}
func mineNextBlock(client *minerClient, foundBlock chan *util.Block, templateStopChan chan struct{}, errChan chan error) {
func mineNextBlock(client *minerClient, foundBlock chan *util.Block, mineWhenNotSynced bool,
templateStopChan chan struct{}, errChan chan error) {
newTemplateChan := make(chan *rpcmodel.GetBlockTemplateResult)
spawn(func() {
templatesLoop(client, newTemplateChan, errChan, templateStopChan)
})
spawn(func() {
solveLoop(newTemplateChan, foundBlock, errChan)
solveLoop(newTemplateChan, foundBlock, mineWhenNotSynced, errChan)
})
}
@ -207,12 +209,23 @@ func getBlockTemplate(client *minerClient, longPollID string) (*rpcmodel.GetBloc
return client.GetBlockTemplate([]string{"coinbasetxn"}, longPollID)
}
func solveLoop(newTemplateChan chan *rpcmodel.GetBlockTemplateResult, foundBlock chan *util.Block, errChan chan error) {
func solveLoop(newTemplateChan chan *rpcmodel.GetBlockTemplateResult, foundBlock chan *util.Block,
mineWhenNotSynced bool, errChan chan error) {
var stopOldTemplateSolving chan struct{}
for template := range newTemplateChan {
if stopOldTemplateSolving != nil {
close(stopOldTemplateSolving)
}
if !template.IsSynced {
if !mineWhenNotSynced {
errChan <- errors.Errorf("got template with isSynced=false")
return
}
log.Warnf("Got template with isSynced=false")
}
stopOldTemplateSolving = make(chan struct{})
block, err := parseBlock(template)
if err != nil {

View File

@ -151,6 +151,7 @@ type GetBlockTemplateResult struct {
CoinbaseTxn *GetBlockTemplateResultTx `json:"coinbaseTxn,omitempty"`
CoinbaseValue *uint64 `json:"coinbaseValue,omitempty"`
WorkID string `json:"workId,omitempty"`
IsSynced bool `json:"isSynced"`
// Optional long polling from BIP 0022.
LongPollID string `json:"longPollId,omitempty"`

View File

@ -71,6 +71,7 @@ type gbtWorkState struct {
template *mining.BlockTemplate
notifyMap map[string]map[int64]chan struct{}
timeSource blockdag.TimeSource
isSynced bool
}
// newGbtWorkState returns a new instance of a gbtWorkState with all internal
@ -105,17 +106,6 @@ func handleGetBlockTemplate(s *Server, cmd interface{}, closeChan <-chan struct{
mode = request.Mode
}
// No point in generating templates or processing proposals before
// the DAG is synced. Note that we make a special check for when
// we have nothing besides the genesis block (blueScore == 0),
// because in that state IsCurrent may still return true.
if !isSyncedForMining(s) {
return nil, &rpcmodel.RPCError{
Code: rpcmodel.ErrRPCClientInInitialDownload,
Message: "Kaspa is downloading blocks...",
}
}
switch mode {
case "template":
return handleGetBlockTemplateRequest(s, request, closeChan)
@ -655,6 +645,15 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool)
// consensus rules.
minTimestamp := s.cfg.DAG.NextBlockMinimumTime()
// Check whether this node is synced with the rest of of the
// network. There's almost never a good reason to mine on top
// of an unsynced DAG, and miners are generally expected not to
// mine when isSynced is false.
// This is not a straight-up error because the choice of whether
// to mine or not is the responsibility of the miner rather
// than the node's.
isSynced := isSyncedForMining(s)
// Update work state to ensure another block template isn't
// generated until needed.
state.template = template
@ -662,6 +661,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool)
state.lastTxUpdate = lastTxUpdate
state.tipHashes = tipHashes
state.minTimestamp = minTimestamp
state.isSynced = isSynced
log.Debugf("Generated block template (timestamp %s, "+
"target %s, merkle root %s)",
@ -820,6 +820,7 @@ func (state *gbtWorkState) blockTemplateResult(dag *blockdag.BlockDAG, useCoinba
Mutable: gbtMutableFields,
NonceRange: gbtNonceRange,
Capabilities: gbtCapabilities,
IsSynced: state.isSynced,
}
if useCoinbaseValue {

View File

@ -331,6 +331,7 @@ var helpDescsEnUS = map[string]string{
"getBlockTemplateResult-nonceRange": "Two concatenated hex-encoded big-endian 64-bit integers which represent the valid ranges of nonces the miner may scan",
"getBlockTemplateResult-capabilities": "List of server capabilities including 'proposal' to indicate support for block proposals",
"getBlockTemplateResult-rejectReason": "Reason the proposal was invalid as-is (only applies to proposal responses)",
"getBlockTemplateResult-isSynced": "Whether this node is synced with the rest of of the network. Miners are generally expected not to mine when isSynced is false",
// GetBlockTemplateCmd help.
"getBlockTemplate--synopsis": "Returns a JSON object with information necessary to construct a block to mine or accepts a proposal to validate.\n" +