Remove wallet notifications chan from std commands.
This commit cleans up the standard RPC command hanlding a bit by removing the websocket specific notification channel from the handlers. This was previously required because the sendrawtransaction, when called from a websocket enabled connection, needs to add a notification for when the transaction is mined. This commit modifies that to instead implement a websocket extended version of sendrawtransaction which invokes the standard handler and adds the notification. In addition, the main send was modified to first look if the command has a websocket specific handler first, and then falls back to standard commands, rather than the previous approach of first checking for a standard command and falling through to websocket commands. This essentially allows websockets connections to extend commands with the same name with additional functionality such as what was done in this commit.
This commit is contained in:
parent
5ad6d543d6
commit
9b166b3876
2 changed files with 96 additions and 100 deletions
72
rpcserver.go
72
rpcserver.go
|
@ -328,7 +328,7 @@ func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) {
|
|||
if jsonErr != nil {
|
||||
reply.Error = jsonErr
|
||||
} else {
|
||||
reply = standardCmdReply(cmd, s, nil)
|
||||
reply = standardCmdReply(cmd, s)
|
||||
}
|
||||
|
||||
rpcsLog.Tracef("reply: %v", reply)
|
||||
|
@ -341,9 +341,9 @@ func jsonRPCRead(w http.ResponseWriter, r *http.Request, s *rpcServer) {
|
|||
rpcsLog.Debugf(msg)
|
||||
}
|
||||
|
||||
// TODO(jrick): Remove the wallet notification chan.
|
||||
type commandHandler func(*rpcServer, btcjson.Cmd, chan []byte) (interface{}, error)
|
||||
type commandHandler func(*rpcServer, btcjson.Cmd) (interface{}, error)
|
||||
|
||||
// handlers maps RPC command strings to appropriate handler functions.
|
||||
var handlers = map[string]commandHandler{
|
||||
"addmultisigaddress": handleAskWallet,
|
||||
"addnode": handleAddNode,
|
||||
|
@ -421,22 +421,19 @@ var handlers = map[string]commandHandler{
|
|||
|
||||
// handleUnimplemented is a temporary handler for commands that we should
|
||||
// support but do not.
|
||||
func handleUnimplemented(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleUnimplemented(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
return nil, btcjson.ErrUnimplemented
|
||||
}
|
||||
|
||||
// handleAskWallet is the handler for commands that we do recognise as valid
|
||||
// but that we can not answer correctly since it involves wallet state.
|
||||
// These commands will be implemented in btcwallet.
|
||||
func handleAskWallet(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleAskWallet(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
return nil, btcjson.ErrNoWallet
|
||||
}
|
||||
|
||||
// handleAddNode handles addnode commands.
|
||||
func handleAddNode(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleAddNode(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.AddNodeCmd)
|
||||
|
||||
addr := normalizeAddress(c.Addr, activeNetParams.peerPort)
|
||||
|
@ -464,8 +461,7 @@ func handleAddNode(s *rpcServer, cmd btcjson.Cmd,
|
|||
}
|
||||
|
||||
// handleDebugLevel handles debuglevel commands.
|
||||
func handleDebugLevel(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleDebugLevel(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.DebugLevelCmd)
|
||||
|
||||
// Special show command to list supported subsystems.
|
||||
|
@ -559,8 +555,7 @@ func createVoutList(mtx *btcwire.MsgTx, net btcwire.BitcoinNet) ([]btcjson.Vout,
|
|||
}
|
||||
|
||||
// handleDecodeRawTransaction handles decoderawtransaction commands.
|
||||
func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.DecodeRawTransactionCmd)
|
||||
|
||||
// Deserialize the transaction.
|
||||
|
@ -607,9 +602,7 @@ func handleDecodeRawTransaction(s *rpcServer, cmd btcjson.Cmd,
|
|||
}
|
||||
|
||||
// handleGetBestBlockHash implements the getbestblockhash command.
|
||||
func handleGetBestBlockHash(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
var sha *btcwire.ShaHash
|
||||
func handleGetBestBlockHash(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
sha, _, err := s.server.db.NewestSha()
|
||||
if err != nil {
|
||||
rpcsLog.Errorf("Error getting newest sha: %v", err)
|
||||
|
@ -634,8 +627,7 @@ func messageToHex(msg btcwire.Message) (string, error) {
|
|||
}
|
||||
|
||||
// handleGetBlock implements the getblock command.
|
||||
func handleGetBlock(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetBlock(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetBlockCmd)
|
||||
sha, err := btcwire.NewShaHashFromStr(c.Hash)
|
||||
if err != nil {
|
||||
|
@ -732,8 +724,7 @@ func handleGetBlock(s *rpcServer, cmd btcjson.Cmd,
|
|||
}
|
||||
|
||||
// handleGetBlockCount implements the getblockcount command.
|
||||
func handleGetBlockCount(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetBlockCount(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
_, maxidx, err := s.server.db.NewestSha()
|
||||
if err != nil {
|
||||
rpcsLog.Errorf("Error getting newest sha: %v", err)
|
||||
|
@ -744,8 +735,7 @@ func handleGetBlockCount(s *rpcServer, cmd btcjson.Cmd,
|
|||
}
|
||||
|
||||
// handleGetBlockHash implements the getblockhash command.
|
||||
func handleGetBlockHash(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetBlockHash(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetBlockHashCmd)
|
||||
sha, err := s.server.db.FetchBlockShaByHeight(c.Index)
|
||||
if err != nil {
|
||||
|
@ -757,14 +747,12 @@ func handleGetBlockHash(s *rpcServer, cmd btcjson.Cmd,
|
|||
}
|
||||
|
||||
// handleGetConnectionCount implements the getconnectioncount command.
|
||||
func handleGetConnectionCount(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetConnectionCount(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
return s.server.ConnectedCount(), nil
|
||||
}
|
||||
|
||||
// handleGetDifficulty implements the getdifficulty command.
|
||||
func handleGetDifficulty(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetDifficulty(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
sha, _, err := s.server.db.NewestSha()
|
||||
if err != nil {
|
||||
rpcsLog.Errorf("Error getting sha: %v", err)
|
||||
|
@ -781,21 +769,19 @@ func handleGetDifficulty(s *rpcServer, cmd btcjson.Cmd,
|
|||
}
|
||||
|
||||
// handleGetGenerate implements the getgenerate command.
|
||||
func handleGetGenerate(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetGenerate(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
// btcd does not do mining so we can hardcode replies here.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// handleGetHashesPerSec implements the gethashespersec command.
|
||||
func handleGetHashesPerSec(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetHashesPerSec(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
// btcd does not do mining so we can hardcode replies here.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// handleGetPeerInfo implements the getpeerinfo command.
|
||||
func handleGetPeerInfo(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetPeerInfo(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
return s.server.PeerInfo(), nil
|
||||
}
|
||||
|
||||
|
@ -813,7 +799,7 @@ type mempoolDescriptor struct {
|
|||
}
|
||||
|
||||
// handleGetRawMempool implements the getrawmempool command.
|
||||
func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetRawMempoolCmd)
|
||||
descs := s.server.txMemPool.TxDescs()
|
||||
|
||||
|
@ -854,7 +840,7 @@ func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd, walletNotification chan
|
|||
}
|
||||
|
||||
// handleGetRawTransaction implements the getrawtransaction command.
|
||||
func handleGetRawTransaction(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleGetRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetRawTransactionCmd)
|
||||
|
||||
// Convert the provided transaction hash hex to a ShaHash.
|
||||
|
@ -962,7 +948,7 @@ func createTxRawResult(net btcwire.BitcoinNet, txSha string, mtx *btcwire.MsgTx,
|
|||
}
|
||||
|
||||
// handleSendRawTransaction implements the sendrawtransaction command.
|
||||
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.SendRawTransactionCmd)
|
||||
// Deserialize and send off to tx relay
|
||||
serializedTx, err := hex.DecodeString(c.HexTx)
|
||||
|
@ -1000,23 +986,17 @@ func handleSendRawTransaction(s *rpcServer, cmd btcjson.Cmd, walletNotification
|
|||
}
|
||||
}
|
||||
|
||||
// If called from websocket code, add a mined tx hashes
|
||||
// request.
|
||||
if walletNotification != nil {
|
||||
s.ws.AddMinedTxRequest(walletNotification, tx.Sha())
|
||||
}
|
||||
|
||||
return tx.Sha().String(), nil
|
||||
}
|
||||
|
||||
// handleSetGenerate implements the setgenerate command.
|
||||
func handleSetGenerate(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleSetGenerate(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
// btcd does not do mining so we can hardcode replies here.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleStop implements the stop command.
|
||||
func handleStop(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleStop(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
s.server.Stop()
|
||||
return "btcd stopping.", nil
|
||||
}
|
||||
|
@ -1067,7 +1047,7 @@ func verifyChain(db btcdb.Db, level, depth int32) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func handleVerifyChain(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) {
|
||||
func handleVerifyChain(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.VerifyChainCmd)
|
||||
|
||||
err := verifyChain(s.server.db, c.CheckLevel, c.CheckDepth)
|
||||
|
@ -1095,9 +1075,7 @@ func parseCmd(b []byte) (btcjson.Cmd, *btcjson.Error) {
|
|||
// standardCmdReply checks that a parsed command is a standard
|
||||
// Bitcoin JSON-RPC command and runs the proper handler to reply to the
|
||||
// command.
|
||||
func standardCmdReply(cmd btcjson.Cmd, s *rpcServer,
|
||||
walletNotification chan []byte) (reply btcjson.Reply) {
|
||||
|
||||
func standardCmdReply(cmd btcjson.Cmd, s *rpcServer) (reply btcjson.Reply) {
|
||||
id := cmd.Id()
|
||||
reply.Id = &id
|
||||
|
||||
|
@ -1107,7 +1085,7 @@ func standardCmdReply(cmd btcjson.Cmd, s *rpcServer,
|
|||
return reply
|
||||
}
|
||||
|
||||
result, err := handler(s, cmd, walletNotification)
|
||||
result, err := handler(s, cmd)
|
||||
if err != nil {
|
||||
jsonErr, ok := err.(btcjson.Error)
|
||||
if !ok {
|
||||
|
|
124
rpcwebsocket.go
124
rpcwebsocket.go
|
@ -228,11 +228,12 @@ type requestContexts struct {
|
|||
type wsCommandHandler func(*rpcServer, btcjson.Cmd, chan []byte, *requestContexts) error
|
||||
|
||||
var wsHandlers = map[string]wsCommandHandler{
|
||||
"getcurrentnet": handleGetCurrentNet,
|
||||
"getbestblock": handleGetBestBlock,
|
||||
"rescan": handleRescan,
|
||||
"notifynewtxs": handleNotifyNewTXs,
|
||||
"notifyspent": handleNotifySpent,
|
||||
"getcurrentnet": handleGetCurrentNet,
|
||||
"getbestblock": handleGetBestBlock,
|
||||
"notifynewtxs": handleNotifyNewTXs,
|
||||
"notifyspent": handleNotifySpent,
|
||||
"rescan": handleRescan,
|
||||
"sendrawtransaction:": handleWalletSendRawTransaction,
|
||||
}
|
||||
|
||||
// respondToAnyCmd checks that a parsed command is a standard or
|
||||
|
@ -242,22 +243,20 @@ var wsHandlers = map[string]wsCommandHandler{
|
|||
func respondToAnyCmd(cmd btcjson.Cmd, s *rpcServer,
|
||||
walletNotification chan []byte, rc *requestContexts) {
|
||||
|
||||
reply := standardCmdReply(cmd, s, walletNotification)
|
||||
if reply.Error != &btcjson.ErrMethodNotFound {
|
||||
mreply, _ := json.Marshal(reply)
|
||||
walletNotification <- mreply
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup the websocket extension for the command and if it doesn't
|
||||
// exist fallback to handling the command as a standard command.
|
||||
wsHandler, ok := wsHandlers[cmd.Method()]
|
||||
if !ok {
|
||||
reply.Error = &btcjson.ErrMethodNotFound
|
||||
reply := standardCmdReply(cmd, s)
|
||||
mreply, _ := json.Marshal(reply)
|
||||
walletNotification <- mreply
|
||||
return
|
||||
}
|
||||
|
||||
// Call the appropriate handler which responds unless there was an
|
||||
// error in which case the error is marshalled and sent here.
|
||||
if err := wsHandler(s, cmd, walletNotification, rc); err != nil {
|
||||
var reply btcjson.Reply
|
||||
jsonErr, ok := err.(btcjson.Error)
|
||||
if ok {
|
||||
reply.Error = &jsonErr
|
||||
|
@ -325,6 +324,54 @@ func handleGetBestBlock(s *rpcServer, cmd btcjson.Cmd,
|
|||
return nil
|
||||
}
|
||||
|
||||
// handleNotifyNewTXs implements the notifynewtxs command extension for
|
||||
// websocket connections.
|
||||
func handleNotifyNewTXs(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte, rc *requestContexts) error {
|
||||
|
||||
id := cmd.Id()
|
||||
reply := &btcjson.Reply{Id: &id}
|
||||
|
||||
notifyCmd, ok := cmd.(*btcws.NotifyNewTXsCmd)
|
||||
if !ok {
|
||||
return btcjson.ErrInternal
|
||||
}
|
||||
|
||||
for _, addr := range notifyCmd.Addresses {
|
||||
hash, _, err := btcutil.DecodeAddress(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot decode address: %v", err)
|
||||
}
|
||||
s.ws.AddTxRequest(walletNotification, rc, string(hash),
|
||||
cmd.Id())
|
||||
}
|
||||
|
||||
mreply, _ := json.Marshal(reply)
|
||||
walletNotification <- mreply
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleNotifySpent implements the notifyspent command extension for
|
||||
// websocket connections.
|
||||
func handleNotifySpent(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte, rc *requestContexts) error {
|
||||
|
||||
id := cmd.Id()
|
||||
reply := &btcjson.Reply{Id: &id}
|
||||
|
||||
notifyCmd, ok := cmd.(*btcws.NotifySpentCmd)
|
||||
if !ok {
|
||||
return btcjson.ErrInternal
|
||||
}
|
||||
|
||||
s.ws.AddSpentRequest(walletNotification, rc, notifyCmd.OutPoint,
|
||||
cmd.Id())
|
||||
|
||||
mreply, _ := json.Marshal(reply)
|
||||
walletNotification <- mreply
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleRescan implements the rescan command extension for websocket
|
||||
// connections.
|
||||
func handleRescan(s *rpcServer, cmd btcjson.Cmd,
|
||||
|
@ -444,51 +491,22 @@ func handleRescan(s *rpcServer, cmd btcjson.Cmd,
|
|||
return nil
|
||||
}
|
||||
|
||||
// handleNotifyNewTXs implements the notifynewtxs command extension for
|
||||
// websocket connections.
|
||||
func handleNotifyNewTXs(s *rpcServer, cmd btcjson.Cmd,
|
||||
// handleWalletSendRawTransaction implements the websocket extended version of
|
||||
// the sendrawtransaction command.
|
||||
func handleWalletSendRawTransaction(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte, rc *requestContexts) error {
|
||||
|
||||
id := cmd.Id()
|
||||
reply := &btcjson.Reply{Id: &id}
|
||||
|
||||
notifyCmd, ok := cmd.(*btcws.NotifyNewTXsCmd)
|
||||
if !ok {
|
||||
return btcjson.ErrInternal
|
||||
result, err := handleSendRawTransaction(s, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, addr := range notifyCmd.Addresses {
|
||||
hash, _, err := btcutil.DecodeAddress(addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot decode address: %v", err)
|
||||
}
|
||||
s.ws.AddTxRequest(walletNotification, rc, string(hash),
|
||||
cmd.Id())
|
||||
}
|
||||
// The result is already guaranteed to be a valid hash string if no
|
||||
// error was returned above, so it's safe to ignore the error here.
|
||||
txSha, _ := btcwire.NewShaHashFromStr(result.(string))
|
||||
|
||||
mreply, _ := json.Marshal(reply)
|
||||
walletNotification <- mreply
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleNotifySpent implements the notifyspent command extension for
|
||||
// websocket connections.
|
||||
func handleNotifySpent(s *rpcServer, cmd btcjson.Cmd,
|
||||
walletNotification chan []byte, rc *requestContexts) error {
|
||||
|
||||
id := cmd.Id()
|
||||
reply := &btcjson.Reply{Id: &id}
|
||||
|
||||
notifyCmd, ok := cmd.(*btcws.NotifySpentCmd)
|
||||
if !ok {
|
||||
return btcjson.ErrInternal
|
||||
}
|
||||
|
||||
s.ws.AddSpentRequest(walletNotification, rc, notifyCmd.OutPoint,
|
||||
cmd.Id())
|
||||
|
||||
mreply, _ := json.Marshal(reply)
|
||||
walletNotification <- mreply
|
||||
// Request to be notified when the transaction is mined.
|
||||
s.ws.AddMinedTxRequest(walletNotification, txSha)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue