diff --git a/cmd/kaspaminer/config.go b/cmd/kaspaminer/config.go index 5f87d947e..392eb70ca 100644 --- a/cmd/kaspaminer/config.go +++ b/cmd/kaspaminer/config.go @@ -36,6 +36,7 @@ type configFlags struct { 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"` + MiningAddr string `long:"miningaddr" description:"Address to mine to"` 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."` diff --git a/cmd/kaspaminer/main.go b/cmd/kaspaminer/main.go index 32bf524df..fbd9b2fdd 100644 --- a/cmd/kaspaminer/main.go +++ b/cmd/kaspaminer/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/kaspanet/kaspad/util" "os" "github.com/kaspanet/kaspad/version" @@ -39,15 +40,20 @@ func main() { client, err := connectToServer(cfg) if err != nil { - panic(errors.Wrap(err, "Error connecting to the RPC server")) + panic(errors.Wrap(err, "error connecting to the RPC server")) } defer client.Disconnect() + miningAddr, err := util.DecodeAddress(cfg.MiningAddr, cfg.ActiveNetParams.Prefix) + if err != nil { + panic(errors.Wrap(err, "error decoding mining address")) + } + doneChan := make(chan struct{}) spawn(func() { - err = mineLoop(client, cfg.NumberOfBlocks, cfg.BlockDelay, cfg.MineWhenNotSynced) + err = mineLoop(client, cfg.NumberOfBlocks, cfg.BlockDelay, cfg.MineWhenNotSynced, miningAddr) if err != nil { - panic(errors.Errorf("Error in mine loop: %s", err)) + panic(errors.Wrap(err, "error in mine loop")) } doneChan <- struct{}{} }) diff --git a/cmd/kaspaminer/mineloop.go b/cmd/kaspaminer/mineloop.go index 58a537eeb..fb4819cc6 100644 --- a/cmd/kaspaminer/mineloop.go +++ b/cmd/kaspaminer/mineloop.go @@ -25,7 +25,9 @@ var hashesTried uint64 const logHashRateInterval = 10 * time.Second -func mineLoop(client *minerClient, numberOfBlocks uint64, blockDelay uint64, mineWhenNotSynced bool) error { +func mineLoop(client *minerClient, numberOfBlocks uint64, blockDelay uint64, mineWhenNotSynced bool, + miningAddr util.Address) error { + errChan := make(chan error) templateStopChan := make(chan struct{}) @@ -35,7 +37,7 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, blockDelay uint64, min wg := sync.WaitGroup{} for i := uint64(0); numberOfBlocks == 0 || i < numberOfBlocks; i++ { foundBlock := make(chan *util.Block) - mineNextBlock(client, foundBlock, mineWhenNotSynced, templateStopChan, errChan) + mineNextBlock(client, miningAddr, foundBlock, mineWhenNotSynced, templateStopChan, errChan) block := <-foundBlock templateStopChan <- struct{}{} wg.Add(1) @@ -80,12 +82,12 @@ func logHashRate() { }) } -func mineNextBlock(client *minerClient, foundBlock chan *util.Block, mineWhenNotSynced bool, +func mineNextBlock(client *minerClient, miningAddr util.Address, foundBlock chan *util.Block, mineWhenNotSynced bool, templateStopChan chan struct{}, errChan chan error) { newTemplateChan := make(chan *rpcmodel.GetBlockTemplateResult) spawn(func() { - templatesLoop(client, newTemplateChan, errChan, templateStopChan) + templatesLoop(client, miningAddr, newTemplateChan, errChan, templateStopChan) }) spawn(func() { solveLoop(newTemplateChan, foundBlock, mineWhenNotSynced, errChan) @@ -134,7 +136,7 @@ func parseBlock(template *rpcmodel.GetBlockTemplateResult) (*util.Block, error) wire.NewBlockHeader(template.Version, parentHashes, &daghash.Hash{}, acceptedIDMerkleRoot, utxoCommitment, bits, 0)) - for i, txResult := range append([]rpcmodel.GetBlockTemplateResultTx{*template.CoinbaseTxn}, template.Transactions...) { + for i, txResult := range template.Transactions { reader := hex.NewDecoder(strings.NewReader(txResult.Data)) tx := &wire.MsgTx{} if err := tx.KaspaDecode(reader, 0); err != nil { @@ -169,7 +171,9 @@ func solveBlock(block *util.Block, stopChan chan struct{}, foundBlock chan *util } -func templatesLoop(client *minerClient, newTemplateChan chan *rpcmodel.GetBlockTemplateResult, errChan chan error, stopChan chan struct{}) { +func templatesLoop(client *minerClient, miningAddr util.Address, + newTemplateChan chan *rpcmodel.GetBlockTemplateResult, errChan chan error, stopChan chan struct{}) { + longPollID := "" getBlockTemplateLongPoll := func() { if longPollID != "" { @@ -177,7 +181,7 @@ func templatesLoop(client *minerClient, newTemplateChan chan *rpcmodel.GetBlockT } else { log.Infof("Requesting template without longPollID from %s", client.Host()) } - template, err := getBlockTemplate(client, longPollID) + template, err := getBlockTemplate(client, miningAddr, longPollID) if nativeerrors.Is(err, rpcclient.ErrResponseTimedOut) { log.Infof("Got timeout while requesting template '%s' from %s", longPollID, client.Host()) return @@ -205,8 +209,8 @@ func templatesLoop(client *minerClient, newTemplateChan chan *rpcmodel.GetBlockT } } -func getBlockTemplate(client *minerClient, longPollID string) (*rpcmodel.GetBlockTemplateResult, error) { - return client.GetBlockTemplate([]string{"coinbasetxn"}, longPollID) +func getBlockTemplate(client *minerClient, miningAddr util.Address, longPollID string) (*rpcmodel.GetBlockTemplateResult, error) { + return client.GetBlockTemplate(miningAddr.String(), longPollID) } func solveLoop(newTemplateChan chan *rpcmodel.GetBlockTemplateResult, foundBlock chan *util.Block, diff --git a/config/config.go b/config/config.go index 548a8e161..97794a9ba 100644 --- a/config/config.go +++ b/config/config.go @@ -117,7 +117,6 @@ type Flags struct { Upnp bool `long:"upnp" description:"Use UPnP to map our listening port outside of NAT"` MinRelayTxFee float64 `long:"minrelaytxfee" description:"The minimum transaction fee in KAS/kB to be considered a non-zero fee."` MaxOrphanTxs int `long:"maxorphantx" description:"Max number of orphan transactions to keep in memory"` - MiningAddrs []string `long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set"` BlockMaxMass uint64 `long:"blockmaxmass" description:"Maximum transaction mass to be used when creating a block"` UserAgentComments []string `long:"uacomment" description:"Comment to add to the user agent -- See BIP 14 for more information."` NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"` @@ -607,27 +606,6 @@ func loadConfig() (*Config, []string, error) { return nil, nil, err } - // Check mining addresses are valid and saved parsed versions. - activeConfig.MiningAddrs = make([]util.Address, 0, len(activeConfig.Flags.MiningAddrs)) - for _, strAddr := range activeConfig.Flags.MiningAddrs { - addr, err := util.DecodeAddress(strAddr, activeConfig.NetParams().Prefix) - if err != nil { - str := "%s: mining address '%s' failed to decode: %s" - err := errors.Errorf(str, funcName, strAddr, err) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, nil, err - } - if !addr.IsForPrefix(activeConfig.NetParams().Prefix) { - str := "%s: mining address '%s' is on the wrong network" - err := errors.Errorf(str, funcName, strAddr) - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, nil, err - } - activeConfig.MiningAddrs = append(activeConfig.MiningAddrs, addr) - } - // Add default port to all listener addresses if needed and remove // duplicate addresses. activeConfig.Listeners, err = network.NormalizeAddresses(activeConfig.Listeners, diff --git a/mining/mining.go b/mining/mining.go index e193f261c..cc8aee705 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -79,12 +79,6 @@ type BlockTemplate struct { // Height is the height at which the block template connects to the DAG Height uint64 - - // ValidPayAddress indicates whether or not the template coinbase pays - // to an address or is redeemable by anyone. See the documentation on - // NewBlockTemplate for details on which this can be useful to generate - // templates without a coinbase payment address. - ValidPayAddress bool } // BlkTmplGenerator provides a type that can be used to generate block templates @@ -212,10 +206,9 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address, extraNonc txsForBlockTemplate.totalMass, util.CompactToBig(msgBlock.Header.Bits)) return &BlockTemplate{ - Block: msgBlock, - TxMasses: txsForBlockTemplate.txMasses, - Fees: txsForBlockTemplate.txFees, - ValidPayAddress: payToAddress != nil, + Block: msgBlock, + TxMasses: txsForBlockTemplate.txMasses, + Fees: txsForBlockTemplate.txFees, }, nil } diff --git a/rpcclient/mining.go b/rpcclient/mining.go index f39ba9ef7..a00cd46af 100644 --- a/rpcclient/mining.go +++ b/rpcclient/mining.go @@ -71,11 +71,11 @@ type FutureGetBlockTemplateResult chan *response // the returned instance. // // See GetBlockTemplate for the blocking version and more details -func (c *Client) GetBlockTemplateAsync(capabilities []string, longPollID string) FutureGetBlockTemplateResult { +func (c *Client) GetBlockTemplateAsync(payAddress string, longPollID string) FutureGetBlockTemplateResult { request := &rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: capabilities, - LongPollID: longPollID, + Mode: "template", + LongPollID: longPollID, + PayAddress: payAddress, } cmd := rpcmodel.NewGetBlockTemplateCmd(request) return c.sendCmd(cmd) @@ -97,6 +97,6 @@ func (r FutureGetBlockTemplateResult) Receive() (*rpcmodel.GetBlockTemplateResul } // GetBlockTemplate request a block template from the server, to mine upon -func (c *Client) GetBlockTemplate(capabilities []string, longPollID string) (*rpcmodel.GetBlockTemplateResult, error) { - return c.GetBlockTemplateAsync(capabilities, longPollID).Receive() +func (c *Client) GetBlockTemplate(payAddress string, longPollID string) (*rpcmodel.GetBlockTemplateResult, error) { + return c.GetBlockTemplateAsync(payAddress, longPollID).Receive() } diff --git a/rpcmodel/rpc_commands.go b/rpcmodel/rpc_commands.go index f6dbbecb2..f9003fbcb 100644 --- a/rpcmodel/rpc_commands.go +++ b/rpcmodel/rpc_commands.go @@ -206,8 +206,7 @@ func NewGetBlockHeaderCmd(hash string, verbose *bool) *GetBlockHeaderCmd { // TemplateRequest is a request object as defined in BIP22. It is optionally // provided as an pointer argument to GetBlockTemplateCmd. type TemplateRequest struct { - Mode string `json:"mode,omitempty"` - Capabilities []string `json:"capabilities,omitempty"` + Mode string `json:"mode,omitempty"` // Optional long polling. LongPollID string `json:"longPollId,omitempty"` @@ -225,6 +224,8 @@ type TemplateRequest struct { // "proposal". Data string `json:"data,omitempty"` WorkID string `json:"workId,omitempty"` + + PayAddress string `json:"payAddress"` } // convertTemplateRequestField potentially converts the provided value as diff --git a/rpcmodel/rpc_commands_test.go b/rpcmodel/rpc_commands_test.go index 2283836cc..de6694923 100644 --- a/rpcmodel/rpc_commands_test.go +++ b/rpcmodel/rpc_commands_test.go @@ -256,72 +256,72 @@ func TestRPCServerCommands(t *testing.T) { { name: "getBlockTemplate optional - template request", newCmd: func() (interface{}, error) { - return rpcmodel.NewCommand("getBlockTemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"]}`) + return rpcmodel.NewCommand("getBlockTemplate", `{"mode":"template","payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}`) }, staticCmd: func() interface{} { template := rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: []string{"longpoll", "coinbasetxn"}, + Mode: "template", + PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", } return rpcmodel.NewGetBlockTemplateCmd(&template) }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"]}],"id":1}`, + marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}],"id":1}`, unmarshalled: &rpcmodel.GetBlockTemplateCmd{ Request: &rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: []string{"longpoll", "coinbasetxn"}, + Mode: "template", + PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", }, }, }, { name: "getBlockTemplate optional - template request with tweaks", newCmd: func() (interface{}, error) { - return rpcmodel.NewCommand("getBlockTemplate", `{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":500,"massLimit":100000000,"maxVersion":1}`) + return rpcmodel.NewCommand("getBlockTemplate", `{"mode":"template","sigOpLimit":500,"massLimit":100000000,"maxVersion":1,"payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}`) }, staticCmd: func() interface{} { template := rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: []string{"longPoll", "coinbaseTxn"}, - SigOpLimit: 500, - MassLimit: 100000000, - MaxVersion: 1, + Mode: "template", + PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", + SigOpLimit: 500, + MassLimit: 100000000, + MaxVersion: 1, } return rpcmodel.NewGetBlockTemplateCmd(&template) }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":500,"massLimit":100000000,"maxVersion":1}],"id":1}`, + marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","sigOpLimit":500,"massLimit":100000000,"maxVersion":1,"payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}],"id":1}`, unmarshalled: &rpcmodel.GetBlockTemplateCmd{ Request: &rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: []string{"longPoll", "coinbaseTxn"}, - SigOpLimit: int64(500), - MassLimit: int64(100000000), - MaxVersion: 1, + Mode: "template", + PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", + SigOpLimit: int64(500), + MassLimit: int64(100000000), + MaxVersion: 1, }, }, }, { name: "getBlockTemplate optional - template request with tweaks 2", newCmd: func() (interface{}, error) { - return rpcmodel.NewCommand("getBlockTemplate", `{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":true,"massLimit":100000000,"maxVersion":1}`) + return rpcmodel.NewCommand("getBlockTemplate", `{"mode":"template","payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3","sigOpLimit":true,"massLimit":100000000,"maxVersion":1}`) }, staticCmd: func() interface{} { template := rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: []string{"longPoll", "coinbaseTxn"}, - SigOpLimit: true, - MassLimit: 100000000, - MaxVersion: 1, + Mode: "template", + PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", + SigOpLimit: true, + MassLimit: 100000000, + MaxVersion: 1, } return rpcmodel.NewGetBlockTemplateCmd(&template) }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":true,"massLimit":100000000,"maxVersion":1}],"id":1}`, + marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","sigOpLimit":true,"massLimit":100000000,"maxVersion":1,"payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}],"id":1}`, unmarshalled: &rpcmodel.GetBlockTemplateCmd{ Request: &rpcmodel.TemplateRequest{ - Mode: "template", - Capabilities: []string{"longPoll", "coinbaseTxn"}, - SigOpLimit: true, - MassLimit: int64(100000000), - MaxVersion: 1, + Mode: "template", + PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", + SigOpLimit: true, + MassLimit: int64(100000000), + MaxVersion: 1, }, }, }, diff --git a/rpcmodel/rpc_results.go b/rpcmodel/rpc_results.go index 9b3da13b6..5b3c98d79 100644 --- a/rpcmodel/rpc_results.go +++ b/rpcmodel/rpc_results.go @@ -127,12 +127,6 @@ type GetBlockTemplateResultTx struct { Fee uint64 `json:"fee"` } -// GetBlockTemplateResultAux models the coinbaseaux field of the -// getblocktemplate command. -type GetBlockTemplateResultAux struct { - Flags string `json:"flags"` -} - // GetBlockTemplateResult models the data returned from the getblocktemplate // command. type GetBlockTemplateResult struct { @@ -147,9 +141,6 @@ type GetBlockTemplateResult struct { AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"` UTXOCommitment string `json:"utxoCommitment"` Version int32 `json:"version"` - CoinbaseAux *GetBlockTemplateResultAux `json:"coinbaseAux,omitempty"` - CoinbaseTxn *GetBlockTemplateResultTx `json:"coinbaseTxn,omitempty"` - CoinbaseValue *uint64 `json:"coinbaseValue,omitempty"` WorkID string `json:"workId,omitempty"` IsSynced bool `json:"isSynced"` diff --git a/server/rpc/handle_get_block_template.go b/server/rpc/handle_get_block_template.go index a890ae730..e6a538bd8 100644 --- a/server/rpc/handle_get_block_template.go +++ b/server/rpc/handle_get_block_template.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/hex" "fmt" - "math/rand" "strconv" "strings" "sync" @@ -44,16 +43,6 @@ var ( "time", "transactions/add", "parentblock", "coinbase/append", } - // gbtCoinbaseAux describes additional data that miners should include - // in the coinbase signature script. It is declared here to avoid the - // overhead of creating a new object on every invocation for constant - // data. - gbtCoinbaseAux = &rpcmodel.GetBlockTemplateResultAux{ - Flags: hex.EncodeToString(builderScript(txscript. - NewScriptBuilder(). - AddData([]byte(mining.CoinbaseFlags)))), - } - // gbtCapabilities describes additional capabilities returned with a // block template generated by the getBlockTemplate RPC. It is // declared here to avoid the overhead of creating the slice on every @@ -72,6 +61,7 @@ type gbtWorkState struct { template *mining.BlockTemplate notifyMap map[string]map[int64]chan struct{} timeSource blockdag.TimeSource + payAddress util.Address } // newGbtWorkState returns a new instance of a gbtWorkState with all internal @@ -122,42 +112,8 @@ func handleGetBlockTemplate(s *Server, cmd interface{}, closeChan <-chan struct{ // handleGetBlockTemplateRequest is a helper for handleGetBlockTemplate which // deals with generating and returning block templates to the caller. It // handles both long poll requests as specified by BIP 0022 as well as regular -// requests. In addition, it detects the capabilities reported by the caller -// in regards to whether or not it supports creating its own coinbase (the -// coinbasetxn and coinbasevalue capabilities) and modifies the returned block -// template accordingly. +// requests. func handleGetBlockTemplateRequest(s *Server, request *rpcmodel.TemplateRequest, closeChan <-chan struct{}) (interface{}, error) { - // Extract the relevant passed capabilities and restrict the result to - // either a coinbase value or a coinbase transaction object depending on - // the request. Default to only providing a coinbase value. - useCoinbaseValue := true - if request != nil { - var hasCoinbaseValue, hasCoinbaseTxn bool - for _, capability := range request.Capabilities { - switch capability { - case "coinbasetxn": - hasCoinbaseTxn = true - case "coinbasevalue": - hasCoinbaseValue = true - } - } - - if hasCoinbaseTxn && !hasCoinbaseValue { - useCoinbaseValue = false - } - } - - // When a coinbase transaction has been requested, respond with an error - // if there are no addresses to pay the created block template to. - if !useCoinbaseValue && len(config.ActiveConfig().MiningAddrs) == 0 { - return nil, &rpcmodel.RPCError{ - Code: rpcmodel.ErrRPCInternal.Code, - Message: "A coinbase transaction has been requested, " + - "but the server has not been configured with " + - "any payment addresses via --miningaddr", - } - } - // Return an error if there are no peers connected since there is no // way to relay a found block or receive transactions to work on. // However, allow this state when running in the regression test or @@ -171,12 +127,16 @@ func handleGetBlockTemplateRequest(s *Server, request *rpcmodel.TemplateRequest, } } + payAddr, err := util.DecodeAddress(request.PayAddress, s.cfg.DAGParams.Prefix) + if err != nil { + return nil, err + } + // When a long poll ID was provided, this is a long poll request by the // client to be notified when block template referenced by the ID should // be replaced with a new one. if request != nil && request.LongPollID != "" { - return handleGetBlockTemplateLongPoll(s, request.LongPollID, - useCoinbaseValue, closeChan) + return handleGetBlockTemplateLongPoll(s, request.LongPollID, payAddr, closeChan) } // Protect concurrent access when updating block templates. @@ -190,10 +150,10 @@ func handleGetBlockTemplateRequest(s *Server, request *rpcmodel.TemplateRequest, // seconds since the last template was generated. Otherwise, the // timestamp for the existing block template is updated (and possibly // the difficulty on testnet per the consesus rules). - if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { + if err := state.updateBlockTemplate(s, payAddr); err != nil { return nil, err } - return state.blockTemplateResult(s, useCoinbaseValue) + return state.blockTemplateResult(s) } // handleGetBlockTemplateLongPoll is a helper for handleGetBlockTemplateRequest @@ -204,10 +164,10 @@ func handleGetBlockTemplateRequest(s *Server, request *rpcmodel.TemplateRequest, // old block template is no longer valid due to a solution already being found // and added to the block DAG, or new transactions have shown up and some time // has passed without finding a solution. -func handleGetBlockTemplateLongPoll(s *Server, longPollID string, useCoinbaseValue bool, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockTemplateLongPoll(s *Server, longPollID string, payAddr util.Address, closeChan <-chan struct{}) (interface{}, error) { state := s.gbtWorkState - result, longPollChan, err := blockTemplateOrLongPollChan(s, longPollID, useCoinbaseValue) + result, longPollChan, err := blockTemplateOrLongPollChan(s, longPollID, payAddr) if err != nil { return nil, err } @@ -231,14 +191,14 @@ func handleGetBlockTemplateLongPoll(s *Server, longPollID string, useCoinbaseVal state.Lock() defer state.Unlock() - if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { + if err := state.updateBlockTemplate(s, payAddr); err != nil { return nil, err } // Include whether or not it is valid to submit work against the old // block template depending on whether or not a solution has already // been found and added to the block DAG. - result, err = state.blockTemplateResult(s, useCoinbaseValue) + result, err = state.blockTemplateResult(s) if err != nil { return nil, err } @@ -250,7 +210,7 @@ func handleGetBlockTemplateLongPoll(s *Server, longPollID string, useCoinbaseVal // template identified by the provided long poll ID is stale or // invalid. Otherwise, it returns a channel that will notify // when there's a more current template. -func blockTemplateOrLongPollChan(s *Server, longPollID string, useCoinbaseValue bool) (*rpcmodel.GetBlockTemplateResult, chan struct{}, error) { +func blockTemplateOrLongPollChan(s *Server, longPollID string, payAddr util.Address) (*rpcmodel.GetBlockTemplateResult, chan struct{}, error) { state := s.gbtWorkState state.Lock() @@ -259,7 +219,7 @@ func blockTemplateOrLongPollChan(s *Server, longPollID string, useCoinbaseValue // be manually unlocked before waiting for a notification about block // template changes. - if err := state.updateBlockTemplate(s, useCoinbaseValue); err != nil { + if err := state.updateBlockTemplate(s, payAddr); err != nil { return nil, nil, err } @@ -267,7 +227,7 @@ func blockTemplateOrLongPollChan(s *Server, longPollID string, useCoinbaseValue // the caller is invalid. parentHashes, lastGenerated, err := decodeLongPollID(longPollID) if err != nil { - result, err := state.blockTemplateResult(s, useCoinbaseValue) + result, err := state.blockTemplateResult(s) if err != nil { return nil, nil, err } @@ -285,7 +245,7 @@ func blockTemplateOrLongPollChan(s *Server, longPollID string, useCoinbaseValue // Include whether or not it is valid to submit work against the // old block template depending on whether or not a solution has // already been found and added to the block DAG. - result, err := state.blockTemplateResult(s, useCoinbaseValue) + result, err := state.blockTemplateResult(s) if err != nil { return nil, nil, err } @@ -566,7 +526,7 @@ func (state *gbtWorkState) templateUpdateChan(tipHashes []*daghash.Hash, lastGen // addresses. // // This function MUST be called with the state locked. -func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) error { +func (state *gbtWorkState) updateBlockTemplate(s *Server, payAddr util.Address) error { generator := s.cfg.Generator lastTxUpdate := generator.TxSource().LastUpdated() if lastTxUpdate.IsZero() { @@ -583,6 +543,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) template := state.template if template == nil || state.tipHashes == nil || !daghash.AreEqual(state.tipHashes, tipHashes) || + state.payAddress.String() != payAddr.String() || (state.lastTxUpdate != lastTxUpdate && time.Now().After(state.lastGenerated.Add(time.Second* gbtRegenerateSeconds))) { @@ -592,14 +553,6 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) // again. state.tipHashes = nil - // Choose a payment address at random if the caller requests a - // full coinbase as opposed to only the pertinent details needed - // to create their own coinbase. - var payAddr util.Address - if !useCoinbaseValue { - payAddr = config.ActiveConfig().MiningAddrs[rand.Intn(len(config.ActiveConfig().MiningAddrs))] - } - // Create a new block template that has a coinbase which anyone // can redeem. This is only acceptable because the returned // block template doesn't include the coinbase, so the caller @@ -634,6 +587,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) state.lastTxUpdate = lastTxUpdate state.tipHashes = tipHashes state.minTimestamp = minTimestamp + state.payAddress = payAddr log.Debugf("Generated block template (timestamp %s, "+ "target %s, merkle root %s)", @@ -650,32 +604,6 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) // trigger a new block template to be generated. So, update the // existing block template. - // When the caller requires a full coinbase as opposed to only - // the pertinent details needed to create their own coinbase, - // add a payment address to the output of the coinbase of the - // template if it doesn't already have one. Since this requires - // mining addresses to be specified via the config, an error is - // returned if none have been specified. - if !useCoinbaseValue && !template.ValidPayAddress { - // Choose a payment address at random. - payToAddr := config.ActiveConfig().MiningAddrs[rand.Intn(len(config.ActiveConfig().MiningAddrs))] - - // Update the block coinbase output of the template to - // pay to the randomly selected payment address. - scriptPubKey, err := txscript.PayToAddrScript(payToAddr) - if err != nil { - context := "Failed to create pay-to-addr script" - return internalRPCError(err.Error(), context) - } - template.Block.Transactions[util.CoinbaseTransactionIndex].TxOut[0].ScriptPubKey = scriptPubKey - template.ValidPayAddress = true - - // Update the merkle root. - block := util.NewBlock(template.Block) - hashMerkleTree := blockdag.BuildHashMerkleTreeStore(block.Transactions()) - template.Block.Header.HashMerkleRoot = hashMerkleTree.Root() - } - // Set locals for convenience. msgBlock = template.Block targetDifficulty = fmt.Sprintf("%064x", @@ -700,7 +628,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) // and returned to the caller. // // This function MUST be called with the state locked. -func (state *gbtWorkState) blockTemplateResult(s *Server, useCoinbaseValue bool) (*rpcmodel.GetBlockTemplateResult, error) { +func (state *gbtWorkState) blockTemplateResult(s *Server) (*rpcmodel.GetBlockTemplateResult, error) { dag := s.cfg.DAG // Ensure the timestamps are still in valid range for the template. // This should really only ever happen if the local clock is changed @@ -731,11 +659,6 @@ func (state *gbtWorkState) blockTemplateResult(s *Server, useCoinbaseValue bool) txID := tx.TxID() txIndex[*txID] = int64(i) - // Skip the coinbase transaction. - if i == 0 { - continue - } - // Create an array of 1-based indices to transactions that come // before this one in the transactions list which this one // depends on. This is necessary since the created block must @@ -775,7 +698,7 @@ func (state *gbtWorkState) blockTemplateResult(s *Server, useCoinbaseValue bool) // Including MinTime -> time/decrement // Omitting CoinbaseTxn -> coinbase, generation targetDifficulty := fmt.Sprintf("%064x", util.CompactToBig(header.Bits)) - longPollID := encodeLongPollID(state.tipHashes, state.lastGenerated) + longPollID := encodeLongPollID(state.tipHashes, state.payAddress, state.lastGenerated) // Check whether this node is synced with the rest of of the // network. There's almost never a good reason to mine on top @@ -806,48 +729,13 @@ func (state *gbtWorkState) blockTemplateResult(s *Server, useCoinbaseValue bool) IsSynced: isSynced, } - if useCoinbaseValue { - reply.CoinbaseAux = gbtCoinbaseAux - reply.CoinbaseValue = &msgBlock.Transactions[util.CoinbaseTransactionIndex].TxOut[0].Value - } else { - // Ensure the template has a valid payment address associated - // with it when a full coinbase is requested. - if !template.ValidPayAddress { - return nil, &rpcmodel.RPCError{ - Code: rpcmodel.ErrRPCInternal.Code, - Message: "A coinbase transaction has been " + - "requested, but the server has not " + - "been configured with any payment " + - "addresses via --miningaddr", - } - } - - // Serialize the transaction for conversion to hex. - tx := msgBlock.Transactions[util.CoinbaseTransactionIndex] - txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) - if err := tx.Serialize(txBuf); err != nil { - context := "Failed to serialize transaction" - return nil, internalRPCError(err.Error(), context) - } - - resultTx := rpcmodel.GetBlockTemplateResultTx{ - Data: hex.EncodeToString(txBuf.Bytes()), - ID: tx.TxID().String(), - Depends: []int64{}, - Mass: template.TxMasses[0], - Fee: template.Fees[0], - } - - reply.CoinbaseTxn = &resultTx - } - return &reply, nil } // encodeLongPollID encodes the passed details into an ID that can be used to // uniquely identify a block template. -func encodeLongPollID(parentHashes []*daghash.Hash, lastGenerated time.Time) string { - return fmt.Sprintf("%s-%d", daghash.JoinHashesStrings(parentHashes, ""), lastGenerated.Unix()) +func encodeLongPollID(parentHashes []*daghash.Hash, miningAddress util.Address, lastGenerated time.Time) string { + return fmt.Sprintf("%s-%s-%d", daghash.JoinHashesStrings(parentHashes, ""), miningAddress, lastGenerated.Unix()) } // decodeLongPollID decodes an ID that is used to uniquely identify a block diff --git a/server/rpc/rpcserverhelp.go b/server/rpc/rpcserverhelp.go index 2514d0204..eb31d888d 100644 --- a/server/rpc/rpcserverhelp.go +++ b/server/rpc/rpcserverhelp.go @@ -283,15 +283,15 @@ var helpDescsEnUS = map[string]string{ "getBlockHeaderVerboseResult-childHashes": "The hashes of the child blocks (only if there are any)", // TemplateRequest help. - "templateRequest-mode": "This is 'template', 'proposal', or omitted", - "templateRequest-capabilities": "List of capabilities", - "templateRequest-longPollId": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ", - "templateRequest-sigOpLimit": "Number of signature operations allowed in blocks (this parameter is ignored)", - "templateRequest-massLimit": "Max transaction mass allowed in blocks (this parameter is ignored)", - "templateRequest-maxVersion": "Highest supported block version number (this parameter is ignored)", - "templateRequest-target": "The desired target for the block template (this parameter is ignored)", - "templateRequest-data": "Hex-encoded block data (only for mode=proposal)", - "templateRequest-workId": "The server provided workid if provided in block template (not applicable)", + "templateRequest-mode": "This is 'template', 'proposal', or omitted", + "templateRequest-payAddress": "The address the coinbase pays to", + "templateRequest-longPollId": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ", + "templateRequest-sigOpLimit": "Number of signature operations allowed in blocks (this parameter is ignored)", + "templateRequest-massLimit": "Max transaction mass allowed in blocks (this parameter is ignored)", + "templateRequest-maxVersion": "Highest supported block version number (this parameter is ignored)", + "templateRequest-target": "The desired target for the block template (this parameter is ignored)", + "templateRequest-data": "Hex-encoded block data (only for mode=proposal)", + "templateRequest-workId": "The server provided workid if provided in block template (not applicable)", // GetBlockTemplateResultTx help. "getBlockTemplateResultTx-data": "Hex-encoded transaction data (byte-for-byte)",