diff --git a/blockmanager.go b/blockmanager.go
index 6a569bf7fa..fe2141301c 100644
--- a/blockmanager.go
+++ b/blockmanager.go
@@ -61,6 +61,12 @@ const (
// do expensive lottery data look ups for these blocks. It is
// equivalent to 24 hours of work on mainnet.
maxLotteryDataBlockDelta = 288
+
+ // templateRegenSeconds is the number of seconds that must pass before
+ // a new template is generated when the previous block hash has not
+ // changed and there have been changes to the available transactions
+ // in the memory pool.
+ templateRegenSeconds = 15
)
// zeroHash is the zero value hash (all zeros). It is defined as a convenience.
@@ -286,6 +292,13 @@ type setParentTemplateMsg struct {
reply chan setParentTemplateResponse
}
+// RegenTemplateMsg is a message sent to regenerate a block template.
+type RegenTemplateMsg struct{}
+
+// SetNextTemplateRegenMsg is a message sent to set a future time for the next
+// block template regeneration.
+type SetNextTemplateRegenMsg struct{}
+
// setParentTemplateResponse is a response sent to the reply channel of a
// setParentTemplateMsg.
type setParentTemplateResponse struct {
@@ -409,6 +422,13 @@ type blockManager struct {
lotteryDataBroadcast map[chainhash.Hash]struct{}
lotteryDataBroadcastMutex sync.RWMutex
+ // the following fields handle block template regeneration.
+ templateChan chan interface{}
+ lastRegen time.Time
+ nextRegen time.Time
+ nextRegenUpdated bool
+ regenTicker *time.Ticker
+
cachedCurrentTemplate *BlockTemplate
cachedParentTemplate *BlockTemplate
AggressiveMining bool
@@ -1929,6 +1949,70 @@ out:
bmgrLog.Trace("Block handler done")
}
+// evaluateBlockTemplate generates a new block template if the current
+// template has been invalidated. It must be run as a goroutine.
+func (b *blockManager) evaluateBlockTemplate() {
+ b.regenTicker = time.NewTicker(time.Second)
+ b.nextRegen = b.server.txMemPool.LastUpdated().
+ Add(time.Second * templateRegenSeconds)
+out:
+ for {
+ select {
+ case msg := <-b.templateChan:
+ switch msg.(type) {
+ case RegenTemplateMsg:
+ // Choose a payment address at random.
+ payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))]
+ template, err := NewBlockTemplate(b.server.rpcServer.policy,
+ b.server, payToAddr)
+ if err != nil {
+ bmgrLog.Errorf("Failed to create new block template: %v", err)
+ }
+
+ if template == nil {
+ // This happens if the template is returned nil because
+ // there are not enough voters on HEAD and there is
+ // currently an unsuitable parent cached template to
+ // try building off of.
+ bmgrLog.Error("Failed to create new block template: not " +
+ "enough voters and failed to find a suitable " +
+ "parent template to build from")
+ }
+
+ // Update cached templates.
+ b.SetParentTemplate(b.cachedCurrentTemplate)
+ b.SetCurrentTemplate(template)
+
+ // Set the next update time.
+ b.lastRegen = time.Now()
+ b.nextRegen = b.lastRegen.Add(time.Second * templateRegenSeconds)
+ b.nextRegenUpdated = false
+
+ case SetNextTemplateRegenMsg:
+ // Extend the next template regeneration time.
+ if !b.nextRegenUpdated {
+ b.nextRegen = b.nextRegen.Add(time.Second * templateRegenSeconds)
+ b.nextRegenUpdated = true
+ }
+ }
+ case <-b.regenTicker.C:
+ // Assert the mempool has been updated since the last template regeneration.
+ if b.server.txMemPool.LastUpdated().After(b.lastRegen) {
+ // Regenerate block template when the threshold is met.
+ if b.server.txMemPool.LastUpdated().Equal(b.nextRegen) ||
+ b.server.txMemPool.LastUpdated().After(b.nextRegen) {
+ b.templateChan <- RegenTemplateMsg{}
+ }
+ }
+
+ case <-b.quit:
+ break out
+ }
+ }
+
+ b.regenTicker.Stop()
+}
+
// handleNotifyMsg handles notifications from blockchain. It does things such
// as request orphan block parents and relay accepted blocks to connected peers.
func (b *blockManager) handleNotifyMsg(notification *blockchain.Notification) {
diff --git a/dcrjson/chainsvrwscmds.go b/dcrjson/chainsvrwscmds.go
index f42639fcd6..ac1961ef76 100644
--- a/dcrjson/chainsvrwscmds.go
+++ b/dcrjson/chainsvrwscmds.go
@@ -58,6 +58,15 @@ func NewNotifyBlocksCmd() *NotifyBlocksCmd {
return &NotifyBlocksCmd{}
}
+// NotifyWorkCmd defines the notifywork JSON-RPC command.
+type NotifyWorkCmd struct{}
+
+// NewNotifyWorkCmd returns a new instance which can be used to issue a
+// notifywork JSON-RPC command.
+func NewNotifyWorkCmd() *NotifyWorkCmd {
+ return &NotifyWorkCmd{}
+}
+
// NotifyWinningTicketsCmd is a type handling custom marshaling and
// unmarshaling of notifywinningtickets JSON websocket extension
// commands.
@@ -111,6 +120,15 @@ func NewStopNotifyBlocksCmd() *StopNotifyBlocksCmd {
return &StopNotifyBlocksCmd{}
}
+// StopNotifyWorkCmd defines the stopnotifywork JSON-RPC command.
+type StopNotifyWorkCmd struct{}
+
+// NewStopNotifyWorkCmd returns a new instance which can be used to issue a
+// stopnotifywork JSON-RPC command.
+func NewStopNotifyWorkCmd() *StopNotifyWorkCmd {
+ return &StopNotifyWorkCmd{}
+}
+
// NotifyNewTransactionsCmd defines the notifynewtransactions JSON-RPC command.
type NotifyNewTransactionsCmd struct {
Verbose *bool `jsonrpcdefault:"false"`
@@ -168,6 +186,7 @@ func init() {
MustRegisterCmd("authenticate", (*AuthenticateCmd)(nil), flags)
MustRegisterCmd("loadtxfilter", (*LoadTxFilterCmd)(nil), flags)
MustRegisterCmd("notifyblocks", (*NotifyBlocksCmd)(nil), flags)
+ MustRegisterCmd("notifywork", (*NotifyWorkCmd)(nil), flags)
MustRegisterCmd("notifynewtransactions", (*NotifyNewTransactionsCmd)(nil), flags)
MustRegisterCmd("notifynewtickets", (*NotifyNewTicketsCmd)(nil), flags)
MustRegisterCmd("notifyspentandmissedtickets",
@@ -178,6 +197,7 @@ func init() {
(*NotifyWinningTicketsCmd)(nil), flags)
MustRegisterCmd("session", (*SessionCmd)(nil), flags)
MustRegisterCmd("stopnotifyblocks", (*StopNotifyBlocksCmd)(nil), flags)
+ MustRegisterCmd("stopnotifywork", (*StopNotifyWorkCmd)(nil), flags)
MustRegisterCmd("stopnotifynewtransactions", (*StopNotifyNewTransactionsCmd)(nil), flags)
MustRegisterCmd("rescan", (*RescanCmd)(nil), flags)
}
diff --git a/dcrjson/chainsvrwsntfns.go b/dcrjson/chainsvrwsntfns.go
index dcab78177a..2b8ff0000f 100644
--- a/dcrjson/chainsvrwsntfns.go
+++ b/dcrjson/chainsvrwsntfns.go
@@ -13,6 +13,10 @@ const (
// the chain server that a block has been connected.
BlockConnectedNtfnMethod = "blockconnected"
+ // WorkAvialableNtfnMethod is the method used for notifications from
+ // the chain server that a block is available to mined.
+ WorkAvailableNtfnMethod = "workavailable"
+
// BlockDisconnectedNtfnMethod is the method used for notifications from
// the chain server that a block has been disconnected.
BlockDisconnectedNtfnMethod = "blockdisconnected"
@@ -52,6 +56,21 @@ func NewBlockConnectedNtfn(header string, subscribedTxs []string) *BlockConnecte
}
}
+// WorkAvailableNtfn models the data from the workavailable JSON-RPC notification.
+type WorkAvailableNtfn struct {
+ Data string `json:"data"`
+ Target string `json:"target"`
+}
+
+// NewWorkNtfn returns a new instance which can be used to issue a
+// newwork JSON-RPC notification.
+func NewWorkAvailableNtfn(data string, target string) *WorkAvailableNtfn {
+ return &WorkAvailableNtfn{
+ Data: data,
+ Target: target,
+ }
+}
+
// BlockDisconnectedNtfn defines the blockdisconnected JSON-RPC notification.
type BlockDisconnectedNtfn struct {
Header string `json:"header"`
diff --git a/docs/json_rpc_api.md b/docs/json_rpc_api.md
index 53088d8370..26bfdca5d3 100644
--- a/docs/json_rpc_api.md
+++ b/docs/json_rpc_api.md
@@ -693,16 +693,18 @@ user. Click the method name for further details such as parameter and return in
|---|------|-----------|-------------|
|1|[authenticate](#authenticate)|Authenticate the connection against the username and passphrase configured for the RPC server.
NOTE: This is only required if an HTTP Authorization header is not being used.|None|
|2|[notifyblocks](#notifyblocks)|Send notifications when a block is connected or disconnected from the best chain.|[blockconnected](#blockconnected) and [blockdisconnected](#blockdisconnected)|
-|3|[stopnotifyblocks](#stopnotifyblocks)|Cancel registered notifications for whenever a block is connected or disconnected from the main (best) chain. |None|
-|4|[notifyreceived](#notifyreceived)|Send notifications when a txout spends to an address.|[recvtx](#recvtx) and [redeemingtx](#redeemingtx)|
-|5|[stopnotifyreceived](#stopnotifyreceived)|Cancel registered notifications for when a txout spends to any of the passed addresses.|None|
-|6|[notifyspent](#notifyspent)|Send notification when a txout is spent.|[redeemingtx](#redeemingtx)|
-|7|[stopnotifyspent](#stopnotifyspent)|Cancel registered spending notifications for each passed outpoint.|None|
-|8|[loadtxfilter](#loadtxfilter)|Load, add to, or reload a websocket client's transaction filter for mempool transactions, new blocks and rescanblocks.|[relevanttxaccepted](#relevanttxaccepted)|
-|9|[rescan](#rescan)|Rescan block chain for transactions to addresses and spent transaction outpoints.|[recvtx](#recvtx), [redeemingtx](#redeemingtx), [rescanprogress](#rescanprogress), and [rescanfinished](#rescanfinished) |
-|10|[notifynewtransactions](#notifynewtransactions)|Send notifications for all new transactions as they are accepted into the mempool.|[txaccepted](#txaccepted) or [txacceptedverbose](#txacceptedverbose)|
-|11|[stopnotifynewtransactions](#stopnotifynewtransactions)|Stop sending either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.|None|
-|12|[session](#session)|Return details regarding a websocket client's current connection.|None|
+|3|[notifywork](#notifywork)|Send notifications when a block is available to be mined.| [workavailable](#workavailable)|
+|4|[stopnotifyblocks](#stopnotifyblocks)|Cancel registered notifications for whenever a block is connected or disconnected from the main (best) chain. |None|
+|5|[stopnotifywork](#stopnotifywork)|Cancel registered notifications for whenever a block is available to be mined. |None|
+|6|[notifyreceived](#notifyreceived)|Send notifications when a txout spends to an address.|[recvtx](#recvtx) and [redeemingtx](#redeemingtx)|
+|7|[stopnotifyreceived](#stopnotifyreceived)|Cancel registered notifications for when a txout spends to any of the passed addresses.|None|
+|8|[notifyspent](#notifyspent)|Send notification when a txout is spent.|[redeemingtx](#redeemingtx)|
+|9|[stopnotifyspent](#stopnotifyspent)|Cancel registered spending notifications for each passed outpoint.|None|
+|10|[loadtxfilter](#loadtxfilter)|Load, add to, or reload a websocket client's transaction filter for mempool transactions, new blocks and rescanblocks.|[relevanttxaccepted](#relevanttxaccepted)|
+|11|[rescan](#rescan)|Rescan block chain for transactions to addresses and spent transaction outpoints.|[recvtx](#recvtx), [redeemingtx](#redeemingtx), [rescanprogress](#rescanprogress), and [rescanfinished](#rescanfinished) |
+|12|[notifynewtransactions](#notifynewtransactions)|Send notifications for all new transactions as they are accepted into the mempool.|[txaccepted](#txaccepted) or [txacceptedverbose](#txacceptedverbose)|
+|13|[stopnotifynewtransactions](#stopnotifynewtransactions)|Stop sending either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.|None|
+|14|[session](#session)|Return details regarding a websocket client's current connection.|None|
**6.2 Method Details**
@@ -730,6 +732,19 @@ user. Click the method name for further details such as parameter and return in
|Returns|Nothing|
[Return to Overview](#WSMethodOverview)
+***
+
+
+
+| | |
+|---|---|
+|Method|notifywork|
+|Notifications|[workavailable](#workavailable)|
+|Parameters|None|
+|Description|Request notifications for whenever a block is available to be mined.|
+|Returns|Nothing|
+[Return to Overview](#WSMethodOverview)
+
***
@@ -742,6 +757,18 @@ user. Click the method name for further details such as parameter and return in
|Returns|Nothing|
[Return to Overview](#WSMethodOverview)
+***
+
+
+| | |
+|---|---|
+|Method|stopnotifywork|
+|Notifications|None|
+|Parameters|None|
+|Description|Cancel sending notifications for whenever a block is available to be mined.|
+|Returns|Nothing|
+[Return to Overview](#WSMethodOverview)
+
***
diff --git a/mempool/mempool.go b/mempool/mempool.go
index aa5b39ef97..5506e332d9 100644
--- a/mempool/mempool.go
+++ b/mempool/mempool.go
@@ -1166,7 +1166,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, rateLimit, allow
mp.addTransaction(utxoView, tx, txType, bestHeight, txFee)
// If it's an SSGen (vote), insert it into the list of
- // votes.
+ // votes and send block template generation message.
if txType == stake.TxTypeSSGen {
mp.votesMtx.Lock()
err := mp.insertVote(tx)
@@ -1174,6 +1174,21 @@ func (mp *TxPool) maybeAcceptTransaction(tx *dcrutil.Tx, isNew, rateLimit, allow
if err != nil {
return nil, err
}
+
+ // send block template generation message here.
+
+ // currently can not because the mempool does not have a reference
+ // to the dcrd server or the block manager. Looking for an elegant way
+ // of exposing the template channel to the mempool for this.
+
+ // So far passing a reference of the template channel to the mempool
+ // seems to be the way to go. That's posible since the mempool and
+ // the block manager are initialized when the dcrd server is being
+ // created. Let me know if I'm overthinking this, thanks.
+ }
+
+ if txType != stake.TxTypeSSGen {
+ // send block template generation message here.
}
log.Debugf("Accepted transaction %v (pool size: %v)", txHash,
diff --git a/rpcclient/infrastructure.go b/rpcclient/infrastructure.go
index dea40eecca..0db521c504 100644
--- a/rpcclient/infrastructure.go
+++ b/rpcclient/infrastructure.go
@@ -524,6 +524,14 @@ func (c *Client) reregisterNtfns() error {
}
}
+ // Reregister notifywork if needed.
+ if stateCopy.notifyWork {
+ log.Debugf("Reregistering [notifywork]")
+ if err := c.NotifyWork(); err != nil {
+ return err
+ }
+ }
+
// Reregister notifywinningtickets if needed.
if stateCopy.notifyWinningTickets {
log.Debugf("Reregistering [notifywinningtickets]")
diff --git a/rpcclient/notify.go b/rpcclient/notify.go
index 7ec621a4c4..bdd826899a 100644
--- a/rpcclient/notify.go
+++ b/rpcclient/notify.go
@@ -32,6 +32,7 @@ var (
// reconnect.
type notificationState struct {
notifyBlocks bool
+ notifyWork bool
notifyWinningTickets bool
notifySpentAndMissedTickets bool
notifyNewTickets bool
@@ -44,6 +45,7 @@ type notificationState struct {
func (s *notificationState) Copy() *notificationState {
var stateCopy notificationState
stateCopy.notifyBlocks = s.notifyBlocks
+ stateCopy.notifyWork = s.notifyWork
stateCopy.notifyWinningTickets = s.notifyWinningTickets
stateCopy.notifySpentAndMissedTickets = s.notifySpentAndMissedTickets
stateCopy.notifyNewTickets = s.notifyNewTickets
@@ -84,6 +86,12 @@ type NotificationHandlers struct {
// notification handlers, and is safe for blocking client requests.
OnClientConnected func()
+ // OnWorkAvailable is invoked when a block is available to be mined.
+ // It will only be invoked if a preceding call to
+ // NotifyWork has been made to register for the notification and the
+ // function is non-nil.
+ OnWorkAvailable func(data string, target string)
+
// OnBlockConnected is invoked when a block is connected to the longest
// (best) chain. It will only be invoked if a preceding call to
// NotifyBlocks has been made to register for the notification and the
@@ -231,6 +239,23 @@ func (c *Client) handleNotification(ntfn *rawNotification) {
c.ntfnHandlers.OnBlockConnected(blockHeader, transactions)
+ // OnWorkAvailable
+ case dcrjson.WorkAvailableNtfnMethod:
+ // Ignore the notification if the client is not interested in
+ // it.
+ if c.ntfnHandlers.OnWorkAvailable == nil {
+ return
+ }
+
+ data, target, err := parseWorkAvailableParams(ntfn.Params)
+ if err != nil {
+ log.Warnf("Received invalid workavailable "+
+ "notification: %v", err)
+ return
+ }
+
+ c.ntfnHandlers.OnWorkAvailable(string(data), string(target))
+
// OnBlockDisconnected
case dcrjson.BlockDisconnectedNtfnMethod:
// Ignore the notification if the client is not interested in
@@ -560,6 +585,16 @@ func parseBlockConnectedParams(params []json.RawMessage) (blockHeader []byte, tr
return blockHeader, transactions, nil
}
+// parseWorkAvailableParams parses out the parameters included in a
+// workavailable notification.
+func parseWorkAvailableParams(params []json.RawMessage) (data, target []byte, err error) {
+ if len(params) != 2 {
+ return nil, nil, wrongNumParams(len(params))
+ }
+
+ return params[0], params[1], nil
+}
+
// parseBlockDisconnectedParams parses out the parameters included in a
// blockdisconnected notification.
func parseBlockDisconnectedParams(params []json.RawMessage) (blockHeader []byte, err error) {
@@ -1174,6 +1209,54 @@ func (c *Client) NotifyBlocks() error {
return c.NotifyBlocksAsync().Receive()
}
+// FutureNotifyWorkResult is a future promise to deliver the result of a
+// NotifyWorkAsync RPC invocation (or an applicable error).
+type FutureNotifyWorkResult chan *response
+
+// Receive waits for the response promised by the future and returns an error
+// if the registration was not successful.
+func (r FutureNotifyWorkResult) Receive() error {
+ _, err := receiveFuture(r)
+ return err
+}
+
+// NotifyWorkAsync returns an instance of a type that can be used to get the
+// result of the RPC at some future time by invoking the Receive function on
+// the returned instance.
+//
+// See NotifyWork for the blocking version and more details.
+//
+// NOTE: This is a dcrd extension and requires a websocket connection.
+func (c *Client) NotifyWorkAsync() FutureNotifyWorkResult {
+ // Not supported in HTTP POST mode.
+ if c.config.HTTPPostMode {
+ return newFutureError(ErrWebsocketsRequired)
+ }
+
+ // Ignore the notification if the client is not interested in
+ // notifications.
+ if c.ntfnHandlers == nil {
+ return newNilFutureResult()
+ }
+
+ cmd := dcrjson.NewNotifyWorkCmd()
+ return c.sendCmd(cmd)
+}
+
+// NotifyWork registers the client to receive notifications when blocks are
+// connected and disconnected from the main chain. The notifications are
+// delivered to the notification handlers associated with the client. Calling
+// this function has no effect if there are no notification handlers and will
+// result in an error if the client is configured to run in HTTP POST mode.
+//
+// The notifications delivered as a result of this call will be via
+// OnWorkAvailable.
+//
+// NOTE: This is a dcrd extension and requires a websocket connection.
+func (c *Client) NotifyWork() error {
+ return c.NotifyWorkAsync().Receive()
+}
+
// FutureNotifyWinningTicketsResult is a future promise to deliver the result of a
// NotifyWinningTicketsAsync RPC invocation (or an applicable error).
type FutureNotifyWinningTicketsResult chan *response
diff --git a/rpcserver.go b/rpcserver.go
index 134f3f3167..7ba9c217a1 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -307,6 +307,7 @@ var rpcUnimplemented = map[string]struct{}{
var rpcLimited = map[string]struct{}{
// Websockets commands
"notifyblocks": {},
+ "notifywork": {},
"notifynewtransactions": {},
"notifyreceived": {},
"notifyspent": {},
diff --git a/rpcserverhelp.go b/rpcserverhelp.go
index 5b460e24e1..df56bc634e 100644
--- a/rpcserverhelp.go
+++ b/rpcserverhelp.go
@@ -780,9 +780,15 @@ var helpDescsEnUS = map[string]string{
// NotifyBlocksCmd help.
"notifyblocks--synopsis": "Request notifications for whenever a block is connected or disconnected from the main (best) chain.",
+ // NotifyWorkCmd help.
+ "notifywork--synopsis": "Request notifications for whenever a block is available to be mined.",
+
// StopNotifyBlocksCmd help.
"stopnotifyblocks--synopsis": "Cancel registered notifications for whenever a block is connected or disconnected from the main (best) chain.",
+ // StopNotifyWorkCmd help.
+ "stopnotifywork--synopsis": "Cancel registered notifications for whenever a block is available to be mined.",
+
// NotifyNewTransactionsCmd help.
"notifynewtransactions--synopsis": "Send either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.",
"notifynewtransactions-verbose": "Specifies which type of notification to receive. If verbose is true, then the caller receives txacceptedverbose, otherwise the caller receives txaccepted",
@@ -995,11 +1001,13 @@ var rpcResultTypes = map[string][]interface{}{
"notifynewtickets": nil,
"notifystakedifficulty": nil,
"notifyblocks": nil,
+ "notifywork": nil,
"notifynewtransactions": nil,
"notifyreceived": nil,
"notifyspent": nil,
"rescan": nil,
"stopnotifyblocks": nil,
+ "stopnotifywork": nil,
"stopnotifynewtransactions": nil,
"stopnotifyreceived": nil,
"stopnotifyspent": nil,
diff --git a/rpcwebsocket.go b/rpcwebsocket.go
index 046e76e7e4..be1efa9d9e 100644
--- a/rpcwebsocket.go
+++ b/rpcwebsocket.go
@@ -65,6 +65,7 @@ var wsHandlers map[string]wsCommandHandler
var wsHandlersBeforeInit = map[string]wsCommandHandler{
"loadtxfilter": handleLoadTxFilter,
"notifyblocks": handleNotifyBlocks,
+ "notifywork": handleNotifyWork,
"notifywinningtickets": handleWinningTickets,
"notifyspentandmissedtickets": handleSpentAndMissedTickets,
"notifynewtickets": handleNewTickets,
@@ -74,6 +75,7 @@ var wsHandlersBeforeInit = map[string]wsCommandHandler{
"help": handleWebsocketHelp,
"rescan": handleRescan,
"stopnotifyblocks": handleStopNotifyBlocks,
+ "stopnotifywork": handleStopNotifyWork,
"stopnotifynewtransactions": handleStopNotifyNewTransactions,
}
@@ -223,6 +225,19 @@ func (m *wsNotificationManager) NotifyBlockDisconnected(block *dcrutil.Block) {
}
}
+// NotifyWorkAvailable passes an available block to be mined to the notification
+// manager for processing.
+func (m *wsNotificationManager) NotifyWorkAvailable(wd *dcrjson.WorkAvailableNtfn) {
+ // As NotifyWorkAvailable will be called by the block manager
+ // and the RPC server may no longer be running, use a select
+ // statement to unblock enqueuing the notification once the RPC
+ // server has begun shutting down.
+ select {
+ case m.queueNotification <- (*notificationWorkAvailable)(wd):
+ case <-m.quit:
+ }
+}
+
// NotifyReorganization passes a blockchain reorganization notification for
// reorganization notification processing.
func (m *wsNotificationManager) NotifyReorganization(rd *blockchain.ReorganizationNtfnsData) {
@@ -452,6 +467,7 @@ func (f *wsClientFilter) existsUnspentOutPoint(op *wire.OutPoint) bool {
// Notification types
type notificationBlockConnected dcrutil.Block
type notificationBlockDisconnected dcrutil.Block
+type notificationWorkAvailable dcrjson.WorkAvailableNtfn
type notificationReorganization blockchain.ReorganizationNtfnsData
type notificationWinningTickets WinningTicketsNtfnData
type notificationSpentAndMissedTickets blockchain.TicketNotificationsData
@@ -467,6 +483,8 @@ type notificationRegisterClient wsClient
type notificationUnregisterClient wsClient
type notificationRegisterBlocks wsClient
type notificationUnregisterBlocks wsClient
+type notificationRegisterWorkAvailable wsClient
+type notificationUnregisterWorkAvailable wsClient
type notificationRegisterWinningTickets wsClient
type notificationUnregisterWinningTickets wsClient
type notificationRegisterSpentAndMissedTickets wsClient
@@ -492,6 +510,7 @@ func (m *wsNotificationManager) notificationHandler() {
// Where possible, the quit channel is used as the unique id for a client
// since it is quite a bit more efficient than using the entire struct.
blockNotifications := make(map[chan struct{}]*wsClient)
+ workNotifications := make(map[chan struct{}]*wsClient)
winningTicketNotifications := make(map[chan struct{}]*wsClient)
ticketSMNotifications := make(map[chan struct{}]*wsClient)
ticketNewNotifications := make(map[chan struct{}]*wsClient)
@@ -522,6 +541,10 @@ out:
m.notifyBlockDisconnected(blockNotifications,
(*dcrutil.Block)(n))
+ case *notificationWorkAvailable:
+ m.notifyWorkAvailable(workNotifications,
+ (*dcrjson.WorkAvailableNtfn)(n))
+
case *notificationReorganization:
m.notifyReorganization(blockNotifications,
(*blockchain.ReorganizationNtfnsData)(n))
@@ -556,6 +579,14 @@ out:
wsc := (*wsClient)(n)
delete(blockNotifications, wsc.quit)
+ case *notificationRegisterWorkAvailable:
+ wsc := (*wsClient)(n)
+ workNotifications[wsc.quit] = wsc
+
+ case *notificationUnregisterWorkAvailable:
+ wsc := (*wsClient)(n)
+ delete(workNotifications, wsc.quit)
+
case *notificationRegisterWinningTickets:
wsc := (*wsClient)(n)
winningTicketNotifications[wsc.quit] = wsc
@@ -597,6 +628,7 @@ out:
// Remove any requests made by the client as well as
// the client itself.
delete(blockNotifications, wsc.quit)
+ delete(workNotifications, wsc.quit)
delete(txNotifications, wsc.quit)
delete(clients, wsc.quit)
@@ -756,6 +788,33 @@ func (m *wsNotificationManager) notifyBlockConnected(clients map[chan struct{}]*
}
}
+// RegisterWorkAvailable requests block update notifications to the passed
+// websocket client.
+func (m *wsNotificationManager) RegisterWorkUpdates(wsc *wsClient) {
+ m.queueNotification <- (*notificationRegisterWorkAvailable)(wsc)
+}
+
+// UnregisterWorkUpdates removes work update notifications for the passed
+// websocket client.
+func (m *wsNotificationManager) UnregisterWorkUpdates(wsc *wsClient) {
+ m.queueNotification <- (*notificationUnregisterWorkAvailable)(wsc)
+}
+
+// notifyWorkAvailable notifies websocket clients that have registered for
+// work updates when a block is available to be mined.
+func (m *wsNotificationManager) notifyWorkAvailable(clients map[chan struct{}]*wsClient, workNtfn *dcrjson.WorkAvailableNtfn) {
+ for _, client := range clients {
+ // Marshal and queue notification.
+ marshalledJSON, err := dcrjson.MarshalCmd("1.0", nil, workNtfn)
+ if err != nil {
+ rpcsLog.Errorf("Failed to marshal work available"+
+ "notification: %v", err)
+ continue
+ }
+ client.QueueNotification(marshalledJSON)
+ }
+}
+
// notifyBlockDisconnected notifies websocket clients that have registered for
// block updates when a block is disconnected from the main chain (due to a
// reorganize).
@@ -2019,6 +2078,13 @@ func handleNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) {
return nil, nil
}
+// handleNotifyWork implements the notifywork command extension for
+// websocket connections.
+func handleNotifyWork(wsc *wsClient, icmd interface{}) (interface{}, error) {
+ wsc.server.ntfnMgr.RegisterWorkUpdates(wsc)
+ return nil, nil
+}
+
// handleSession implements the session command extension for websocket
// connections.
func handleSession(wsc *wsClient, icmd interface{}) (interface{}, error) {
@@ -2060,6 +2126,13 @@ func handleStopNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error
return nil, nil
}
+// handleStopNotifyWork implements the stopnotifywork command extension for
+// websocket connections.
+func handleStopNotifyWork(wsc *wsClient, icmd interface{}) (interface{}, error) {
+ wsc.server.ntfnMgr.UnregisterWorkUpdates(wsc)
+ return nil, nil
+}
+
// handleNotifyNewTransations implements the notifynewtransactions command
// extension for websocket connections.
func handleNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) {