github.com/palcoin-project/palcd@v1.0.0/rpcwebsocket.go (about) 1 // Copyright (c) 2013-2017 The btcsuite developers 2 // Copyright (c) 2015-2017 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package main 7 8 import ( 9 "bytes" 10 "container/list" 11 "crypto/sha256" 12 "crypto/subtle" 13 "encoding/base64" 14 "encoding/hex" 15 "encoding/json" 16 "errors" 17 "fmt" 18 "io" 19 "math" 20 "sync" 21 "time" 22 23 "github.com/btcsuite/websocket" 24 "github.com/palcoin-project/palcd/blockchain" 25 "github.com/palcoin-project/palcd/btcjson" 26 "github.com/palcoin-project/palcd/chaincfg" 27 "github.com/palcoin-project/palcd/chaincfg/chainhash" 28 "github.com/palcoin-project/palcd/database" 29 "github.com/palcoin-project/palcd/txscript" 30 "github.com/palcoin-project/palcd/wire" 31 "github.com/palcoin-project/palcutil" 32 "golang.org/x/crypto/ripemd160" 33 ) 34 35 const ( 36 // websocketSendBufferSize is the number of elements the send channel 37 // can queue before blocking. Note that this only applies to requests 38 // handled directly in the websocket client input handler or the async 39 // handler since notifications have their own queuing mechanism 40 // independent of the send channel buffer. 41 websocketSendBufferSize = 50 42 ) 43 44 type semaphore chan struct{} 45 46 func makeSemaphore(n int) semaphore { 47 return make(chan struct{}, n) 48 } 49 50 func (s semaphore) acquire() { s <- struct{}{} } 51 func (s semaphore) release() { <-s } 52 53 // timeZeroVal is simply the zero value for a time.Time and is used to avoid 54 // creating multiple instances. 55 var timeZeroVal time.Time 56 57 // wsCommandHandler describes a callback function used to handle a specific 58 // command. 59 type wsCommandHandler func(*wsClient, interface{}) (interface{}, error) 60 61 // wsHandlers maps RPC command strings to appropriate websocket handler 62 // functions. This is set by init because help references wsHandlers and thus 63 // causes a dependency loop. 64 var wsHandlers map[string]wsCommandHandler 65 var wsHandlersBeforeInit = map[string]wsCommandHandler{ 66 "loadtxfilter": handleLoadTxFilter, 67 "help": handleWebsocketHelp, 68 "notifyblocks": handleNotifyBlocks, 69 "notifynewtransactions": handleNotifyNewTransactions, 70 "notifyreceived": handleNotifyReceived, 71 "notifyspent": handleNotifySpent, 72 "session": handleSession, 73 "stopnotifyblocks": handleStopNotifyBlocks, 74 "stopnotifynewtransactions": handleStopNotifyNewTransactions, 75 "stopnotifyspent": handleStopNotifySpent, 76 "stopnotifyreceived": handleStopNotifyReceived, 77 "rescan": handleRescan, 78 "rescanblocks": handleRescanBlocks, 79 } 80 81 // WebsocketHandler handles a new websocket client by creating a new wsClient, 82 // starting it, and blocking until the connection closes. Since it blocks, it 83 // must be run in a separate goroutine. It should be invoked from the websocket 84 // server handler which runs each new connection in a new goroutine thereby 85 // satisfying the requirement. 86 func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, 87 authenticated bool, isAdmin bool) { 88 89 // Clear the read deadline that was set before the websocket hijacked 90 // the connection. 91 conn.SetReadDeadline(timeZeroVal) 92 93 // Limit max number of websocket clients. 94 rpcsLog.Infof("New websocket client %s", remoteAddr) 95 if s.ntfnMgr.NumClients()+1 > cfg.RPCMaxWebsockets { 96 rpcsLog.Infof("Max websocket clients exceeded [%d] - "+ 97 "disconnecting client %s", cfg.RPCMaxWebsockets, 98 remoteAddr) 99 conn.Close() 100 return 101 } 102 103 // Create a new websocket client to handle the new websocket connection 104 // and wait for it to shutdown. Once it has shutdown (and hence 105 // disconnected), remove it and any notifications it registered for. 106 client, err := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin) 107 if err != nil { 108 rpcsLog.Errorf("Failed to serve client %s: %v", remoteAddr, err) 109 conn.Close() 110 return 111 } 112 s.ntfnMgr.AddClient(client) 113 client.Start() 114 client.WaitForShutdown() 115 s.ntfnMgr.RemoveClient(client) 116 rpcsLog.Infof("Disconnected websocket client %s", remoteAddr) 117 } 118 119 // wsNotificationManager is a connection and notification manager used for 120 // websockets. It allows websocket clients to register for notifications they 121 // are interested in. When an event happens elsewhere in the code such as 122 // transactions being added to the memory pool or block connects/disconnects, 123 // the notification manager is provided with the relevant details needed to 124 // figure out which websocket clients need to be notified based on what they 125 // have registered for and notifies them accordingly. It is also used to keep 126 // track of all connected websocket clients. 127 type wsNotificationManager struct { 128 // server is the RPC server the notification manager is associated with. 129 server *rpcServer 130 131 // queueNotification queues a notification for handling. 132 queueNotification chan interface{} 133 134 // notificationMsgs feeds notificationHandler with notifications 135 // and client (un)registeration requests from a queue as well as 136 // registeration and unregisteration requests from clients. 137 notificationMsgs chan interface{} 138 139 // Access channel for current number of connected clients. 140 numClients chan int 141 142 // Shutdown handling 143 wg sync.WaitGroup 144 quit chan struct{} 145 } 146 147 // queueHandler manages a queue of empty interfaces, reading from in and 148 // sending the oldest unsent to out. This handler stops when either of the 149 // in or quit channels are closed, and closes out before returning, without 150 // waiting to send any variables still remaining in the queue. 151 func queueHandler(in <-chan interface{}, out chan<- interface{}, quit <-chan struct{}) { 152 var q []interface{} 153 var dequeue chan<- interface{} 154 skipQueue := out 155 var next interface{} 156 out: 157 for { 158 select { 159 case n, ok := <-in: 160 if !ok { 161 // Sender closed input channel. 162 break out 163 } 164 165 // Either send to out immediately if skipQueue is 166 // non-nil (queue is empty) and reader is ready, 167 // or append to the queue and send later. 168 select { 169 case skipQueue <- n: 170 default: 171 q = append(q, n) 172 dequeue = out 173 skipQueue = nil 174 next = q[0] 175 } 176 177 case dequeue <- next: 178 copy(q, q[1:]) 179 q[len(q)-1] = nil // avoid leak 180 q = q[:len(q)-1] 181 if len(q) == 0 { 182 dequeue = nil 183 skipQueue = out 184 } else { 185 next = q[0] 186 } 187 188 case <-quit: 189 break out 190 } 191 } 192 close(out) 193 } 194 195 // queueHandler maintains a queue of notifications and notification handler 196 // control messages. 197 func (m *wsNotificationManager) queueHandler() { 198 queueHandler(m.queueNotification, m.notificationMsgs, m.quit) 199 m.wg.Done() 200 } 201 202 // NotifyBlockConnected passes a block newly-connected to the best chain 203 // to the notification manager for block and transaction notification 204 // processing. 205 func (m *wsNotificationManager) NotifyBlockConnected(block *palcutil.Block) { 206 // As NotifyBlockConnected will be called by the block manager 207 // and the RPC server may no longer be running, use a select 208 // statement to unblock enqueuing the notification once the RPC 209 // server has begun shutting down. 210 select { 211 case m.queueNotification <- (*notificationBlockConnected)(block): 212 case <-m.quit: 213 } 214 } 215 216 // NotifyBlockDisconnected passes a block disconnected from the best chain 217 // to the notification manager for block notification processing. 218 func (m *wsNotificationManager) NotifyBlockDisconnected(block *palcutil.Block) { 219 // As NotifyBlockDisconnected will be called by the block manager 220 // and the RPC server may no longer be running, use a select 221 // statement to unblock enqueuing the notification once the RPC 222 // server has begun shutting down. 223 select { 224 case m.queueNotification <- (*notificationBlockDisconnected)(block): 225 case <-m.quit: 226 } 227 } 228 229 // NotifyMempoolTx passes a transaction accepted by mempool to the 230 // notification manager for transaction notification processing. If 231 // isNew is true, the tx is is a new transaction, rather than one 232 // added to the mempool during a reorg. 233 func (m *wsNotificationManager) NotifyMempoolTx(tx *palcutil.Tx, isNew bool) { 234 n := ¬ificationTxAcceptedByMempool{ 235 isNew: isNew, 236 tx: tx, 237 } 238 239 // As NotifyMempoolTx will be called by mempool and the RPC server 240 // may no longer be running, use a select statement to unblock 241 // enqueuing the notification once the RPC server has begun 242 // shutting down. 243 select { 244 case m.queueNotification <- n: 245 case <-m.quit: 246 } 247 } 248 249 // wsClientFilter tracks relevant addresses for each websocket client for 250 // the `rescanblocks` extension. It is modified by the `loadtxfilter` command. 251 // 252 // NOTE: This extension was ported from github.com/decred/dcrd 253 type wsClientFilter struct { 254 mu sync.Mutex 255 256 // Implemented fast paths for address lookup. 257 pubKeyHashes map[[ripemd160.Size]byte]struct{} 258 scriptHashes map[[ripemd160.Size]byte]struct{} 259 compressedPubKeys map[[33]byte]struct{} 260 uncompressedPubKeys map[[65]byte]struct{} 261 262 // A fallback address lookup map in case a fast path doesn't exist. 263 // Only exists for completeness. If using this shows up in a profile, 264 // there's a good chance a fast path should be added. 265 otherAddresses map[string]struct{} 266 267 // Outpoints of unspent outputs. 268 unspent map[wire.OutPoint]struct{} 269 } 270 271 // newWSClientFilter creates a new, empty wsClientFilter struct to be used 272 // for a websocket client. 273 // 274 // NOTE: This extension was ported from github.com/decred/dcrd 275 func newWSClientFilter(addresses []string, unspentOutPoints []wire.OutPoint, params *chaincfg.Params) *wsClientFilter { 276 filter := &wsClientFilter{ 277 pubKeyHashes: map[[ripemd160.Size]byte]struct{}{}, 278 scriptHashes: map[[ripemd160.Size]byte]struct{}{}, 279 compressedPubKeys: map[[33]byte]struct{}{}, 280 uncompressedPubKeys: map[[65]byte]struct{}{}, 281 otherAddresses: map[string]struct{}{}, 282 unspent: make(map[wire.OutPoint]struct{}, len(unspentOutPoints)), 283 } 284 285 for _, s := range addresses { 286 filter.addAddressStr(s, params) 287 } 288 for i := range unspentOutPoints { 289 filter.addUnspentOutPoint(&unspentOutPoints[i]) 290 } 291 292 return filter 293 } 294 295 // addAddress adds an address to a wsClientFilter, treating it correctly based 296 // on the type of address passed as an argument. 297 // 298 // NOTE: This extension was ported from github.com/decred/dcrd 299 func (f *wsClientFilter) addAddress(a palcutil.Address) { 300 switch a := a.(type) { 301 case *palcutil.AddressPubKeyHash: 302 f.pubKeyHashes[*a.Hash160()] = struct{}{} 303 return 304 case *palcutil.AddressScriptHash: 305 f.scriptHashes[*a.Hash160()] = struct{}{} 306 return 307 case *palcutil.AddressPubKey: 308 serializedPubKey := a.ScriptAddress() 309 switch len(serializedPubKey) { 310 case 33: // compressed 311 var compressedPubKey [33]byte 312 copy(compressedPubKey[:], serializedPubKey) 313 f.compressedPubKeys[compressedPubKey] = struct{}{} 314 return 315 case 65: // uncompressed 316 var uncompressedPubKey [65]byte 317 copy(uncompressedPubKey[:], serializedPubKey) 318 f.uncompressedPubKeys[uncompressedPubKey] = struct{}{} 319 return 320 } 321 } 322 323 f.otherAddresses[a.EncodeAddress()] = struct{}{} 324 } 325 326 // addAddressStr parses an address from a string and then adds it to the 327 // wsClientFilter using addAddress. 328 // 329 // NOTE: This extension was ported from github.com/decred/dcrd 330 func (f *wsClientFilter) addAddressStr(s string, params *chaincfg.Params) { 331 // If address can't be decoded, no point in saving it since it should also 332 // impossible to create the address from an inspected transaction output 333 // script. 334 a, err := palcutil.DecodeAddress(s, params) 335 if err != nil { 336 return 337 } 338 f.addAddress(a) 339 } 340 341 // existsAddress returns true if the passed address has been added to the 342 // wsClientFilter. 343 // 344 // NOTE: This extension was ported from github.com/decred/dcrd 345 func (f *wsClientFilter) existsAddress(a palcutil.Address) bool { 346 switch a := a.(type) { 347 case *palcutil.AddressPubKeyHash: 348 _, ok := f.pubKeyHashes[*a.Hash160()] 349 return ok 350 case *palcutil.AddressScriptHash: 351 _, ok := f.scriptHashes[*a.Hash160()] 352 return ok 353 case *palcutil.AddressPubKey: 354 serializedPubKey := a.ScriptAddress() 355 switch len(serializedPubKey) { 356 case 33: // compressed 357 var compressedPubKey [33]byte 358 copy(compressedPubKey[:], serializedPubKey) 359 _, ok := f.compressedPubKeys[compressedPubKey] 360 if !ok { 361 _, ok = f.pubKeyHashes[*a.AddressPubKeyHash().Hash160()] 362 } 363 return ok 364 case 65: // uncompressed 365 var uncompressedPubKey [65]byte 366 copy(uncompressedPubKey[:], serializedPubKey) 367 _, ok := f.uncompressedPubKeys[uncompressedPubKey] 368 if !ok { 369 _, ok = f.pubKeyHashes[*a.AddressPubKeyHash().Hash160()] 370 } 371 return ok 372 } 373 } 374 375 _, ok := f.otherAddresses[a.EncodeAddress()] 376 return ok 377 } 378 379 // removeAddress removes the passed address, if it exists, from the 380 // wsClientFilter. 381 // 382 // NOTE: This extension was ported from github.com/decred/dcrd 383 func (f *wsClientFilter) removeAddress(a palcutil.Address) { 384 switch a := a.(type) { 385 case *palcutil.AddressPubKeyHash: 386 delete(f.pubKeyHashes, *a.Hash160()) 387 return 388 case *palcutil.AddressScriptHash: 389 delete(f.scriptHashes, *a.Hash160()) 390 return 391 case *palcutil.AddressPubKey: 392 serializedPubKey := a.ScriptAddress() 393 switch len(serializedPubKey) { 394 case 33: // compressed 395 var compressedPubKey [33]byte 396 copy(compressedPubKey[:], serializedPubKey) 397 delete(f.compressedPubKeys, compressedPubKey) 398 return 399 case 65: // uncompressed 400 var uncompressedPubKey [65]byte 401 copy(uncompressedPubKey[:], serializedPubKey) 402 delete(f.uncompressedPubKeys, uncompressedPubKey) 403 return 404 } 405 } 406 407 delete(f.otherAddresses, a.EncodeAddress()) 408 } 409 410 // removeAddressStr parses an address from a string and then removes it from the 411 // wsClientFilter using removeAddress. 412 // 413 // NOTE: This extension was ported from github.com/decred/dcrd 414 func (f *wsClientFilter) removeAddressStr(s string, params *chaincfg.Params) { 415 a, err := palcutil.DecodeAddress(s, params) 416 if err == nil { 417 f.removeAddress(a) 418 } else { 419 delete(f.otherAddresses, s) 420 } 421 } 422 423 // addUnspentOutPoint adds an outpoint to the wsClientFilter. 424 // 425 // NOTE: This extension was ported from github.com/decred/dcrd 426 func (f *wsClientFilter) addUnspentOutPoint(op *wire.OutPoint) { 427 f.unspent[*op] = struct{}{} 428 } 429 430 // existsUnspentOutPoint returns true if the passed outpoint has been added to 431 // the wsClientFilter. 432 // 433 // NOTE: This extension was ported from github.com/decred/dcrd 434 func (f *wsClientFilter) existsUnspentOutPoint(op *wire.OutPoint) bool { 435 _, ok := f.unspent[*op] 436 return ok 437 } 438 439 // removeUnspentOutPoint removes the passed outpoint, if it exists, from the 440 // wsClientFilter. 441 // 442 // NOTE: This extension was ported from github.com/decred/dcrd 443 func (f *wsClientFilter) removeUnspentOutPoint(op *wire.OutPoint) { 444 delete(f.unspent, *op) 445 } 446 447 // Notification types 448 type notificationBlockConnected palcutil.Block 449 type notificationBlockDisconnected palcutil.Block 450 type notificationTxAcceptedByMempool struct { 451 isNew bool 452 tx *palcutil.Tx 453 } 454 455 // Notification control requests 456 type notificationRegisterClient wsClient 457 type notificationUnregisterClient wsClient 458 type notificationRegisterBlocks wsClient 459 type notificationUnregisterBlocks wsClient 460 type notificationRegisterNewMempoolTxs wsClient 461 type notificationUnregisterNewMempoolTxs wsClient 462 type notificationRegisterSpent struct { 463 wsc *wsClient 464 ops []*wire.OutPoint 465 } 466 type notificationUnregisterSpent struct { 467 wsc *wsClient 468 op *wire.OutPoint 469 } 470 type notificationRegisterAddr struct { 471 wsc *wsClient 472 addrs []string 473 } 474 type notificationUnregisterAddr struct { 475 wsc *wsClient 476 addr string 477 } 478 479 // notificationHandler reads notifications and control messages from the queue 480 // handler and processes one at a time. 481 func (m *wsNotificationManager) notificationHandler() { 482 // clients is a map of all currently connected websocket clients. 483 clients := make(map[chan struct{}]*wsClient) 484 485 // Maps used to hold lists of websocket clients to be notified on 486 // certain events. Each websocket client also keeps maps for the events 487 // which have multiple triggers to make removal from these lists on 488 // connection close less horrendously expensive. 489 // 490 // Where possible, the quit channel is used as the unique id for a client 491 // since it is quite a bit more efficient than using the entire struct. 492 blockNotifications := make(map[chan struct{}]*wsClient) 493 txNotifications := make(map[chan struct{}]*wsClient) 494 watchedOutPoints := make(map[wire.OutPoint]map[chan struct{}]*wsClient) 495 watchedAddrs := make(map[string]map[chan struct{}]*wsClient) 496 497 out: 498 for { 499 select { 500 case n, ok := <-m.notificationMsgs: 501 if !ok { 502 // queueHandler quit. 503 break out 504 } 505 switch n := n.(type) { 506 case *notificationBlockConnected: 507 block := (*palcutil.Block)(n) 508 509 // Skip iterating through all txs if no 510 // tx notification requests exist. 511 if len(watchedOutPoints) != 0 || len(watchedAddrs) != 0 { 512 for _, tx := range block.Transactions() { 513 m.notifyForTx(watchedOutPoints, 514 watchedAddrs, tx, block) 515 } 516 } 517 518 if len(blockNotifications) != 0 { 519 m.notifyBlockConnected(blockNotifications, 520 block) 521 m.notifyFilteredBlockConnected(blockNotifications, 522 block) 523 } 524 525 case *notificationBlockDisconnected: 526 block := (*palcutil.Block)(n) 527 528 if len(blockNotifications) != 0 { 529 m.notifyBlockDisconnected(blockNotifications, 530 block) 531 m.notifyFilteredBlockDisconnected(blockNotifications, 532 block) 533 } 534 535 case *notificationTxAcceptedByMempool: 536 if n.isNew && len(txNotifications) != 0 { 537 m.notifyForNewTx(txNotifications, n.tx) 538 } 539 m.notifyForTx(watchedOutPoints, watchedAddrs, n.tx, nil) 540 m.notifyRelevantTxAccepted(n.tx, clients) 541 542 case *notificationRegisterBlocks: 543 wsc := (*wsClient)(n) 544 blockNotifications[wsc.quit] = wsc 545 546 case *notificationUnregisterBlocks: 547 wsc := (*wsClient)(n) 548 delete(blockNotifications, wsc.quit) 549 550 case *notificationRegisterClient: 551 wsc := (*wsClient)(n) 552 clients[wsc.quit] = wsc 553 554 case *notificationUnregisterClient: 555 wsc := (*wsClient)(n) 556 // Remove any requests made by the client as well as 557 // the client itself. 558 delete(blockNotifications, wsc.quit) 559 delete(txNotifications, wsc.quit) 560 for k := range wsc.spentRequests { 561 op := k 562 m.removeSpentRequest(watchedOutPoints, wsc, &op) 563 } 564 for addr := range wsc.addrRequests { 565 m.removeAddrRequest(watchedAddrs, wsc, addr) 566 } 567 delete(clients, wsc.quit) 568 569 case *notificationRegisterSpent: 570 m.addSpentRequests(watchedOutPoints, n.wsc, n.ops) 571 572 case *notificationUnregisterSpent: 573 m.removeSpentRequest(watchedOutPoints, n.wsc, n.op) 574 575 case *notificationRegisterAddr: 576 m.addAddrRequests(watchedAddrs, n.wsc, n.addrs) 577 578 case *notificationUnregisterAddr: 579 m.removeAddrRequest(watchedAddrs, n.wsc, n.addr) 580 581 case *notificationRegisterNewMempoolTxs: 582 wsc := (*wsClient)(n) 583 txNotifications[wsc.quit] = wsc 584 585 case *notificationUnregisterNewMempoolTxs: 586 wsc := (*wsClient)(n) 587 delete(txNotifications, wsc.quit) 588 589 default: 590 rpcsLog.Warn("Unhandled notification type") 591 } 592 593 case m.numClients <- len(clients): 594 595 case <-m.quit: 596 // RPC server shutting down. 597 break out 598 } 599 } 600 601 for _, c := range clients { 602 c.Disconnect() 603 } 604 m.wg.Done() 605 } 606 607 // NumClients returns the number of clients actively being served. 608 func (m *wsNotificationManager) NumClients() (n int) { 609 select { 610 case n = <-m.numClients: 611 case <-m.quit: // Use default n (0) if server has shut down. 612 } 613 return 614 } 615 616 // RegisterBlockUpdates requests block update notifications to the passed 617 // websocket client. 618 func (m *wsNotificationManager) RegisterBlockUpdates(wsc *wsClient) { 619 m.queueNotification <- (*notificationRegisterBlocks)(wsc) 620 } 621 622 // UnregisterBlockUpdates removes block update notifications for the passed 623 // websocket client. 624 func (m *wsNotificationManager) UnregisterBlockUpdates(wsc *wsClient) { 625 m.queueNotification <- (*notificationUnregisterBlocks)(wsc) 626 } 627 628 // subscribedClients returns the set of all websocket client quit channels that 629 // are registered to receive notifications regarding tx, either due to tx 630 // spending a watched output or outputting to a watched address. Matching 631 // client's filters are updated based on this transaction's outputs and output 632 // addresses that may be relevant for a client. 633 func (m *wsNotificationManager) subscribedClients(tx *palcutil.Tx, 634 clients map[chan struct{}]*wsClient) map[chan struct{}]struct{} { 635 636 // Use a map of client quit channels as keys to prevent duplicates when 637 // multiple inputs and/or outputs are relevant to the client. 638 subscribed := make(map[chan struct{}]struct{}) 639 640 msgTx := tx.MsgTx() 641 for _, input := range msgTx.TxIn { 642 for quitChan, wsc := range clients { 643 wsc.Lock() 644 filter := wsc.filterData 645 wsc.Unlock() 646 if filter == nil { 647 continue 648 } 649 filter.mu.Lock() 650 if filter.existsUnspentOutPoint(&input.PreviousOutPoint) { 651 subscribed[quitChan] = struct{}{} 652 } 653 filter.mu.Unlock() 654 } 655 } 656 657 for i, output := range msgTx.TxOut { 658 _, addrs, _, err := txscript.ExtractPkScriptAddrs( 659 output.PkScript, m.server.cfg.ChainParams) 660 if err != nil { 661 // Clients are not able to subscribe to 662 // nonstandard or non-address outputs. 663 continue 664 } 665 for quitChan, wsc := range clients { 666 wsc.Lock() 667 filter := wsc.filterData 668 wsc.Unlock() 669 if filter == nil { 670 continue 671 } 672 filter.mu.Lock() 673 for _, a := range addrs { 674 if filter.existsAddress(a) { 675 subscribed[quitChan] = struct{}{} 676 op := wire.OutPoint{ 677 Hash: *tx.Hash(), 678 Index: uint32(i), 679 } 680 filter.addUnspentOutPoint(&op) 681 } 682 } 683 filter.mu.Unlock() 684 } 685 } 686 687 return subscribed 688 } 689 690 // notifyBlockConnected notifies websocket clients that have registered for 691 // block updates when a block is connected to the main chain. 692 func (*wsNotificationManager) notifyBlockConnected(clients map[chan struct{}]*wsClient, 693 block *palcutil.Block) { 694 695 // Notify interested websocket clients about the connected block. 696 ntfn := btcjson.NewBlockConnectedNtfn(block.Hash().String(), block.Height(), 697 block.MsgBlock().Header.Timestamp.Unix()) 698 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 699 if err != nil { 700 rpcsLog.Errorf("Failed to marshal block connected notification: "+ 701 "%v", err) 702 return 703 } 704 for _, wsc := range clients { 705 wsc.QueueNotification(marshalledJSON) 706 } 707 } 708 709 // notifyBlockDisconnected notifies websocket clients that have registered for 710 // block updates when a block is disconnected from the main chain (due to a 711 // reorganize). 712 func (*wsNotificationManager) notifyBlockDisconnected(clients map[chan struct{}]*wsClient, block *palcutil.Block) { 713 // Skip notification creation if no clients have requested block 714 // connected/disconnected notifications. 715 if len(clients) == 0 { 716 return 717 } 718 719 // Notify interested websocket clients about the disconnected block. 720 ntfn := btcjson.NewBlockDisconnectedNtfn(block.Hash().String(), 721 block.Height(), block.MsgBlock().Header.Timestamp.Unix()) 722 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 723 if err != nil { 724 rpcsLog.Errorf("Failed to marshal block disconnected "+ 725 "notification: %v", err) 726 return 727 } 728 for _, wsc := range clients { 729 wsc.QueueNotification(marshalledJSON) 730 } 731 } 732 733 // notifyFilteredBlockConnected notifies websocket clients that have registered for 734 // block updates when a block is connected to the main chain. 735 func (m *wsNotificationManager) notifyFilteredBlockConnected(clients map[chan struct{}]*wsClient, 736 block *palcutil.Block) { 737 738 // Create the common portion of the notification that is the same for 739 // every client. 740 var w bytes.Buffer 741 err := block.MsgBlock().Header.Serialize(&w) 742 if err != nil { 743 rpcsLog.Errorf("Failed to serialize header for filtered block "+ 744 "connected notification: %v", err) 745 return 746 } 747 ntfn := btcjson.NewFilteredBlockConnectedNtfn(block.Height(), 748 hex.EncodeToString(w.Bytes()), nil) 749 750 // Search for relevant transactions for each client and save them 751 // serialized in hex encoding for the notification. 752 subscribedTxs := make(map[chan struct{}][]string) 753 for _, tx := range block.Transactions() { 754 var txHex string 755 for quitChan := range m.subscribedClients(tx, clients) { 756 if txHex == "" { 757 txHex = txHexString(tx.MsgTx()) 758 } 759 subscribedTxs[quitChan] = append(subscribedTxs[quitChan], txHex) 760 } 761 } 762 for quitChan, wsc := range clients { 763 // Add all discovered transactions for this client. For clients 764 // that have no new-style filter, add the empty string slice. 765 ntfn.SubscribedTxs = subscribedTxs[quitChan] 766 767 // Marshal and queue notification. 768 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 769 if err != nil { 770 rpcsLog.Errorf("Failed to marshal filtered block "+ 771 "connected notification: %v", err) 772 return 773 } 774 wsc.QueueNotification(marshalledJSON) 775 } 776 } 777 778 // notifyFilteredBlockDisconnected notifies websocket clients that have registered for 779 // block updates when a block is disconnected from the main chain (due to a 780 // reorganize). 781 func (*wsNotificationManager) notifyFilteredBlockDisconnected(clients map[chan struct{}]*wsClient, 782 block *palcutil.Block) { 783 // Skip notification creation if no clients have requested block 784 // connected/disconnected notifications. 785 if len(clients) == 0 { 786 return 787 } 788 789 // Notify interested websocket clients about the disconnected block. 790 var w bytes.Buffer 791 err := block.MsgBlock().Header.Serialize(&w) 792 if err != nil { 793 rpcsLog.Errorf("Failed to serialize header for filtered block "+ 794 "disconnected notification: %v", err) 795 return 796 } 797 ntfn := btcjson.NewFilteredBlockDisconnectedNtfn(block.Height(), 798 hex.EncodeToString(w.Bytes())) 799 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 800 if err != nil { 801 rpcsLog.Errorf("Failed to marshal filtered block disconnected "+ 802 "notification: %v", err) 803 return 804 } 805 for _, wsc := range clients { 806 wsc.QueueNotification(marshalledJSON) 807 } 808 } 809 810 // RegisterNewMempoolTxsUpdates requests notifications to the passed websocket 811 // client when new transactions are added to the memory pool. 812 func (m *wsNotificationManager) RegisterNewMempoolTxsUpdates(wsc *wsClient) { 813 m.queueNotification <- (*notificationRegisterNewMempoolTxs)(wsc) 814 } 815 816 // UnregisterNewMempoolTxsUpdates removes notifications to the passed websocket 817 // client when new transaction are added to the memory pool. 818 func (m *wsNotificationManager) UnregisterNewMempoolTxsUpdates(wsc *wsClient) { 819 m.queueNotification <- (*notificationUnregisterNewMempoolTxs)(wsc) 820 } 821 822 // notifyForNewTx notifies websocket clients that have registered for updates 823 // when a new transaction is added to the memory pool. 824 func (m *wsNotificationManager) notifyForNewTx(clients map[chan struct{}]*wsClient, tx *palcutil.Tx) { 825 txHashStr := tx.Hash().String() 826 mtx := tx.MsgTx() 827 828 var amount int64 829 for _, txOut := range mtx.TxOut { 830 amount += txOut.Value 831 } 832 833 ntfn := btcjson.NewTxAcceptedNtfn(txHashStr, palcutil.Amount(amount).ToBTC()) 834 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 835 if err != nil { 836 rpcsLog.Errorf("Failed to marshal tx notification: %s", err.Error()) 837 return 838 } 839 840 var verboseNtfn *btcjson.TxAcceptedVerboseNtfn 841 var marshalledJSONVerbose []byte 842 for _, wsc := range clients { 843 if wsc.verboseTxUpdates { 844 if marshalledJSONVerbose != nil { 845 wsc.QueueNotification(marshalledJSONVerbose) 846 continue 847 } 848 849 net := m.server.cfg.ChainParams 850 rawTx, err := createTxRawResult(net, mtx, txHashStr, nil, 851 "", 0, 0) 852 if err != nil { 853 return 854 } 855 856 verboseNtfn = btcjson.NewTxAcceptedVerboseNtfn(*rawTx) 857 marshalledJSONVerbose, err = btcjson.MarshalCmd(btcjson.RpcVersion1, nil, 858 verboseNtfn) 859 if err != nil { 860 rpcsLog.Errorf("Failed to marshal verbose tx "+ 861 "notification: %s", err.Error()) 862 return 863 } 864 wsc.QueueNotification(marshalledJSONVerbose) 865 } else { 866 wsc.QueueNotification(marshalledJSON) 867 } 868 } 869 } 870 871 // RegisterSpentRequests requests a notification when each of the passed 872 // outpoints is confirmed spent (contained in a block connected to the main 873 // chain) for the passed websocket client. The request is automatically 874 // removed once the notification has been sent. 875 func (m *wsNotificationManager) RegisterSpentRequests(wsc *wsClient, ops []*wire.OutPoint) { 876 m.queueNotification <- ¬ificationRegisterSpent{ 877 wsc: wsc, 878 ops: ops, 879 } 880 } 881 882 // addSpentRequests modifies a map of watched outpoints to sets of websocket 883 // clients to add a new request watch all of the outpoints in ops and create 884 // and send a notification when spent to the websocket client wsc. 885 func (m *wsNotificationManager) addSpentRequests(opMap map[wire.OutPoint]map[chan struct{}]*wsClient, 886 wsc *wsClient, ops []*wire.OutPoint) { 887 888 for _, op := range ops { 889 // Track the request in the client as well so it can be quickly 890 // be removed on disconnect. 891 wsc.spentRequests[*op] = struct{}{} 892 893 // Add the client to the list to notify when the outpoint is seen. 894 // Create the list as needed. 895 cmap, ok := opMap[*op] 896 if !ok { 897 cmap = make(map[chan struct{}]*wsClient) 898 opMap[*op] = cmap 899 } 900 cmap[wsc.quit] = wsc 901 } 902 903 // Check if any transactions spending these outputs already exists in 904 // the mempool, if so send the notification immediately. 905 spends := make(map[chainhash.Hash]*palcutil.Tx) 906 for _, op := range ops { 907 spend := m.server.cfg.TxMemPool.CheckSpend(*op) 908 if spend != nil { 909 rpcsLog.Debugf("Found existing mempool spend for "+ 910 "outpoint<%v>: %v", op, spend.Hash()) 911 spends[*spend.Hash()] = spend 912 } 913 } 914 915 for _, spend := range spends { 916 m.notifyForTx(opMap, nil, spend, nil) 917 } 918 } 919 920 // UnregisterSpentRequest removes a request from the passed websocket client 921 // to be notified when the passed outpoint is confirmed spent (contained in a 922 // block connected to the main chain). 923 func (m *wsNotificationManager) UnregisterSpentRequest(wsc *wsClient, op *wire.OutPoint) { 924 m.queueNotification <- ¬ificationUnregisterSpent{ 925 wsc: wsc, 926 op: op, 927 } 928 } 929 930 // removeSpentRequest modifies a map of watched outpoints to remove the 931 // websocket client wsc from the set of clients to be notified when a 932 // watched outpoint is spent. If wsc is the last client, the outpoint 933 // key is removed from the map. 934 func (*wsNotificationManager) removeSpentRequest(ops map[wire.OutPoint]map[chan struct{}]*wsClient, 935 wsc *wsClient, op *wire.OutPoint) { 936 937 // Remove the request tracking from the client. 938 delete(wsc.spentRequests, *op) 939 940 // Remove the client from the list to notify. 941 notifyMap, ok := ops[*op] 942 if !ok { 943 rpcsLog.Warnf("Attempt to remove nonexistent spent request "+ 944 "for websocket client %s", wsc.addr) 945 return 946 } 947 delete(notifyMap, wsc.quit) 948 949 // Remove the map entry altogether if there are 950 // no more clients interested in it. 951 if len(notifyMap) == 0 { 952 delete(ops, *op) 953 } 954 } 955 956 // txHexString returns the serialized transaction encoded in hexadecimal. 957 func txHexString(tx *wire.MsgTx) string { 958 buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) 959 // Ignore Serialize's error, as writing to a bytes.buffer cannot fail. 960 tx.Serialize(buf) 961 return hex.EncodeToString(buf.Bytes()) 962 } 963 964 // blockDetails creates a BlockDetails struct to include in btcws notifications 965 // from a block and a transaction's block index. 966 func blockDetails(block *palcutil.Block, txIndex int) *btcjson.BlockDetails { 967 if block == nil { 968 return nil 969 } 970 return &btcjson.BlockDetails{ 971 Height: block.Height(), 972 Hash: block.Hash().String(), 973 Index: txIndex, 974 Time: block.MsgBlock().Header.Timestamp.Unix(), 975 } 976 } 977 978 // newRedeemingTxNotification returns a new marshalled redeemingtx notification 979 // with the passed parameters. 980 func newRedeemingTxNotification(txHex string, index int, block *palcutil.Block) ([]byte, error) { 981 // Create and marshal the notification. 982 ntfn := btcjson.NewRedeemingTxNtfn(txHex, blockDetails(block, index)) 983 return btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 984 } 985 986 // notifyForTxOuts examines each transaction output, notifying interested 987 // websocket clients of the transaction if an output spends to a watched 988 // address. A spent notification request is automatically registered for 989 // the client for each matching output. 990 func (m *wsNotificationManager) notifyForTxOuts(ops map[wire.OutPoint]map[chan struct{}]*wsClient, 991 addrs map[string]map[chan struct{}]*wsClient, tx *palcutil.Tx, block *palcutil.Block) { 992 993 // Nothing to do if nobody is listening for address notifications. 994 if len(addrs) == 0 { 995 return 996 } 997 998 txHex := "" 999 wscNotified := make(map[chan struct{}]struct{}) 1000 for i, txOut := range tx.MsgTx().TxOut { 1001 _, txAddrs, _, err := txscript.ExtractPkScriptAddrs( 1002 txOut.PkScript, m.server.cfg.ChainParams) 1003 if err != nil { 1004 continue 1005 } 1006 1007 for _, txAddr := range txAddrs { 1008 cmap, ok := addrs[txAddr.EncodeAddress()] 1009 if !ok { 1010 continue 1011 } 1012 1013 if txHex == "" { 1014 txHex = txHexString(tx.MsgTx()) 1015 } 1016 ntfn := btcjson.NewRecvTxNtfn(txHex, blockDetails(block, 1017 tx.Index())) 1018 1019 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 1020 if err != nil { 1021 rpcsLog.Errorf("Failed to marshal processedtx notification: %v", err) 1022 continue 1023 } 1024 1025 op := []*wire.OutPoint{wire.NewOutPoint(tx.Hash(), uint32(i))} 1026 for wscQuit, wsc := range cmap { 1027 m.addSpentRequests(ops, wsc, op) 1028 1029 if _, ok := wscNotified[wscQuit]; !ok { 1030 wscNotified[wscQuit] = struct{}{} 1031 wsc.QueueNotification(marshalledJSON) 1032 } 1033 } 1034 } 1035 } 1036 } 1037 1038 // notifyRelevantTxAccepted examines the inputs and outputs of the passed 1039 // transaction, notifying websocket clients of outputs spending to a watched 1040 // address and inputs spending a watched outpoint. Any outputs paying to a 1041 // watched address result in the output being watched as well for future 1042 // notifications. 1043 func (m *wsNotificationManager) notifyRelevantTxAccepted(tx *palcutil.Tx, 1044 clients map[chan struct{}]*wsClient) { 1045 1046 clientsToNotify := m.subscribedClients(tx, clients) 1047 1048 if len(clientsToNotify) != 0 { 1049 n := btcjson.NewRelevantTxAcceptedNtfn(txHexString(tx.MsgTx())) 1050 marshalled, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, n) 1051 if err != nil { 1052 rpcsLog.Errorf("Failed to marshal notification: %v", err) 1053 return 1054 } 1055 for quitChan := range clientsToNotify { 1056 clients[quitChan].QueueNotification(marshalled) 1057 } 1058 } 1059 } 1060 1061 // notifyForTx examines the inputs and outputs of the passed transaction, 1062 // notifying websocket clients of outputs spending to a watched address 1063 // and inputs spending a watched outpoint. 1064 func (m *wsNotificationManager) notifyForTx(ops map[wire.OutPoint]map[chan struct{}]*wsClient, 1065 addrs map[string]map[chan struct{}]*wsClient, tx *palcutil.Tx, block *palcutil.Block) { 1066 1067 if len(ops) != 0 { 1068 m.notifyForTxIns(ops, tx, block) 1069 } 1070 if len(addrs) != 0 { 1071 m.notifyForTxOuts(ops, addrs, tx, block) 1072 } 1073 } 1074 1075 // notifyForTxIns examines the inputs of the passed transaction and sends 1076 // interested websocket clients a redeemingtx notification if any inputs 1077 // spend a watched output. If block is non-nil, any matching spent 1078 // requests are removed. 1079 func (m *wsNotificationManager) notifyForTxIns(ops map[wire.OutPoint]map[chan struct{}]*wsClient, 1080 tx *palcutil.Tx, block *palcutil.Block) { 1081 1082 // Nothing to do if nobody is watching outpoints. 1083 if len(ops) == 0 { 1084 return 1085 } 1086 1087 txHex := "" 1088 wscNotified := make(map[chan struct{}]struct{}) 1089 for _, txIn := range tx.MsgTx().TxIn { 1090 prevOut := &txIn.PreviousOutPoint 1091 if cmap, ok := ops[*prevOut]; ok { 1092 if txHex == "" { 1093 txHex = txHexString(tx.MsgTx()) 1094 } 1095 marshalledJSON, err := newRedeemingTxNotification(txHex, tx.Index(), block) 1096 if err != nil { 1097 rpcsLog.Warnf("Failed to marshal redeemingtx notification: %v", err) 1098 continue 1099 } 1100 for wscQuit, wsc := range cmap { 1101 if block != nil { 1102 m.removeSpentRequest(ops, wsc, prevOut) 1103 } 1104 1105 if _, ok := wscNotified[wscQuit]; !ok { 1106 wscNotified[wscQuit] = struct{}{} 1107 wsc.QueueNotification(marshalledJSON) 1108 } 1109 } 1110 } 1111 } 1112 } 1113 1114 // RegisterTxOutAddressRequests requests notifications to the passed websocket 1115 // client when a transaction output spends to the passed address. 1116 func (m *wsNotificationManager) RegisterTxOutAddressRequests(wsc *wsClient, addrs []string) { 1117 m.queueNotification <- ¬ificationRegisterAddr{ 1118 wsc: wsc, 1119 addrs: addrs, 1120 } 1121 } 1122 1123 // addAddrRequests adds the websocket client wsc to the address to client set 1124 // addrMap so wsc will be notified for any mempool or block transaction outputs 1125 // spending to any of the addresses in addrs. 1126 func (*wsNotificationManager) addAddrRequests(addrMap map[string]map[chan struct{}]*wsClient, 1127 wsc *wsClient, addrs []string) { 1128 1129 for _, addr := range addrs { 1130 // Track the request in the client as well so it can be quickly be 1131 // removed on disconnect. 1132 wsc.addrRequests[addr] = struct{}{} 1133 1134 // Add the client to the set of clients to notify when the 1135 // outpoint is seen. Create map as needed. 1136 cmap, ok := addrMap[addr] 1137 if !ok { 1138 cmap = make(map[chan struct{}]*wsClient) 1139 addrMap[addr] = cmap 1140 } 1141 cmap[wsc.quit] = wsc 1142 } 1143 } 1144 1145 // UnregisterTxOutAddressRequest removes a request from the passed websocket 1146 // client to be notified when a transaction spends to the passed address. 1147 func (m *wsNotificationManager) UnregisterTxOutAddressRequest(wsc *wsClient, addr string) { 1148 m.queueNotification <- ¬ificationUnregisterAddr{ 1149 wsc: wsc, 1150 addr: addr, 1151 } 1152 } 1153 1154 // removeAddrRequest removes the websocket client wsc from the address to 1155 // client set addrs so it will no longer receive notification updates for 1156 // any transaction outputs send to addr. 1157 func (*wsNotificationManager) removeAddrRequest(addrs map[string]map[chan struct{}]*wsClient, 1158 wsc *wsClient, addr string) { 1159 1160 // Remove the request tracking from the client. 1161 delete(wsc.addrRequests, addr) 1162 1163 // Remove the client from the list to notify. 1164 cmap, ok := addrs[addr] 1165 if !ok { 1166 rpcsLog.Warnf("Attempt to remove nonexistent addr request "+ 1167 "<%s> for websocket client %s", addr, wsc.addr) 1168 return 1169 } 1170 delete(cmap, wsc.quit) 1171 1172 // Remove the map entry altogether if there are no more clients 1173 // interested in it. 1174 if len(cmap) == 0 { 1175 delete(addrs, addr) 1176 } 1177 } 1178 1179 // AddClient adds the passed websocket client to the notification manager. 1180 func (m *wsNotificationManager) AddClient(wsc *wsClient) { 1181 m.queueNotification <- (*notificationRegisterClient)(wsc) 1182 } 1183 1184 // RemoveClient removes the passed websocket client and all notifications 1185 // registered for it. 1186 func (m *wsNotificationManager) RemoveClient(wsc *wsClient) { 1187 select { 1188 case m.queueNotification <- (*notificationUnregisterClient)(wsc): 1189 case <-m.quit: 1190 } 1191 } 1192 1193 // Start starts the goroutines required for the manager to queue and process 1194 // websocket client notifications. 1195 func (m *wsNotificationManager) Start() { 1196 m.wg.Add(2) 1197 go m.queueHandler() 1198 go m.notificationHandler() 1199 } 1200 1201 // WaitForShutdown blocks until all notification manager goroutines have 1202 // finished. 1203 func (m *wsNotificationManager) WaitForShutdown() { 1204 m.wg.Wait() 1205 } 1206 1207 // Shutdown shuts down the manager, stopping the notification queue and 1208 // notification handler goroutines. 1209 func (m *wsNotificationManager) Shutdown() { 1210 close(m.quit) 1211 } 1212 1213 // newWsNotificationManager returns a new notification manager ready for use. 1214 // See wsNotificationManager for more details. 1215 func newWsNotificationManager(server *rpcServer) *wsNotificationManager { 1216 return &wsNotificationManager{ 1217 server: server, 1218 queueNotification: make(chan interface{}), 1219 notificationMsgs: make(chan interface{}), 1220 numClients: make(chan int), 1221 quit: make(chan struct{}), 1222 } 1223 } 1224 1225 // wsResponse houses a message to send to a connected websocket client as 1226 // well as a channel to reply on when the message is sent. 1227 type wsResponse struct { 1228 msg []byte 1229 doneChan chan bool 1230 } 1231 1232 // wsClient provides an abstraction for handling a websocket client. The 1233 // overall data flow is split into 3 main goroutines, a possible 4th goroutine 1234 // for long-running operations (only started if request is made), and a 1235 // websocket manager which is used to allow things such as broadcasting 1236 // requested notifications to all connected websocket clients. Inbound 1237 // messages are read via the inHandler goroutine and generally dispatched to 1238 // their own handler. However, certain potentially long-running operations such 1239 // as rescans, are sent to the asyncHander goroutine and are limited to one at a 1240 // time. There are two outbound message types - one for responding to client 1241 // requests and another for async notifications. Responses to client requests 1242 // use SendMessage which employs a buffered channel thereby limiting the number 1243 // of outstanding requests that can be made. Notifications are sent via 1244 // QueueNotification which implements a queue via notificationQueueHandler to 1245 // ensure sending notifications from other subsystems can't block. Ultimately, 1246 // all messages are sent via the outHandler. 1247 type wsClient struct { 1248 sync.Mutex 1249 1250 // server is the RPC server that is servicing the client. 1251 server *rpcServer 1252 1253 // conn is the underlying websocket connection. 1254 conn *websocket.Conn 1255 1256 // disconnected indicated whether or not the websocket client is 1257 // disconnected. 1258 disconnected bool 1259 1260 // addr is the remote address of the client. 1261 addr string 1262 1263 // authenticated specifies whether a client has been authenticated 1264 // and therefore is allowed to communicated over the websocket. 1265 authenticated bool 1266 1267 // isAdmin specifies whether a client may change the state of the server; 1268 // false means its access is only to the limited set of RPC calls. 1269 isAdmin bool 1270 1271 // sessionID is a random ID generated for each client when connected. 1272 // These IDs may be queried by a client using the session RPC. A change 1273 // to the session ID indicates that the client reconnected. 1274 sessionID uint64 1275 1276 // verboseTxUpdates specifies whether a client has requested verbose 1277 // information about all new transactions. 1278 verboseTxUpdates bool 1279 1280 // addrRequests is a set of addresses the caller has requested to be 1281 // notified about. It is maintained here so all requests can be removed 1282 // when a wallet disconnects. Owned by the notification manager. 1283 addrRequests map[string]struct{} 1284 1285 // spentRequests is a set of unspent Outpoints a wallet has requested 1286 // notifications for when they are spent by a processed transaction. 1287 // Owned by the notification manager. 1288 spentRequests map[wire.OutPoint]struct{} 1289 1290 // filterData is the new generation transaction filter backported from 1291 // github.com/decred/dcrd for the new backported `loadtxfilter` and 1292 // `rescanblocks` methods. 1293 filterData *wsClientFilter 1294 1295 // Networking infrastructure. 1296 serviceRequestSem semaphore 1297 ntfnChan chan []byte 1298 sendChan chan wsResponse 1299 quit chan struct{} 1300 wg sync.WaitGroup 1301 } 1302 1303 // inHandler handles all incoming messages for the websocket connection. It 1304 // must be run as a goroutine. 1305 func (c *wsClient) inHandler() { 1306 out: 1307 for { 1308 // Break out of the loop once the quit channel has been closed. 1309 // Use a non-blocking select here so we fall through otherwise. 1310 select { 1311 case <-c.quit: 1312 break out 1313 default: 1314 } 1315 1316 _, msg, err := c.conn.ReadMessage() 1317 if err != nil { 1318 // Log the error if it's not due to disconnecting. 1319 if err != io.EOF { 1320 rpcsLog.Errorf("Websocket receive error from "+ 1321 "%s: %v", c.addr, err) 1322 } 1323 break out 1324 } 1325 1326 var batchedRequest bool 1327 1328 // Determine request type 1329 if bytes.HasPrefix(msg, batchedRequestPrefix) { 1330 batchedRequest = true 1331 } 1332 1333 if !batchedRequest { 1334 var req btcjson.Request 1335 var reply json.RawMessage 1336 err = json.Unmarshal(msg, &req) 1337 if err != nil { 1338 // only process requests from authenticated clients 1339 if !c.authenticated { 1340 break out 1341 } 1342 1343 jsonErr := &btcjson.RPCError{ 1344 Code: btcjson.ErrRPCParse.Code, 1345 Message: "Failed to parse request: " + err.Error(), 1346 } 1347 reply, err = createMarshalledReply(btcjson.RpcVersion1, nil, nil, jsonErr) 1348 if err != nil { 1349 rpcsLog.Errorf("Failed to marshal reply: %v", err) 1350 continue 1351 } 1352 c.SendMessage(reply, nil) 1353 continue 1354 } 1355 1356 if req.Method == "" || req.Params == nil { 1357 jsonErr := &btcjson.RPCError{ 1358 Code: btcjson.ErrRPCInvalidRequest.Code, 1359 Message: "Invalid request: malformed", 1360 } 1361 reply, err := createMarshalledReply(req.Jsonrpc, req.ID, nil, jsonErr) 1362 if err != nil { 1363 rpcsLog.Errorf("Failed to marshal reply: %v", err) 1364 continue 1365 } 1366 c.SendMessage(reply, nil) 1367 continue 1368 } 1369 1370 // Valid requests with no ID (notifications) must not have a response 1371 // per the JSON-RPC spec. 1372 if req.ID == nil { 1373 if !c.authenticated { 1374 break out 1375 } 1376 continue 1377 } 1378 1379 cmd := parseCmd(&req) 1380 if cmd.err != nil { 1381 // Only process requests from authenticated clients 1382 if !c.authenticated { 1383 break out 1384 } 1385 1386 reply, err = createMarshalledReply(cmd.jsonrpc, cmd.id, nil, cmd.err) 1387 if err != nil { 1388 rpcsLog.Errorf("Failed to marshal reply: %v", err) 1389 continue 1390 } 1391 c.SendMessage(reply, nil) 1392 continue 1393 } 1394 1395 rpcsLog.Debugf("Received command <%s> from %s", cmd.method, c.addr) 1396 1397 // Check auth. The client is immediately disconnected if the 1398 // first request of an unauthentiated websocket client is not 1399 // the authenticate request, an authenticate request is received 1400 // when the client is already authenticated, or incorrect 1401 // authentication credentials are provided in the request. 1402 switch authCmd, ok := cmd.cmd.(*btcjson.AuthenticateCmd); { 1403 case c.authenticated && ok: 1404 rpcsLog.Warnf("Websocket client %s is already authenticated", 1405 c.addr) 1406 break out 1407 case !c.authenticated && !ok: 1408 rpcsLog.Warnf("Unauthenticated websocket message " + 1409 "received") 1410 break out 1411 case !c.authenticated: 1412 // Check credentials. 1413 login := authCmd.Username + ":" + authCmd.Passphrase 1414 auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) 1415 authSha := sha256.Sum256([]byte(auth)) 1416 cmp := subtle.ConstantTimeCompare(authSha[:], c.server.authsha[:]) 1417 limitcmp := subtle.ConstantTimeCompare(authSha[:], c.server.limitauthsha[:]) 1418 if cmp != 1 && limitcmp != 1 { 1419 rpcsLog.Warnf("Auth failure.") 1420 break out 1421 } 1422 c.authenticated = true 1423 c.isAdmin = cmp == 1 1424 1425 // Marshal and send response. 1426 reply, err = createMarshalledReply(cmd.jsonrpc, cmd.id, nil, nil) 1427 if err != nil { 1428 rpcsLog.Errorf("Failed to marshal authenticate reply: "+ 1429 "%v", err.Error()) 1430 continue 1431 } 1432 c.SendMessage(reply, nil) 1433 continue 1434 } 1435 1436 // Check if the client is using limited RPC credentials and 1437 // error when not authorized to call the supplied RPC. 1438 if !c.isAdmin { 1439 if _, ok := rpcLimited[req.Method]; !ok { 1440 jsonErr := &btcjson.RPCError{ 1441 Code: btcjson.ErrRPCInvalidParams.Code, 1442 Message: "limited user not authorized for this method", 1443 } 1444 // Marshal and send response. 1445 reply, err = createMarshalledReply("", req.ID, nil, jsonErr) 1446 if err != nil { 1447 rpcsLog.Errorf("Failed to marshal parse failure "+ 1448 "reply: %v", err) 1449 continue 1450 } 1451 c.SendMessage(reply, nil) 1452 continue 1453 } 1454 } 1455 1456 // Asynchronously handle the request. A semaphore is used to 1457 // limit the number of concurrent requests currently being 1458 // serviced. If the semaphore can not be acquired, simply wait 1459 // until a request finished before reading the next RPC request 1460 // from the websocket client. 1461 // 1462 // This could be a little fancier by timing out and erroring 1463 // when it takes too long to service the request, but if that is 1464 // done, the read of the next request should not be blocked by 1465 // this semaphore, otherwise the next request will be read and 1466 // will probably sit here for another few seconds before timing 1467 // out as well. This will cause the total timeout duration for 1468 // later requests to be much longer than the check here would 1469 // imply. 1470 // 1471 // If a timeout is added, the semaphore acquiring should be 1472 // moved inside of the new goroutine with a select statement 1473 // that also reads a time.After channel. This will unblock the 1474 // read of the next request from the websocket client and allow 1475 // many requests to be waited on concurrently. 1476 c.serviceRequestSem.acquire() 1477 go func() { 1478 c.serviceRequest(cmd) 1479 c.serviceRequestSem.release() 1480 }() 1481 } 1482 1483 // Process a batched request 1484 if batchedRequest { 1485 var batchedRequests []interface{} 1486 var results []json.RawMessage 1487 var batchSize int 1488 var reply json.RawMessage 1489 c.serviceRequestSem.acquire() 1490 err = json.Unmarshal(msg, &batchedRequests) 1491 if err != nil { 1492 // Only process requests from authenticated clients 1493 if !c.authenticated { 1494 break out 1495 } 1496 1497 jsonErr := &btcjson.RPCError{ 1498 Code: btcjson.ErrRPCParse.Code, 1499 Message: fmt.Sprintf("Failed to parse request: %v", 1500 err), 1501 } 1502 reply, err = btcjson.MarshalResponse(btcjson.RpcVersion2, nil, nil, jsonErr) 1503 if err != nil { 1504 rpcsLog.Errorf("Failed to create reply: %v", err) 1505 } 1506 1507 if reply != nil { 1508 results = append(results, reply) 1509 } 1510 } 1511 1512 if err == nil { 1513 // Response with an empty batch error if the batch size is zero 1514 if len(batchedRequests) == 0 { 1515 if !c.authenticated { 1516 break out 1517 } 1518 1519 jsonErr := &btcjson.RPCError{ 1520 Code: btcjson.ErrRPCInvalidRequest.Code, 1521 Message: "Invalid request: empty batch", 1522 } 1523 reply, err = btcjson.MarshalResponse(btcjson.RpcVersion2, nil, nil, jsonErr) 1524 if err != nil { 1525 rpcsLog.Errorf("Failed to marshal reply: %v", err) 1526 } 1527 1528 if reply != nil { 1529 results = append(results, reply) 1530 } 1531 } 1532 1533 // Process each batch entry individually 1534 if len(batchedRequests) > 0 { 1535 batchSize = len(batchedRequests) 1536 for _, entry := range batchedRequests { 1537 var reqBytes []byte 1538 reqBytes, err = json.Marshal(entry) 1539 if err != nil { 1540 // Only process requests from authenticated clients 1541 if !c.authenticated { 1542 break out 1543 } 1544 1545 jsonErr := &btcjson.RPCError{ 1546 Code: btcjson.ErrRPCInvalidRequest.Code, 1547 Message: fmt.Sprintf("Invalid request: %v", 1548 err), 1549 } 1550 reply, err = btcjson.MarshalResponse(btcjson.RpcVersion2, nil, nil, jsonErr) 1551 if err != nil { 1552 rpcsLog.Errorf("Failed to create reply: %v", err) 1553 continue 1554 } 1555 1556 if reply != nil { 1557 results = append(results, reply) 1558 } 1559 continue 1560 } 1561 1562 var req btcjson.Request 1563 err := json.Unmarshal(reqBytes, &req) 1564 if err != nil { 1565 // Only process requests from authenticated clients 1566 if !c.authenticated { 1567 break out 1568 } 1569 1570 jsonErr := &btcjson.RPCError{ 1571 Code: btcjson.ErrRPCInvalidRequest.Code, 1572 Message: fmt.Sprintf("Invalid request: %v", 1573 err), 1574 } 1575 reply, err = btcjson.MarshalResponse(btcjson.RpcVersion2, nil, nil, jsonErr) 1576 if err != nil { 1577 rpcsLog.Errorf("Failed to create reply: %v", err) 1578 continue 1579 } 1580 1581 if reply != nil { 1582 results = append(results, reply) 1583 } 1584 continue 1585 } 1586 1587 if req.Method == "" || req.Params == nil { 1588 jsonErr := &btcjson.RPCError{ 1589 Code: btcjson.ErrRPCInvalidRequest.Code, 1590 Message: "Invalid request: malformed", 1591 } 1592 reply, err := createMarshalledReply(req.Jsonrpc, req.ID, nil, jsonErr) 1593 if err != nil { 1594 rpcsLog.Errorf("Failed to marshal reply: %v", err) 1595 continue 1596 } 1597 1598 if reply != nil { 1599 results = append(results, reply) 1600 } 1601 continue 1602 } 1603 1604 // Valid requests with no ID (notifications) must not have a response 1605 // per the JSON-RPC spec. 1606 if req.ID == nil { 1607 if !c.authenticated { 1608 break out 1609 } 1610 continue 1611 } 1612 1613 cmd := parseCmd(&req) 1614 if cmd.err != nil { 1615 // Only process requests from authenticated clients 1616 if !c.authenticated { 1617 break out 1618 } 1619 1620 reply, err = createMarshalledReply(cmd.jsonrpc, cmd.id, nil, cmd.err) 1621 if err != nil { 1622 rpcsLog.Errorf("Failed to marshal reply: %v", err) 1623 continue 1624 } 1625 1626 if reply != nil { 1627 results = append(results, reply) 1628 } 1629 continue 1630 } 1631 1632 rpcsLog.Debugf("Received command <%s> from %s", cmd.method, c.addr) 1633 1634 // Check auth. The client is immediately disconnected if the 1635 // first request of an unauthentiated websocket client is not 1636 // the authenticate request, an authenticate request is received 1637 // when the client is already authenticated, or incorrect 1638 // authentication credentials are provided in the request. 1639 switch authCmd, ok := cmd.cmd.(*btcjson.AuthenticateCmd); { 1640 case c.authenticated && ok: 1641 rpcsLog.Warnf("Websocket client %s is already authenticated", 1642 c.addr) 1643 break out 1644 case !c.authenticated && !ok: 1645 rpcsLog.Warnf("Unauthenticated websocket message " + 1646 "received") 1647 break out 1648 case !c.authenticated: 1649 // Check credentials. 1650 login := authCmd.Username + ":" + authCmd.Passphrase 1651 auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) 1652 authSha := sha256.Sum256([]byte(auth)) 1653 cmp := subtle.ConstantTimeCompare(authSha[:], c.server.authsha[:]) 1654 limitcmp := subtle.ConstantTimeCompare(authSha[:], c.server.limitauthsha[:]) 1655 if cmp != 1 && limitcmp != 1 { 1656 rpcsLog.Warnf("Auth failure.") 1657 break out 1658 } 1659 1660 c.authenticated = true 1661 c.isAdmin = cmp == 1 1662 1663 // Marshal and send response. 1664 reply, err = createMarshalledReply(cmd.jsonrpc, cmd.id, nil, nil) 1665 if err != nil { 1666 rpcsLog.Errorf("Failed to marshal authenticate reply: "+ 1667 "%v", err.Error()) 1668 continue 1669 } 1670 1671 if reply != nil { 1672 results = append(results, reply) 1673 } 1674 continue 1675 } 1676 1677 // Check if the client is using limited RPC credentials and 1678 // error when not authorized to call the supplied RPC. 1679 if !c.isAdmin { 1680 if _, ok := rpcLimited[req.Method]; !ok { 1681 jsonErr := &btcjson.RPCError{ 1682 Code: btcjson.ErrRPCInvalidParams.Code, 1683 Message: "limited user not authorized for this method", 1684 } 1685 // Marshal and send response. 1686 reply, err = createMarshalledReply(req.Jsonrpc, req.ID, nil, jsonErr) 1687 if err != nil { 1688 rpcsLog.Errorf("Failed to marshal parse failure "+ 1689 "reply: %v", err) 1690 continue 1691 } 1692 1693 if reply != nil { 1694 results = append(results, reply) 1695 } 1696 continue 1697 } 1698 } 1699 1700 // Lookup the websocket extension for the command, if it doesn't 1701 // exist fallback to handling the command as a standard command. 1702 var resp interface{} 1703 wsHandler, ok := wsHandlers[cmd.method] 1704 if ok { 1705 resp, err = wsHandler(c, cmd.cmd) 1706 } else { 1707 resp, err = c.server.standardCmdResult(cmd, nil) 1708 } 1709 1710 // Marshal request output. 1711 reply, err := createMarshalledReply(cmd.jsonrpc, cmd.id, resp, err) 1712 if err != nil { 1713 rpcsLog.Errorf("Failed to marshal reply for <%s> "+ 1714 "command: %v", cmd.method, err) 1715 return 1716 } 1717 1718 if reply != nil { 1719 results = append(results, reply) 1720 } 1721 } 1722 } 1723 } 1724 1725 // generate reply 1726 var payload = []byte{} 1727 if batchedRequest && batchSize > 0 { 1728 if len(results) > 0 { 1729 // Form the batched response json 1730 var buffer bytes.Buffer 1731 buffer.WriteByte('[') 1732 for idx, marshalledReply := range results { 1733 if idx == len(results)-1 { 1734 buffer.Write(marshalledReply) 1735 buffer.WriteByte(']') 1736 break 1737 } 1738 buffer.Write(marshalledReply) 1739 buffer.WriteByte(',') 1740 } 1741 payload = buffer.Bytes() 1742 } 1743 } 1744 1745 if !batchedRequest || batchSize == 0 { 1746 // Respond with the first results entry for single requests 1747 if len(results) > 0 { 1748 payload = results[0] 1749 } 1750 } 1751 1752 c.SendMessage(payload, nil) 1753 c.serviceRequestSem.release() 1754 } 1755 } 1756 1757 // Ensure the connection is closed. 1758 c.Disconnect() 1759 c.wg.Done() 1760 rpcsLog.Tracef("Websocket client input handler done for %s", c.addr) 1761 } 1762 1763 // serviceRequest services a parsed RPC request by looking up and executing the 1764 // appropriate RPC handler. The response is marshalled and sent to the 1765 // websocket client. 1766 func (c *wsClient) serviceRequest(r *parsedRPCCmd) { 1767 var ( 1768 result interface{} 1769 err error 1770 ) 1771 1772 // Lookup the websocket extension for the command and if it doesn't 1773 // exist fallback to handling the command as a standard command. 1774 wsHandler, ok := wsHandlers[r.method] 1775 if ok { 1776 result, err = wsHandler(c, r.cmd) 1777 } else { 1778 result, err = c.server.standardCmdResult(r, nil) 1779 } 1780 reply, err := createMarshalledReply(r.jsonrpc, r.id, result, err) 1781 if err != nil { 1782 rpcsLog.Errorf("Failed to marshal reply for <%s> "+ 1783 "command: %v", r.method, err) 1784 return 1785 } 1786 c.SendMessage(reply, nil) 1787 } 1788 1789 // notificationQueueHandler handles the queuing of outgoing notifications for 1790 // the websocket client. This runs as a muxer for various sources of input to 1791 // ensure that queuing up notifications to be sent will not block. Otherwise, 1792 // slow clients could bog down the other systems (such as the mempool or block 1793 // manager) which are queuing the data. The data is passed on to outHandler to 1794 // actually be written. It must be run as a goroutine. 1795 func (c *wsClient) notificationQueueHandler() { 1796 ntfnSentChan := make(chan bool, 1) // nonblocking sync 1797 1798 // pendingNtfns is used as a queue for notifications that are ready to 1799 // be sent once there are no outstanding notifications currently being 1800 // sent. The waiting flag is used over simply checking for items in the 1801 // pending list to ensure cleanup knows what has and hasn't been sent 1802 // to the outHandler. Currently no special cleanup is needed, however 1803 // if something like a done channel is added to notifications in the 1804 // future, not knowing what has and hasn't been sent to the outHandler 1805 // (and thus who should respond to the done channel) would be 1806 // problematic without using this approach. 1807 pendingNtfns := list.New() 1808 waiting := false 1809 out: 1810 for { 1811 select { 1812 // This channel is notified when a message is being queued to 1813 // be sent across the network socket. It will either send the 1814 // message immediately if a send is not already in progress, or 1815 // queue the message to be sent once the other pending messages 1816 // are sent. 1817 case msg := <-c.ntfnChan: 1818 if !waiting { 1819 c.SendMessage(msg, ntfnSentChan) 1820 } else { 1821 pendingNtfns.PushBack(msg) 1822 } 1823 waiting = true 1824 1825 // This channel is notified when a notification has been sent 1826 // across the network socket. 1827 case <-ntfnSentChan: 1828 // No longer waiting if there are no more messages in 1829 // the pending messages queue. 1830 next := pendingNtfns.Front() 1831 if next == nil { 1832 waiting = false 1833 continue 1834 } 1835 1836 // Notify the outHandler about the next item to 1837 // asynchronously send. 1838 msg := pendingNtfns.Remove(next).([]byte) 1839 c.SendMessage(msg, ntfnSentChan) 1840 1841 case <-c.quit: 1842 break out 1843 } 1844 } 1845 1846 // Drain any wait channels before exiting so nothing is left waiting 1847 // around to send. 1848 cleanup: 1849 for { 1850 select { 1851 case <-c.ntfnChan: 1852 case <-ntfnSentChan: 1853 default: 1854 break cleanup 1855 } 1856 } 1857 c.wg.Done() 1858 rpcsLog.Tracef("Websocket client notification queue handler done "+ 1859 "for %s", c.addr) 1860 } 1861 1862 // outHandler handles all outgoing messages for the websocket connection. It 1863 // must be run as a goroutine. It uses a buffered channel to serialize output 1864 // messages while allowing the sender to continue running asynchronously. It 1865 // must be run as a goroutine. 1866 func (c *wsClient) outHandler() { 1867 out: 1868 for { 1869 // Send any messages ready for send until the quit channel is 1870 // closed. 1871 select { 1872 case r := <-c.sendChan: 1873 err := c.conn.WriteMessage(websocket.TextMessage, r.msg) 1874 if err != nil { 1875 c.Disconnect() 1876 break out 1877 } 1878 if r.doneChan != nil { 1879 r.doneChan <- true 1880 } 1881 1882 case <-c.quit: 1883 break out 1884 } 1885 } 1886 1887 // Drain any wait channels before exiting so nothing is left waiting 1888 // around to send. 1889 cleanup: 1890 for { 1891 select { 1892 case r := <-c.sendChan: 1893 if r.doneChan != nil { 1894 r.doneChan <- false 1895 } 1896 default: 1897 break cleanup 1898 } 1899 } 1900 c.wg.Done() 1901 rpcsLog.Tracef("Websocket client output handler done for %s", c.addr) 1902 } 1903 1904 // SendMessage sends the passed json to the websocket client. It is backed 1905 // by a buffered channel, so it will not block until the send channel is full. 1906 // Note however that QueueNotification must be used for sending async 1907 // notifications instead of the this function. This approach allows a limit to 1908 // the number of outstanding requests a client can make without preventing or 1909 // blocking on async notifications. 1910 func (c *wsClient) SendMessage(marshalledJSON []byte, doneChan chan bool) { 1911 // Don't send the message if disconnected. 1912 if c.Disconnected() { 1913 if doneChan != nil { 1914 doneChan <- false 1915 } 1916 return 1917 } 1918 1919 c.sendChan <- wsResponse{msg: marshalledJSON, doneChan: doneChan} 1920 } 1921 1922 // ErrClientQuit describes the error where a client send is not processed due 1923 // to the client having already been disconnected or dropped. 1924 var ErrClientQuit = errors.New("client quit") 1925 1926 // QueueNotification queues the passed notification to be sent to the websocket 1927 // client. This function, as the name implies, is only intended for 1928 // notifications since it has additional logic to prevent other subsystems, such 1929 // as the memory pool and block manager, from blocking even when the send 1930 // channel is full. 1931 // 1932 // If the client is in the process of shutting down, this function returns 1933 // ErrClientQuit. This is intended to be checked by long-running notification 1934 // handlers to stop processing if there is no more work needed to be done. 1935 func (c *wsClient) QueueNotification(marshalledJSON []byte) error { 1936 // Don't queue the message if disconnected. 1937 if c.Disconnected() { 1938 return ErrClientQuit 1939 } 1940 1941 c.ntfnChan <- marshalledJSON 1942 return nil 1943 } 1944 1945 // Disconnected returns whether or not the websocket client is disconnected. 1946 func (c *wsClient) Disconnected() bool { 1947 c.Lock() 1948 isDisconnected := c.disconnected 1949 c.Unlock() 1950 1951 return isDisconnected 1952 } 1953 1954 // Disconnect disconnects the websocket client. 1955 func (c *wsClient) Disconnect() { 1956 c.Lock() 1957 defer c.Unlock() 1958 1959 // Nothing to do if already disconnected. 1960 if c.disconnected { 1961 return 1962 } 1963 1964 rpcsLog.Tracef("Disconnecting websocket client %s", c.addr) 1965 close(c.quit) 1966 c.conn.Close() 1967 c.disconnected = true 1968 } 1969 1970 // Start begins processing input and output messages. 1971 func (c *wsClient) Start() { 1972 rpcsLog.Tracef("Starting websocket client %s", c.addr) 1973 1974 // Start processing input and output. 1975 c.wg.Add(3) 1976 go c.inHandler() 1977 go c.notificationQueueHandler() 1978 go c.outHandler() 1979 } 1980 1981 // WaitForShutdown blocks until the websocket client goroutines are stopped 1982 // and the connection is closed. 1983 func (c *wsClient) WaitForShutdown() { 1984 c.wg.Wait() 1985 } 1986 1987 // newWebsocketClient returns a new websocket client given the notification 1988 // manager, websocket connection, remote address, and whether or not the client 1989 // has already been authenticated (via HTTP Basic access authentication). The 1990 // returned client is ready to start. Once started, the client will process 1991 // incoming and outgoing messages in separate goroutines complete with queuing 1992 // and asynchrous handling for long-running operations. 1993 func newWebsocketClient(server *rpcServer, conn *websocket.Conn, 1994 remoteAddr string, authenticated bool, isAdmin bool) (*wsClient, error) { 1995 1996 sessionID, err := wire.RandomUint64() 1997 if err != nil { 1998 return nil, err 1999 } 2000 2001 client := &wsClient{ 2002 conn: conn, 2003 addr: remoteAddr, 2004 authenticated: authenticated, 2005 isAdmin: isAdmin, 2006 sessionID: sessionID, 2007 server: server, 2008 addrRequests: make(map[string]struct{}), 2009 spentRequests: make(map[wire.OutPoint]struct{}), 2010 serviceRequestSem: makeSemaphore(cfg.RPCMaxConcurrentReqs), 2011 ntfnChan: make(chan []byte, 1), // nonblocking sync 2012 sendChan: make(chan wsResponse, websocketSendBufferSize), 2013 quit: make(chan struct{}), 2014 } 2015 return client, nil 2016 } 2017 2018 // handleWebsocketHelp implements the help command for websocket connections. 2019 func handleWebsocketHelp(wsc *wsClient, icmd interface{}) (interface{}, error) { 2020 cmd, ok := icmd.(*btcjson.HelpCmd) 2021 if !ok { 2022 return nil, btcjson.ErrRPCInternal 2023 } 2024 2025 // Provide a usage overview of all commands when no specific command 2026 // was specified. 2027 var command string 2028 if cmd.Command != nil { 2029 command = *cmd.Command 2030 } 2031 if command == "" { 2032 usage, err := wsc.server.helpCacher.rpcUsage(true) 2033 if err != nil { 2034 context := "Failed to generate RPC usage" 2035 return nil, internalRPCError(err.Error(), context) 2036 } 2037 return usage, nil 2038 } 2039 2040 // Check that the command asked for is supported and implemented. 2041 // Search the list of websocket handlers as well as the main list of 2042 // handlers since help should only be provided for those cases. 2043 valid := true 2044 if _, ok := rpcHandlers[command]; !ok { 2045 if _, ok := wsHandlers[command]; !ok { 2046 valid = false 2047 } 2048 } 2049 if !valid { 2050 return nil, &btcjson.RPCError{ 2051 Code: btcjson.ErrRPCInvalidParameter, 2052 Message: "Unknown command: " + command, 2053 } 2054 } 2055 2056 // Get the help for the command. 2057 help, err := wsc.server.helpCacher.rpcMethodHelp(command) 2058 if err != nil { 2059 context := "Failed to generate help" 2060 return nil, internalRPCError(err.Error(), context) 2061 } 2062 return help, nil 2063 } 2064 2065 // handleLoadTxFilter implements the loadtxfilter command extension for 2066 // websocket connections. 2067 // 2068 // NOTE: This extension is ported from github.com/decred/dcrd 2069 func handleLoadTxFilter(wsc *wsClient, icmd interface{}) (interface{}, error) { 2070 cmd := icmd.(*btcjson.LoadTxFilterCmd) 2071 2072 outPoints := make([]wire.OutPoint, len(cmd.OutPoints)) 2073 for i := range cmd.OutPoints { 2074 hash, err := chainhash.NewHashFromStr(cmd.OutPoints[i].Hash) 2075 if err != nil { 2076 return nil, &btcjson.RPCError{ 2077 Code: btcjson.ErrRPCInvalidParameter, 2078 Message: err.Error(), 2079 } 2080 } 2081 outPoints[i] = wire.OutPoint{ 2082 Hash: *hash, 2083 Index: cmd.OutPoints[i].Index, 2084 } 2085 } 2086 2087 params := wsc.server.cfg.ChainParams 2088 2089 wsc.Lock() 2090 if cmd.Reload || wsc.filterData == nil { 2091 wsc.filterData = newWSClientFilter(cmd.Addresses, outPoints, 2092 params) 2093 wsc.Unlock() 2094 } else { 2095 wsc.Unlock() 2096 2097 wsc.filterData.mu.Lock() 2098 for _, a := range cmd.Addresses { 2099 wsc.filterData.addAddressStr(a, params) 2100 } 2101 for i := range outPoints { 2102 wsc.filterData.addUnspentOutPoint(&outPoints[i]) 2103 } 2104 wsc.filterData.mu.Unlock() 2105 } 2106 2107 return nil, nil 2108 } 2109 2110 // handleNotifyBlocks implements the notifyblocks command extension for 2111 // websocket connections. 2112 func handleNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { 2113 wsc.server.ntfnMgr.RegisterBlockUpdates(wsc) 2114 return nil, nil 2115 } 2116 2117 // handleSession implements the session command extension for websocket 2118 // connections. 2119 func handleSession(wsc *wsClient, icmd interface{}) (interface{}, error) { 2120 return &btcjson.SessionResult{SessionID: wsc.sessionID}, nil 2121 } 2122 2123 // handleStopNotifyBlocks implements the stopnotifyblocks command extension for 2124 // websocket connections. 2125 func handleStopNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { 2126 wsc.server.ntfnMgr.UnregisterBlockUpdates(wsc) 2127 return nil, nil 2128 } 2129 2130 // handleNotifySpent implements the notifyspent command extension for 2131 // websocket connections. 2132 func handleNotifySpent(wsc *wsClient, icmd interface{}) (interface{}, error) { 2133 cmd, ok := icmd.(*btcjson.NotifySpentCmd) 2134 if !ok { 2135 return nil, btcjson.ErrRPCInternal 2136 } 2137 2138 outpoints, err := deserializeOutpoints(cmd.OutPoints) 2139 if err != nil { 2140 return nil, err 2141 } 2142 2143 wsc.server.ntfnMgr.RegisterSpentRequests(wsc, outpoints) 2144 return nil, nil 2145 } 2146 2147 // handleNotifyNewTransations implements the notifynewtransactions command 2148 // extension for websocket connections. 2149 func handleNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) { 2150 cmd, ok := icmd.(*btcjson.NotifyNewTransactionsCmd) 2151 if !ok { 2152 return nil, btcjson.ErrRPCInternal 2153 } 2154 2155 wsc.verboseTxUpdates = cmd.Verbose != nil && *cmd.Verbose 2156 wsc.server.ntfnMgr.RegisterNewMempoolTxsUpdates(wsc) 2157 return nil, nil 2158 } 2159 2160 // handleStopNotifyNewTransations implements the stopnotifynewtransactions 2161 // command extension for websocket connections. 2162 func handleStopNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) { 2163 wsc.server.ntfnMgr.UnregisterNewMempoolTxsUpdates(wsc) 2164 return nil, nil 2165 } 2166 2167 // handleNotifyReceived implements the notifyreceived command extension for 2168 // websocket connections. 2169 func handleNotifyReceived(wsc *wsClient, icmd interface{}) (interface{}, error) { 2170 cmd, ok := icmd.(*btcjson.NotifyReceivedCmd) 2171 if !ok { 2172 return nil, btcjson.ErrRPCInternal 2173 } 2174 2175 // Decode addresses to validate input, but the strings slice is used 2176 // directly if these are all ok. 2177 err := checkAddressValidity(cmd.Addresses, wsc.server.cfg.ChainParams) 2178 if err != nil { 2179 return nil, err 2180 } 2181 2182 wsc.server.ntfnMgr.RegisterTxOutAddressRequests(wsc, cmd.Addresses) 2183 return nil, nil 2184 } 2185 2186 // handleStopNotifySpent implements the stopnotifyspent command extension for 2187 // websocket connections. 2188 func handleStopNotifySpent(wsc *wsClient, icmd interface{}) (interface{}, error) { 2189 cmd, ok := icmd.(*btcjson.StopNotifySpentCmd) 2190 if !ok { 2191 return nil, btcjson.ErrRPCInternal 2192 } 2193 2194 outpoints, err := deserializeOutpoints(cmd.OutPoints) 2195 if err != nil { 2196 return nil, err 2197 } 2198 2199 for _, outpoint := range outpoints { 2200 wsc.server.ntfnMgr.UnregisterSpentRequest(wsc, outpoint) 2201 } 2202 2203 return nil, nil 2204 } 2205 2206 // handleStopNotifyReceived implements the stopnotifyreceived command extension 2207 // for websocket connections. 2208 func handleStopNotifyReceived(wsc *wsClient, icmd interface{}) (interface{}, error) { 2209 cmd, ok := icmd.(*btcjson.StopNotifyReceivedCmd) 2210 if !ok { 2211 return nil, btcjson.ErrRPCInternal 2212 } 2213 2214 // Decode addresses to validate input, but the strings slice is used 2215 // directly if these are all ok. 2216 err := checkAddressValidity(cmd.Addresses, wsc.server.cfg.ChainParams) 2217 if err != nil { 2218 return nil, err 2219 } 2220 2221 for _, addr := range cmd.Addresses { 2222 wsc.server.ntfnMgr.UnregisterTxOutAddressRequest(wsc, addr) 2223 } 2224 2225 return nil, nil 2226 } 2227 2228 // checkAddressValidity checks the validity of each address in the passed 2229 // string slice. It does this by attempting to decode each address using the 2230 // current active network parameters. If any single address fails to decode 2231 // properly, the function returns an error. Otherwise, nil is returned. 2232 func checkAddressValidity(addrs []string, params *chaincfg.Params) error { 2233 for _, addr := range addrs { 2234 _, err := palcutil.DecodeAddress(addr, params) 2235 if err != nil { 2236 return &btcjson.RPCError{ 2237 Code: btcjson.ErrRPCInvalidAddressOrKey, 2238 Message: fmt.Sprintf("Invalid address or key: %v", 2239 addr), 2240 } 2241 } 2242 } 2243 return nil 2244 } 2245 2246 // deserializeOutpoints deserializes each serialized outpoint. 2247 func deserializeOutpoints(serializedOuts []btcjson.OutPoint) ([]*wire.OutPoint, error) { 2248 outpoints := make([]*wire.OutPoint, 0, len(serializedOuts)) 2249 for i := range serializedOuts { 2250 blockHash, err := chainhash.NewHashFromStr(serializedOuts[i].Hash) 2251 if err != nil { 2252 return nil, rpcDecodeHexError(serializedOuts[i].Hash) 2253 } 2254 index := serializedOuts[i].Index 2255 outpoints = append(outpoints, wire.NewOutPoint(blockHash, index)) 2256 } 2257 2258 return outpoints, nil 2259 } 2260 2261 type rescanKeys struct { 2262 addrs map[string]struct{} 2263 unspent map[wire.OutPoint]struct{} 2264 } 2265 2266 // unspentSlice returns a slice of currently-unspent outpoints for the rescan 2267 // lookup keys. This is primarily intended to be used to register outpoints 2268 // for continuous notifications after a rescan has completed. 2269 func (r *rescanKeys) unspentSlice() []*wire.OutPoint { 2270 ops := make([]*wire.OutPoint, 0, len(r.unspent)) 2271 for op := range r.unspent { 2272 opCopy := op 2273 ops = append(ops, &opCopy) 2274 } 2275 return ops 2276 } 2277 2278 // ErrRescanReorg defines the error that is returned when an unrecoverable 2279 // reorganize is detected during a rescan. 2280 var ErrRescanReorg = btcjson.RPCError{ 2281 Code: btcjson.ErrRPCDatabase, 2282 Message: "Reorganize", 2283 } 2284 2285 // rescanBlock rescans all transactions in a single block. This is a helper 2286 // function for handleRescan. 2287 func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *palcutil.Block) { 2288 for _, tx := range blk.Transactions() { 2289 // Hexadecimal representation of this tx. Only created if 2290 // needed, and reused for later notifications if already made. 2291 var txHex string 2292 2293 // All inputs and outputs must be iterated through to correctly 2294 // modify the unspent map, however, just a single notification 2295 // for any matching transaction inputs or outputs should be 2296 // created and sent. 2297 spentNotified := false 2298 recvNotified := false 2299 2300 // notifySpend is a closure we'll use when we first detect that 2301 // a transactions spends an outpoint/script in our filter list. 2302 notifySpend := func() error { 2303 if txHex == "" { 2304 txHex = txHexString(tx.MsgTx()) 2305 } 2306 marshalledJSON, err := newRedeemingTxNotification( 2307 txHex, tx.Index(), blk, 2308 ) 2309 if err != nil { 2310 return fmt.Errorf("unable to marshal "+ 2311 "btcjson.RedeeminTxNtfn: %v", err) 2312 } 2313 2314 return wsc.QueueNotification(marshalledJSON) 2315 } 2316 2317 // We'll start by iterating over the transaction's inputs to 2318 // determine if it spends an outpoint/script in our filter list. 2319 for _, txin := range tx.MsgTx().TxIn { 2320 // If it spends an outpoint, we'll dispatch a spend 2321 // notification for the transaction. 2322 if _, ok := lookups.unspent[txin.PreviousOutPoint]; ok { 2323 delete(lookups.unspent, txin.PreviousOutPoint) 2324 2325 if spentNotified { 2326 continue 2327 } 2328 2329 err := notifySpend() 2330 2331 // Stop the rescan early if the websocket client 2332 // disconnected. 2333 if err == ErrClientQuit { 2334 return 2335 } 2336 if err != nil { 2337 rpcsLog.Errorf("Unable to notify "+ 2338 "redeeming transaction %v: %v", 2339 tx.Hash(), err) 2340 continue 2341 } 2342 2343 spentNotified = true 2344 } 2345 2346 // We'll also recompute the pkScript the input is 2347 // attempting to spend to determine whether it is 2348 // relevant to us. 2349 pkScript, err := txscript.ComputePkScript( 2350 txin.SignatureScript, txin.Witness, 2351 ) 2352 if err != nil { 2353 continue 2354 } 2355 addr, err := pkScript.Address(wsc.server.cfg.ChainParams) 2356 if err != nil { 2357 continue 2358 } 2359 2360 // If it is, we'll also dispatch a spend notification 2361 // for this transaction if we haven't already. 2362 if _, ok := lookups.addrs[addr.String()]; ok { 2363 if spentNotified { 2364 continue 2365 } 2366 2367 err := notifySpend() 2368 2369 // Stop the rescan early if the websocket client 2370 // disconnected. 2371 if err == ErrClientQuit { 2372 return 2373 } 2374 if err != nil { 2375 rpcsLog.Errorf("Unable to notify "+ 2376 "redeeming transaction %v: %v", 2377 tx.Hash(), err) 2378 continue 2379 } 2380 2381 spentNotified = true 2382 } 2383 } 2384 2385 for txOutIdx, txout := range tx.MsgTx().TxOut { 2386 _, addrs, _, _ := txscript.ExtractPkScriptAddrs( 2387 txout.PkScript, wsc.server.cfg.ChainParams) 2388 2389 for _, addr := range addrs { 2390 if _, ok := lookups.addrs[addr.String()]; !ok { 2391 continue 2392 } 2393 2394 outpoint := wire.OutPoint{ 2395 Hash: *tx.Hash(), 2396 Index: uint32(txOutIdx), 2397 } 2398 lookups.unspent[outpoint] = struct{}{} 2399 2400 if recvNotified { 2401 continue 2402 } 2403 2404 if txHex == "" { 2405 txHex = txHexString(tx.MsgTx()) 2406 } 2407 ntfn := btcjson.NewRecvTxNtfn(txHex, 2408 blockDetails(blk, tx.Index())) 2409 2410 marshalledJSON, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, ntfn) 2411 if err != nil { 2412 rpcsLog.Errorf("Failed to marshal recvtx notification: %v", err) 2413 return 2414 } 2415 2416 err = wsc.QueueNotification(marshalledJSON) 2417 // Stop the rescan early if the websocket client 2418 // disconnected. 2419 if err == ErrClientQuit { 2420 return 2421 } 2422 recvNotified = true 2423 } 2424 } 2425 } 2426 } 2427 2428 // rescanBlockFilter rescans a block for any relevant transactions for the 2429 // passed lookup keys. Any discovered transactions are returned hex encoded as 2430 // a string slice. 2431 // 2432 // NOTE: This extension is ported from github.com/decred/dcrd 2433 func rescanBlockFilter(filter *wsClientFilter, block *palcutil.Block, params *chaincfg.Params) []string { 2434 var transactions []string 2435 2436 filter.mu.Lock() 2437 for _, tx := range block.Transactions() { 2438 msgTx := tx.MsgTx() 2439 2440 // Keep track of whether the transaction has already been added 2441 // to the result. It shouldn't be added twice. 2442 added := false 2443 2444 // Scan inputs if not a coinbase transaction. 2445 if !blockchain.IsCoinBaseTx(msgTx) { 2446 for _, input := range msgTx.TxIn { 2447 if !filter.existsUnspentOutPoint(&input.PreviousOutPoint) { 2448 continue 2449 } 2450 if !added { 2451 transactions = append( 2452 transactions, 2453 txHexString(msgTx)) 2454 added = true 2455 } 2456 } 2457 } 2458 2459 // Scan outputs. 2460 for i, output := range msgTx.TxOut { 2461 _, addrs, _, err := txscript.ExtractPkScriptAddrs( 2462 output.PkScript, params) 2463 if err != nil { 2464 continue 2465 } 2466 for _, a := range addrs { 2467 if !filter.existsAddress(a) { 2468 continue 2469 } 2470 2471 op := wire.OutPoint{ 2472 Hash: *tx.Hash(), 2473 Index: uint32(i), 2474 } 2475 filter.addUnspentOutPoint(&op) 2476 2477 if !added { 2478 transactions = append( 2479 transactions, 2480 txHexString(msgTx)) 2481 added = true 2482 } 2483 } 2484 } 2485 } 2486 filter.mu.Unlock() 2487 2488 return transactions 2489 } 2490 2491 // handleRescanBlocks implements the rescanblocks command extension for 2492 // websocket connections. 2493 // 2494 // NOTE: This extension is ported from github.com/decred/dcrd 2495 func handleRescanBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { 2496 cmd, ok := icmd.(*btcjson.RescanBlocksCmd) 2497 if !ok { 2498 return nil, btcjson.ErrRPCInternal 2499 } 2500 2501 // Load client's transaction filter. Must exist in order to continue. 2502 wsc.Lock() 2503 filter := wsc.filterData 2504 wsc.Unlock() 2505 if filter == nil { 2506 return nil, &btcjson.RPCError{ 2507 Code: btcjson.ErrRPCMisc, 2508 Message: "Transaction filter must be loaded before rescanning", 2509 } 2510 } 2511 2512 blockHashes := make([]*chainhash.Hash, len(cmd.BlockHashes)) 2513 2514 for i := range cmd.BlockHashes { 2515 hash, err := chainhash.NewHashFromStr(cmd.BlockHashes[i]) 2516 if err != nil { 2517 return nil, err 2518 } 2519 blockHashes[i] = hash 2520 } 2521 2522 discoveredData := make([]btcjson.RescannedBlock, 0, len(blockHashes)) 2523 2524 // Iterate over each block in the request and rescan. When a block 2525 // contains relevant transactions, add it to the response. 2526 bc := wsc.server.cfg.Chain 2527 params := wsc.server.cfg.ChainParams 2528 var lastBlockHash *chainhash.Hash 2529 for i := range blockHashes { 2530 block, err := bc.BlockByHash(blockHashes[i]) 2531 if err != nil { 2532 return nil, &btcjson.RPCError{ 2533 Code: btcjson.ErrRPCBlockNotFound, 2534 Message: "Failed to fetch block: " + err.Error(), 2535 } 2536 } 2537 if lastBlockHash != nil && block.MsgBlock().Header.PrevBlock != *lastBlockHash { 2538 return nil, &btcjson.RPCError{ 2539 Code: btcjson.ErrRPCInvalidParameter, 2540 Message: fmt.Sprintf("Block %v is not a child of %v", 2541 blockHashes[i], lastBlockHash), 2542 } 2543 } 2544 lastBlockHash = blockHashes[i] 2545 2546 transactions := rescanBlockFilter(filter, block, params) 2547 if len(transactions) != 0 { 2548 discoveredData = append(discoveredData, btcjson.RescannedBlock{ 2549 Hash: cmd.BlockHashes[i], 2550 Transactions: transactions, 2551 }) 2552 } 2553 } 2554 2555 return &discoveredData, nil 2556 } 2557 2558 // recoverFromReorg attempts to recover from a detected reorganize during a 2559 // rescan. It fetches a new range of block shas from the database and 2560 // verifies that the new range of blocks is on the same fork as a previous 2561 // range of blocks. If this condition does not hold true, the JSON-RPC error 2562 // for an unrecoverable reorganize is returned. 2563 func recoverFromReorg(chain *blockchain.BlockChain, minBlock, maxBlock int32, 2564 lastBlock *chainhash.Hash) ([]chainhash.Hash, error) { 2565 2566 hashList, err := chain.HeightRange(minBlock, maxBlock) 2567 if err != nil { 2568 rpcsLog.Errorf("Error looking up block range: %v", err) 2569 return nil, &btcjson.RPCError{ 2570 Code: btcjson.ErrRPCDatabase, 2571 Message: "Database error: " + err.Error(), 2572 } 2573 } 2574 if lastBlock == nil || len(hashList) == 0 { 2575 return hashList, nil 2576 } 2577 2578 blk, err := chain.BlockByHash(&hashList[0]) 2579 if err != nil { 2580 rpcsLog.Errorf("Error looking up possibly reorged block: %v", 2581 err) 2582 return nil, &btcjson.RPCError{ 2583 Code: btcjson.ErrRPCDatabase, 2584 Message: "Database error: " + err.Error(), 2585 } 2586 } 2587 jsonErr := descendantBlock(lastBlock, blk) 2588 if jsonErr != nil { 2589 return nil, jsonErr 2590 } 2591 return hashList, nil 2592 } 2593 2594 // descendantBlock returns the appropriate JSON-RPC error if a current block 2595 // fetched during a reorganize is not a direct child of the parent block hash. 2596 func descendantBlock(prevHash *chainhash.Hash, curBlock *palcutil.Block) error { 2597 curHash := &curBlock.MsgBlock().Header.PrevBlock 2598 if !prevHash.IsEqual(curHash) { 2599 rpcsLog.Errorf("Stopping rescan for reorged block %v "+ 2600 "(replaced by block %v)", prevHash, curHash) 2601 return &ErrRescanReorg 2602 } 2603 return nil 2604 } 2605 2606 // scanBlockChunks executes a rescan in chunked stages. We do this to limit the 2607 // amount of memory that we'll allocate to a given rescan. Every so often, 2608 // we'll send back a rescan progress notification to the websockets client. The 2609 // final block and block hash that we've scanned will be returned. 2610 func scanBlockChunks(wsc *wsClient, cmd *btcjson.RescanCmd, lookups *rescanKeys, minBlock, 2611 maxBlock int32, chain *blockchain.BlockChain) ( 2612 *palcutil.Block, *chainhash.Hash, error) { 2613 2614 // lastBlock and lastBlockHash track the previously-rescanned block. 2615 // They equal nil when no previous blocks have been rescanned. 2616 var ( 2617 lastBlock *palcutil.Block 2618 lastBlockHash *chainhash.Hash 2619 ) 2620 2621 // A ticker is created to wait at least 10 seconds before notifying the 2622 // websocket client of the current progress completed by the rescan. 2623 ticker := time.NewTicker(10 * time.Second) 2624 defer ticker.Stop() 2625 2626 // Instead of fetching all block shas at once, fetch in smaller chunks 2627 // to ensure large rescans consume a limited amount of memory. 2628 fetchRange: 2629 for minBlock < maxBlock { 2630 // Limit the max number of hashes to fetch at once to the 2631 // maximum number of items allowed in a single inventory. 2632 // This value could be higher since it's not creating inventory 2633 // messages, but this mirrors the limiting logic used in the 2634 // peer-to-peer protocol. 2635 maxLoopBlock := maxBlock 2636 if maxLoopBlock-minBlock > wire.MaxInvPerMsg { 2637 maxLoopBlock = minBlock + wire.MaxInvPerMsg 2638 } 2639 hashList, err := chain.HeightRange(minBlock, maxLoopBlock) 2640 if err != nil { 2641 rpcsLog.Errorf("Error looking up block range: %v", err) 2642 return nil, nil, &btcjson.RPCError{ 2643 Code: btcjson.ErrRPCDatabase, 2644 Message: "Database error: " + err.Error(), 2645 } 2646 } 2647 if len(hashList) == 0 { 2648 // The rescan is finished if no blocks hashes for this 2649 // range were successfully fetched and a stop block 2650 // was provided. 2651 if maxBlock != math.MaxInt32 { 2652 break 2653 } 2654 2655 // If the rescan is through the current block, set up 2656 // the client to continue to receive notifications 2657 // regarding all rescanned addresses and the current set 2658 // of unspent outputs. 2659 // 2660 // This is done safely by temporarily grabbing exclusive 2661 // access of the block manager. If no more blocks have 2662 // been attached between this pause and the fetch above, 2663 // then it is safe to register the websocket client for 2664 // continuous notifications if necessary. Otherwise, 2665 // continue the fetch loop again to rescan the new 2666 // blocks (or error due to an irrecoverable reorganize). 2667 pauseGuard := wsc.server.cfg.SyncMgr.Pause() 2668 best := wsc.server.cfg.Chain.BestSnapshot() 2669 curHash := &best.Hash 2670 again := true 2671 if lastBlockHash == nil || *lastBlockHash == *curHash { 2672 again = false 2673 n := wsc.server.ntfnMgr 2674 n.RegisterSpentRequests(wsc, lookups.unspentSlice()) 2675 n.RegisterTxOutAddressRequests(wsc, cmd.Addresses) 2676 } 2677 close(pauseGuard) 2678 if err != nil { 2679 rpcsLog.Errorf("Error fetching best block "+ 2680 "hash: %v", err) 2681 return nil, nil, &btcjson.RPCError{ 2682 Code: btcjson.ErrRPCDatabase, 2683 Message: "Database error: " + 2684 err.Error(), 2685 } 2686 } 2687 if again { 2688 continue 2689 } 2690 break 2691 } 2692 2693 loopHashList: 2694 for i := range hashList { 2695 blk, err := chain.BlockByHash(&hashList[i]) 2696 if err != nil { 2697 // Only handle reorgs if a block could not be 2698 // found for the hash. 2699 if dbErr, ok := err.(database.Error); !ok || 2700 dbErr.ErrorCode != database.ErrBlockNotFound { 2701 2702 rpcsLog.Errorf("Error looking up "+ 2703 "block: %v", err) 2704 return nil, nil, &btcjson.RPCError{ 2705 Code: btcjson.ErrRPCDatabase, 2706 Message: "Database error: " + 2707 err.Error(), 2708 } 2709 } 2710 2711 // If an absolute max block was specified, don't 2712 // attempt to handle the reorg. 2713 if maxBlock != math.MaxInt32 { 2714 rpcsLog.Errorf("Stopping rescan for "+ 2715 "reorged block %v", 2716 cmd.EndBlock) 2717 return nil, nil, &ErrRescanReorg 2718 } 2719 2720 // If the lookup for the previously valid block 2721 // hash failed, there may have been a reorg. 2722 // Fetch a new range of block hashes and verify 2723 // that the previously processed block (if there 2724 // was any) still exists in the database. If it 2725 // doesn't, we error. 2726 // 2727 // A goto is used to branch executation back to 2728 // before the range was evaluated, as it must be 2729 // reevaluated for the new hashList. 2730 minBlock += int32(i) 2731 hashList, err = recoverFromReorg( 2732 chain, minBlock, maxBlock, lastBlockHash, 2733 ) 2734 if err != nil { 2735 return nil, nil, err 2736 } 2737 if len(hashList) == 0 { 2738 break fetchRange 2739 } 2740 goto loopHashList 2741 } 2742 if i == 0 && lastBlockHash != nil { 2743 // Ensure the new hashList is on the same fork 2744 // as the last block from the old hashList. 2745 jsonErr := descendantBlock(lastBlockHash, blk) 2746 if jsonErr != nil { 2747 return nil, nil, jsonErr 2748 } 2749 } 2750 2751 // A select statement is used to stop rescans if the 2752 // client requesting the rescan has disconnected. 2753 select { 2754 case <-wsc.quit: 2755 rpcsLog.Debugf("Stopped rescan at height %v "+ 2756 "for disconnected client", blk.Height()) 2757 return nil, nil, nil 2758 default: 2759 rescanBlock(wsc, lookups, blk) 2760 lastBlock = blk 2761 lastBlockHash = blk.Hash() 2762 } 2763 2764 // Periodically notify the client of the progress 2765 // completed. Continue with next block if no progress 2766 // notification is needed yet. 2767 select { 2768 case <-ticker.C: // fallthrough 2769 default: 2770 continue 2771 } 2772 2773 n := btcjson.NewRescanProgressNtfn( 2774 hashList[i].String(), blk.Height(), 2775 blk.MsgBlock().Header.Timestamp.Unix(), 2776 ) 2777 mn, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, n) 2778 if err != nil { 2779 rpcsLog.Errorf("Failed to marshal rescan "+ 2780 "progress notification: %v", err) 2781 continue 2782 } 2783 2784 if err = wsc.QueueNotification(mn); err == ErrClientQuit { 2785 // Finished if the client disconnected. 2786 rpcsLog.Debugf("Stopped rescan at height %v "+ 2787 "for disconnected client", blk.Height()) 2788 return nil, nil, nil 2789 } 2790 } 2791 2792 minBlock += int32(len(hashList)) 2793 } 2794 2795 return lastBlock, lastBlockHash, nil 2796 } 2797 2798 // handleRescan implements the rescan command extension for websocket 2799 // connections. 2800 // 2801 // NOTE: This does not smartly handle reorgs, and fixing requires database 2802 // changes (for safe, concurrent access to full block ranges, and support 2803 // for other chains than the best chain). It will, however, detect whether 2804 // a reorg removed a block that was previously processed, and result in the 2805 // handler erroring. Clients must handle this by finding a block still in 2806 // the chain (perhaps from a rescanprogress notification) to resume their 2807 // rescan. 2808 func handleRescan(wsc *wsClient, icmd interface{}) (interface{}, error) { 2809 cmd, ok := icmd.(*btcjson.RescanCmd) 2810 if !ok { 2811 return nil, btcjson.ErrRPCInternal 2812 } 2813 2814 outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints)) 2815 for i := range cmd.OutPoints { 2816 cmdOutpoint := &cmd.OutPoints[i] 2817 blockHash, err := chainhash.NewHashFromStr(cmdOutpoint.Hash) 2818 if err != nil { 2819 return nil, rpcDecodeHexError(cmdOutpoint.Hash) 2820 } 2821 outpoint := wire.NewOutPoint(blockHash, cmdOutpoint.Index) 2822 outpoints = append(outpoints, outpoint) 2823 } 2824 2825 numAddrs := len(cmd.Addresses) 2826 if numAddrs == 1 { 2827 rpcsLog.Info("Beginning rescan for 1 address") 2828 } else { 2829 rpcsLog.Infof("Beginning rescan for %d addresses", numAddrs) 2830 } 2831 2832 // Build lookup maps. 2833 lookups := rescanKeys{ 2834 addrs: map[string]struct{}{}, 2835 unspent: map[wire.OutPoint]struct{}{}, 2836 } 2837 for _, addrStr := range cmd.Addresses { 2838 lookups.addrs[addrStr] = struct{}{} 2839 } 2840 for _, outpoint := range outpoints { 2841 lookups.unspent[*outpoint] = struct{}{} 2842 } 2843 2844 chain := wsc.server.cfg.Chain 2845 2846 minBlockHash, err := chainhash.NewHashFromStr(cmd.BeginBlock) 2847 if err != nil { 2848 return nil, rpcDecodeHexError(cmd.BeginBlock) 2849 } 2850 minBlock, err := chain.BlockHeightByHash(minBlockHash) 2851 if err != nil { 2852 return nil, &btcjson.RPCError{ 2853 Code: btcjson.ErrRPCBlockNotFound, 2854 Message: "Error getting block: " + err.Error(), 2855 } 2856 } 2857 2858 maxBlock := int32(math.MaxInt32) 2859 if cmd.EndBlock != nil { 2860 maxBlockHash, err := chainhash.NewHashFromStr(*cmd.EndBlock) 2861 if err != nil { 2862 return nil, rpcDecodeHexError(*cmd.EndBlock) 2863 } 2864 maxBlock, err = chain.BlockHeightByHash(maxBlockHash) 2865 if err != nil { 2866 return nil, &btcjson.RPCError{ 2867 Code: btcjson.ErrRPCBlockNotFound, 2868 Message: "Error getting block: " + err.Error(), 2869 } 2870 } 2871 } 2872 2873 var ( 2874 lastBlock *palcutil.Block 2875 lastBlockHash *chainhash.Hash 2876 ) 2877 if len(lookups.addrs) != 0 || len(lookups.unspent) != 0 { 2878 // With all the arguments parsed, we'll execute our chunked rescan 2879 // which will notify the clients of any address deposits or output 2880 // spends. 2881 lastBlock, lastBlockHash, err = scanBlockChunks( 2882 wsc, cmd, &lookups, minBlock, maxBlock, chain, 2883 ) 2884 if err != nil { 2885 return nil, err 2886 } 2887 2888 // If the last block is nil, then this means that the client 2889 // disconnected mid-rescan. As a result, we don't need to send 2890 // anything back to them. 2891 if lastBlock == nil { 2892 return nil, nil 2893 } 2894 } else { 2895 rpcsLog.Infof("Skipping rescan as client has no addrs/utxos") 2896 2897 // If we didn't actually do a rescan, then we'll give the 2898 // client our best known block within the final rescan finished 2899 // notification. 2900 chainTip := chain.BestSnapshot() 2901 lastBlockHash = &chainTip.Hash 2902 lastBlock, err = chain.BlockByHash(lastBlockHash) 2903 if err != nil { 2904 return nil, &btcjson.RPCError{ 2905 Code: btcjson.ErrRPCBlockNotFound, 2906 Message: "Error getting block: " + err.Error(), 2907 } 2908 } 2909 } 2910 2911 // Notify websocket client of the finished rescan. Due to how btcd 2912 // asynchronously queues notifications to not block calling code, 2913 // there is no guarantee that any of the notifications created during 2914 // rescan (such as rescanprogress, recvtx and redeemingtx) will be 2915 // received before the rescan RPC returns. Therefore, another method 2916 // is needed to safely inform clients that all rescan notifications have 2917 // been sent. 2918 n := btcjson.NewRescanFinishedNtfn( 2919 lastBlockHash.String(), lastBlock.Height(), 2920 lastBlock.MsgBlock().Header.Timestamp.Unix(), 2921 ) 2922 if mn, err := btcjson.MarshalCmd(btcjson.RpcVersion1, nil, n); err != nil { 2923 rpcsLog.Errorf("Failed to marshal rescan finished "+ 2924 "notification: %v", err) 2925 } else { 2926 // The rescan is finished, so we don't care whether the client 2927 // has disconnected at this point, so discard error. 2928 _ = wsc.QueueNotification(mn) 2929 } 2930 2931 rpcsLog.Info("Finished rescan") 2932 return nil, nil 2933 } 2934 2935 func init() { 2936 wsHandlers = wsHandlersBeforeInit 2937 }