mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[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:
parent
0744e8ebc0
commit
dc643c2d76
@ -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."`
|
||||
|
@ -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{}{}
|
||||
})
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -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"`
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)",
|
||||
|
Loading…
x
Reference in New Issue
Block a user