[NOD-833] Remove getBlockTemplate capabilites and move mining address to getBlockTemplate (#762)

* [NOD-833] Remove getBlockTemplate capabilites and move mining address to getBlockTemplate

* [NOD-833] Fix tests

* [NOD-833] Break long lines
This commit is contained in:
Ori Newman 2020-06-16 11:01:06 +03:00 committed by GitHub
parent 0744e8ebc0
commit dc643c2d76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 99 additions and 237 deletions

View File

@ -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."`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"`

View File

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

View File

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