Exemple #1
0
// handleNotifyNewTXs implements the notifynewtxs command extension for
// websocket connections.
func handleNotifyNewTXs(s *rpcServer, cmd btcjson.Cmd, wallet walletChan) error {
	id := cmd.Id()
	reply := &btcjson.Reply{Id: &id}

	notifyCmd, ok := cmd.(*btcws.NotifyNewTXsCmd)
	if !ok {
		return btcjson.ErrInternal
	}

	for _, addr := range notifyCmd.Addresses {
		addr, err := btcutil.DecodeAddr(addr)
		if err != nil {
			return fmt.Errorf("cannot decode address: %v", err)
		}

		// TODO(jrick) Notifing for non-P2PKH addresses is currently
		// unsuported.
		if _, ok := addr.(*btcutil.AddressPubKeyHash); !ok {
			return fmt.Errorf("address is not P2PKH: %v", addr.EncodeAddress())
		}

		s.ws.AddTxRequest(wallet, addr.EncodeAddress())
	}

	mreply, _ := json.Marshal(reply)
	wallet <- mreply
	return nil
}
Exemple #2
0
// sendCmd sends the passed command to the associated server and returns a
// response channel on which the reply will be deliver at some point in the
// future.  It handles both websocket and HTTP POST mode depending on the
// configuration of the client.
func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response {
	// Choose which marshal and send function to use depending on whether
	// the client running in HTTP POST mode or not.  When running in HTTP
	// POST mode, the command is issued via an HTTP client.  Otherwise,
	// the command is issued via the asynchronous websocket channels.
	responseChan := make(chan *response, 1)
	if c.config.HttpPostMode {
		c.marshalAndSendPost(cmd, responseChan)
		return responseChan
	}

	// Check whether the websocket connection has never been established,
	// in which case the handler goroutines are not running.
	select {
	case <-c.connEstablished:
	default:
		responseChan <- &response{err: ErrClientNotConnected}
		return responseChan
	}

	err := c.addRequest(cmd.Id().(uint64), &jsonRequest{
		cmd:          cmd,
		responseChan: responseChan,
	})
	if err != nil {
		responseChan <- &response{err: err}
		return responseChan
	}
	c.marshalAndSend(cmd, responseChan)
	return responseChan
}
Exemple #3
0
// standardCmdReply checks that a parsed command is a standard
// Bitcoin JSON-RPC command and runs the proper handler to reply to the
// command.
func standardCmdReply(cmd btcjson.Cmd, s *rpcServer) (reply btcjson.Reply) {
	id := cmd.Id()
	reply.Id = &id

	handler, ok := rpcHandlers[cmd.Method()]
	if !ok {
		reply.Error = &btcjson.ErrMethodNotFound
		return reply
	}

	result, err := handler(s, cmd)
	if err != nil {
		jsonErr, ok := err.(btcjson.Error)
		if !ok {
			// In the case where we did not have a btcjson
			// error to begin with, make a new one to send,
			// but this really should not happen.
			jsonErr = btcjson.Error{
				Code:    btcjson.ErrInternal.Code,
				Message: err.Error(),
			}
		}
		reply.Error = &jsonErr
	} else {
		reply.Result = result
	}
	return reply
}
Exemple #4
0
// respondToAnyCmd checks that a parsed command is a standard or
// extension JSON-RPC command and runs the proper handler to reply to
// the command.  Any and all responses are sent to the wallet from
// this function.
func respondToAnyCmd(cmd btcjson.Cmd, s *rpcServer, wallet walletChan) {
	// Lookup the websocket extension for the command and if it doesn't
	// exist fallback to handling the command as a standard command.
	wsHandler, ok := wsHandlers[cmd.Method()]
	if !ok {
		reply := standardCmdReply(cmd, s)
		mreply, _ := json.Marshal(reply)
		wallet <- mreply
		return
	}

	// Call the appropriate handler which responds unless there was an
	// error in which case the error is marshalled and sent here.
	if err := wsHandler(s, cmd, wallet); err != nil {
		var reply btcjson.Reply
		jsonErr, ok := err.(btcjson.Error)
		if ok {
			reply.Error = &jsonErr
			mreply, _ := json.Marshal(reply)
			wallet <- mreply
			return
		}

		// In the case where we did not have a btcjson
		// error to begin with, make a new one to send,
		// but this really should not happen.
		jsonErr = btcjson.Error{
			Code:    btcjson.ErrInternal.Code,
			Message: err.Error(),
		}
		reply.Error = &jsonErr
		mreply, _ := json.Marshal(reply)
		wallet <- mreply
	}
}
// marshalAndSendPost marshals the passed command to JSON-RPC and sends it to
// the server by issuing an HTTP POST request and returns a response channel
// on which the reply will be delivered.  Typically a new connection is opened
// and closed for each command when using this method, however, the underlying
// HTTP client might coalesce multiple commands depending on several factors
// including the remote server configuration.
func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureResult) {
	marshalledJSON, err := json.Marshal(cmd)
	if err != nil {
		responseChan <- &futureResult{reply: nil, err: err}
		return
	}

	// Generate a request to the configured RPC server.
	protocol := "http"
	if !c.config.DisableTLS {
		protocol = "https"
	}
	url := protocol + "://" + c.config.Host
	req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON))
	if err != nil {
		responseChan <- &futureResult{reply: nil, err: err}
		return
	}
	req.Close = true
	req.Header.Set("Content-Type", "application/json")

	// Configure basic access authorization.
	req.SetBasicAuth(c.config.User, c.config.Pass)

	log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id())
	c.sendPostRequest(req, cmd, responseChan)
}
Exemple #6
0
// handleBtcdConnectedNtfn handles btcwallet btcdconnected notifications,
// updating the GUI with info about whether btcd is connected to btcwallet
// or not.
func handleBtcdConnectedNtfn(n btcjson.Cmd) {
	bcn, ok := n.(*btcws.BtcdConnectedNtfn)
	if !ok {
		log.Printf("[ERR] %v handler: unexpected type", n.Method())
		return
	}

	updateChans.btcdConnected <- bcn.Connected
}
Exemple #7
0
// handleBlockDisconnectedNtfn handles btcd/btcwallet blockdisconnected
// notifications resulting from blocks disconnected from the main chain.
//
// TODO(jrick): handle this by rolling back tx history and balances.
func handleBlockDisconnectedNtfn(n btcjson.Cmd) {
	bdn, ok := n.(*btcws.BlockDisconnectedNtfn)
	if !ok {
		log.Printf("[ERR] %v handler: unexpected type", n.Method())
		return
	}

	// TODO
	_ = bdn
}
// marshalAndSend marshals the passed command to JSON-RPC and sends it to the
// server.  It returns a response channel on which the reply will be delivered.
func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult) {
	marshalledJSON, err := json.Marshal(cmd)
	if err != nil {
		responseChan <- &futureResult{reply: nil, err: err}
		return
	}

	log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id())
	c.sendMessage(marshalledJSON)
}
Exemple #9
0
// DeferToBtcd sends a marshaled JSON-RPC request to btcd and returns
// the reply.
func DeferToBtcd(cmd btcjson.Cmd) (interface{}, *btcjson.Error) {
	// Update cmd with a new ID so replies can be handled without frontend
	// IDs clashing with requests originating in btcwallet.  The original
	// request ID is always used in the frontend's response.
	cmd.SetId(<-NewJSONID)

	request := NewRPCRequest(cmd, nil)
	response := <-CurrentRPCConn().SendRequest(request)
	return response.Result, response.Err
}
Exemple #10
0
// NtfnRescanProgress handles btcd rescanprogress notifications resulting
// from a partially completed rescan.
func NtfnRescanProgress(n btcjson.Cmd) error {
	cn, ok := n.(*btcws.RescanProgressNtfn)
	if !ok {
		return fmt.Errorf("%v handler: unexpected type", n.Method())
	}

	// Notify the rescan manager of the completed partial progress for
	// the current rescan.
	AcctMgr.rm.MarkProgress(cn.LastProcessed)

	return nil
}
Exemple #11
0
// handleWalletLockStateNtfn handles btcwallet walletlockstate notifications
// by updating the GUI with whether the wallet is locked or not, setting
// the sensitivity of various widgets for locking or unlocking the wallet.
func handleWalletLockStateNtfn(n btcjson.Cmd) {
	wlsn, ok := n.(*btcws.WalletLockStateNtfn)
	if !ok {
		log.Printf("[ERR] %v handler: unexpected type", n.Method())
		return
	}

	// TODO(jrick): do proper filtering and display all
	// account balances somewhere
	if wlsn.Account == "" {
		updateChans.lockState <- wlsn.Locked
	}
}
Exemple #12
0
// NtfnTxMined handles btcd notifications resulting from newly
// mined transactions that originated from this wallet.
func NtfnTxMined(n btcjson.Cmd, marshaled []byte) {
	tmn, ok := n.(*btcws.TxMinedNtfn)
	if !ok {
		log.Errorf("%v handler: unexpected type", n.Method())
		return
	}

	txid, err := btcwire.NewShaHashFromStr(tmn.TxID)
	if err != nil {
		log.Errorf("%v handler: invalid hash string", n.Method())
		return
	}
	blockhash, err := btcwire.NewShaHashFromStr(tmn.BlockHash)
	if err != nil {
		log.Errorf("%v handler: invalid block hash string", n.Method())
		return
	}

	err = accountstore.RecordMinedTx(txid, blockhash,
		tmn.BlockHeight, tmn.Index, tmn.BlockTime)
	if err != nil {
		log.Errorf("%v handler: %v", n.Method(), err)
		return
	}

	// Remove mined transaction from pool.
	UnminedTxs.Lock()
	delete(UnminedTxs.m, TXID(*txid))
	UnminedTxs.Unlock()
}
Exemple #13
0
// handleNotifySpent implements the notifyspent command extension for
// websocket connections.
func handleNotifySpent(s *rpcServer, cmd btcjson.Cmd, wallet walletChan) error {
	id := cmd.Id()
	reply := &btcjson.Reply{Id: &id}

	notifyCmd, ok := cmd.(*btcws.NotifySpentCmd)
	if !ok {
		return btcjson.ErrInternal
	}

	s.ws.AddSpentRequest(wallet, notifyCmd.OutPoint)

	mreply, _ := json.Marshal(reply)
	wallet <- mreply
	return nil
}
Exemple #14
0
// handleGetCurrentNet implements the getcurrentnet command extension
// for websocket connections.
func handleGetCurrentNet(s *rpcServer, cmd btcjson.Cmd, wallet walletChan) error {
	id := cmd.Id()
	reply := &btcjson.Reply{Id: &id}

	var net btcwire.BitcoinNet
	if cfg.TestNet3 {
		net = btcwire.TestNet3
	} else {
		net = btcwire.MainNet
	}

	reply.Result = float64(net)
	mreply, _ := json.Marshal(reply)
	wallet <- mreply
	return nil
}
Exemple #15
0
// handleAccountBalanceNtfn handles btcwallet accountbalance notifications by
// updating the GUI with either a new confirmed or unconfirmed balance.
func handleAccountBalanceNtfn(n btcjson.Cmd) {
	abn, ok := n.(*btcws.AccountBalanceNtfn)
	if !ok {
		log.Printf("[ERR] %v handler: unexpected type", n.Method())
		return
	}

	// TODO(jrick): do proper filtering and display all
	// account balances somewhere
	if abn.Account == "" {
		if abn.Confirmed {
			updateChans.balance <- abn.Balance
		} else {
			updateChans.unconfirmed <- abn.Balance
		}
	}

}
Exemple #16
0
// NtfnBlockDisconnected handles btcd notifications resulting from
// blocks disconnected from the main chain in the event of a chain
// switch and notifies frontends of the new blockchain height.
func NtfnBlockDisconnected(n btcjson.Cmd, marshaled []byte) {
	bdn, ok := n.(*btcws.BlockDisconnectedNtfn)
	if !ok {
		log.Errorf("%v handler: unexpected type", n.Method())
		return
	}
	hash, err := btcwire.NewShaHashFromStr(bdn.Hash)
	if err != nil {
		log.Errorf("%v handler: invalid hash string", n.Method())
		return
	}

	// Rollback Utxo and Tx data stores.
	accountstore.Rollback(bdn.Height, hash)

	// Pass notification to frontends too.
	frontendNotificationMaster <- marshaled
}
// sendCmd sends the passed command to the associated server and returns a
// response channel on which the reply will be deliver at some point in the
// future.  It handles both websocket and HTTP POST mode depending on the
// configuration of the client.
func (c *Client) sendCmd(cmd btcjson.Cmd) chan *futureResult {
	// Choose which marshal and send function to use depending on whether
	// the client running in HTTP POST mode or not.  When running in HTTP
	// POST mode, the command is issued via an HTTP client.  Otherwise,
	// the command is issued via the asynchronous websocket channels.
	responseChan := make(chan *futureResult, 1)
	if c.config.HttpPostMode {
		c.marshalAndSendPost(cmd, responseChan)
		return responseChan
	}

	c.addRequest(cmd.Id().(uint64), &jsonRequest{
		cmd:          cmd,
		responseChan: responseChan,
	})
	c.marshalAndSend(cmd, responseChan)
	return responseChan
}
Exemple #18
0
// respondToAnyCmd checks that a parsed command is a standard or
// extension JSON-RPC command and runs the proper handler to reply to
// the command.  Any and all responses are sent to the wallet from
// this function.
func respondToAnyCmd(cmd btcjson.Cmd, s *rpcServer, c handlerChans) *btcjson.Reply {
	// Lookup the websocket extension for the command and if it doesn't
	// exist fallback to handling the command as a standard command.
	wsHandler, ok := wsHandlers[cmd.Method()]
	if !ok {
		// No websocket-specific handler so handle like a legacy
		// RPC connection.
		response := standardCmdReply(cmd, s)
		return &response
	}
	result, jsonErr := wsHandler(s, cmd, c)
	id := cmd.Id()
	response := btcjson.Reply{
		Id:     &id,
		Result: result,
		Error:  jsonErr,
	}
	return &response
}
Exemple #19
0
// handleTxNtfn handles btcwallet newtx notifications by updating the GUI
// with details about a new tx to or from wallet addresses.
func handleTxNtfn(n btcjson.Cmd) {
	tn, ok := n.(*btcws.TxNtfn)
	if !ok {
		log.Printf("[ERR] %v handler: unexpected type", n.Method())
		return
	}

	// TODO(jrick): do proper filtering and display
	// tx details for all accounts.
	if tn.Account == "" {
		attr, err := parseTxDetails(tn.Details)
		if err != nil {
			log.Printf("[ERR] %v handler: bad details: %v",
				n.Method(), err)
			return
		}
		updateChans.prependOverviewTx <- attr
		updateChans.prependTx <- attr
	}
}
Exemple #20
0
// handleGetBestBlock implements the getbestblock command extension
// for websocket connections.
func handleGetBestBlock(s *rpcServer, cmd btcjson.Cmd, wallet walletChan) error {
	id := cmd.Id()
	reply := &btcjson.Reply{Id: &id}

	// All other "get block" commands give either the height, the
	// hash, or both but require the block SHA.  This gets both for
	// the best block.
	sha, height, err := s.server.db.NewestSha()
	if err != nil {
		return btcjson.ErrBestBlockHash
	}

	reply.Result = map[string]interface{}{
		"hash":   sha.String(),
		"height": height,
	}
	mreply, _ := json.Marshal(reply)
	wallet <- mreply
	return nil
}
Exemple #21
0
// NtfnBlockConnected handles btcd notifications resulting from newly
// connected blocks to the main blockchain.
//
// TODO(jrick): Send block time with notification.  This will be used
// to mark wallet files with a possibly-better earliest block height,
// and will greatly reduce rescan times for wallets created with an
// out of sync btcd.
func NtfnBlockConnected(n btcjson.Cmd, marshaled []byte) {
	bcn, ok := n.(*btcws.BlockConnectedNtfn)
	if !ok {
		log.Errorf("%v handler: unexpected type", n.Method())
		return
	}
	hash, err := btcwire.NewShaHashFromStr(bcn.Hash)
	if err != nil {
		log.Errorf("%v handler: invalid hash string", n.Method())
		return
	}

	// Update the blockstamp for the newly-connected block.
	bs := &wallet.BlockStamp{
		Height: bcn.Height,
		Hash:   *hash,
	}
	curBlock.Lock()
	curBlock.BlockStamp = *bs
	curBlock.Unlock()

	// btcd notifies btcwallet about transactions first, and then sends
	// the new block notification.  New balance notifications for txs
	// in blocks are therefore sent here after all tx notifications
	// have arrived and finished being processed by the handlers.
	workers := NotifyBalanceRequest{
		block: *hash,
		wg:    make(chan *sync.WaitGroup),
	}
	NotifyBalanceSyncerChans.access <- workers
	if wg := <-workers.wg; wg != nil {
		wg.Wait()
		NotifyBalanceSyncerChans.remove <- *hash
	}
	accountstore.BlockNotify(bs)

	// Pass notification to frontends too.
	frontendNotificationMaster <- marshaled
}
Exemple #22
0
// processNotification checks for a handler for a notification, and sends
func processNotification(n btcjson.Cmd, s string) {
	// Message is a btcd notification.  Check the method and dispatch
	// correct handler, or if no handler, pass up to each wallet.
	if ntfnHandler, ok := notificationHandlers[n.Method()]; ok {
		log.Debugf("Running notification handler for method %v",
			n.Method())
		ntfnHandler(n, []byte(s))
	} else {
		// No handler; send to all wallets.
		log.Debugf("Sending notification with method %v to all wallets",
			n.Method())
		frontendNotificationMaster <- []byte(s)
	}
}
Exemple #23
0
// NtfnRedeemingTx handles btcd redeemingtx notifications resulting from a
// transaction spending a watched outpoint.
func NtfnRedeemingTx(n btcjson.Cmd) error {
	cn, ok := n.(*btcws.RedeemingTxNtfn)
	if !ok {
		return fmt.Errorf("%v handler: unexpected type", n.Method())
	}

	rawTx, err := hex.DecodeString(cn.HexTx)
	if err != nil {
		return fmt.Errorf("%v handler: bad hexstring: %v", n.Method(), err)
	}
	tx, err := btcutil.NewTxFromBytes(rawTx)
	if err != nil {
		return fmt.Errorf("%v handler: bad transaction bytes: %v", n.Method(), err)
	}

	block, txIdx, err := parseBlock(cn.Block)
	if err != nil {
		return fmt.Errorf("%v handler: bad block: %v", n.Method(), err)
	}
	tx.SetIndex(txIdx)
	return AcctMgr.RecordSpendingTx(tx, block)
}
Exemple #24
0
// NtfnBlockDisconnected handles btcd notifications resulting from
// blocks disconnected from the main chain in the event of a chain
// switch and notifies frontends of the new blockchain height.
func NtfnBlockDisconnected(n btcjson.Cmd) error {
	bdn, ok := n.(*btcws.BlockDisconnectedNtfn)
	if !ok {
		return fmt.Errorf("%v handler: unexpected type", n.Method())
	}
	hash, err := btcwire.NewShaHashFromStr(bdn.Hash)
	if err != nil {
		return fmt.Errorf("%v handler: invalid hash string", n.Method())
	}

	// Rollback Utxo and Tx data stores.
	AcctMgr.Rollback(bdn.Height, hash)

	// Pass notification to frontends too.
	marshaled, _ := n.MarshalJSON()
	allClients <- marshaled

	return nil
}
Exemple #25
0
// handleRescan implements the rescan command extension for websocket
// connections.
func handleRescan(s *rpcServer, cmd btcjson.Cmd, wallet walletChan) error {
	rescanCmd, ok := cmd.(*btcws.RescanCmd)
	if !ok {
		return btcjson.ErrInternal
	}

	if len(rescanCmd.Addresses) == 1 {
		rpcsLog.Info("Beginning rescan for 1 address.")
	} else {
		rpcsLog.Infof("Beginning rescan for %v addresses.",
			len(rescanCmd.Addresses))
	}

	minblock := int64(rescanCmd.BeginBlock)
	maxblock := int64(rescanCmd.EndBlock)

	// FetchHeightRange may not return a complete list of block shas for
	// the given range, so fetch range as many times as necessary.
	for {
		blkshalist, err := s.server.db.FetchHeightRange(minblock,
			maxblock)
		if err != nil {
			return err
		}
		if len(blkshalist) == 0 {
			break
		}

		for i := range blkshalist {
			blk, err := s.server.db.FetchBlockBySha(&blkshalist[i])
			if err != nil {
				rpcsLog.Errorf("Error looking up block sha: %v",
					err)
				return err
			}
			for _, tx := range blk.Transactions() {
				var txReply *btcdb.TxListReply
			txouts:
				for txOutIdx, txout := range tx.MsgTx().TxOut {
					_, addrs, _, err := btcscript.ExtractPkScriptAddrs(
						txout.PkScript, s.server.btcnet)
					if err != nil {
						continue txouts
					}

					for i, addr := range addrs {
						encodedAddr := addr.EncodeAddress()
						if _, ok := rescanCmd.Addresses[encodedAddr]; ok {
							// TODO(jrick): This lookup is expensive and can be avoided
							// if the wallet is sent the previous outpoints for all inputs
							// of the tx, so any can removed from the utxo set (since
							// they are, as of this tx, now spent).
							if txReply == nil {
								txReplyList, err := s.server.db.FetchTxBySha(tx.Sha())
								if err != nil {
									rpcsLog.Errorf("Tx Sha %v not found by db.", tx.Sha())
									continue txouts
								}
								for i := range txReplyList {
									if txReplyList[i].Height == blk.Height() {
										txReply = txReplyList[i]
										break
									}
								}

							}

							ntfn := &btcws.ProcessedTxNtfn{
								Receiver:    encodedAddr,
								Amount:      txout.Value,
								TxID:        tx.Sha().String(),
								TxOutIndex:  uint32(txOutIdx),
								PkScript:    hex.EncodeToString(txout.PkScript),
								BlockHash:   blkshalist[i].String(),
								BlockHeight: int32(blk.Height()),
								BlockIndex:  tx.Index(),
								BlockTime:   blk.MsgBlock().Header.Timestamp.Unix(),
								Spent:       txReply.TxSpent[txOutIdx],
							}
							mntfn, _ := ntfn.MarshalJSON()
							wallet <- mntfn
						}
					}
				}
			}
		}

		if maxblock-minblock > int64(len(blkshalist)) {
			minblock += int64(len(blkshalist))
		} else {
			break
		}
	}

	rpcsLog.Info("Finished rescan")

	id := cmd.Id()
	response := &btcjson.Reply{
		Id:     &id,
		Result: nil,
		Error:  nil,
	}
	mresponse, _ := json.Marshal(response)
	wallet <- mresponse

	return nil
}
Exemple #26
0
// NtfnRecvTx handles the btcws.RecvTxNtfn notification.
func NtfnRecvTx(n btcjson.Cmd) error {
	rtx, ok := n.(*btcws.RecvTxNtfn)
	if !ok {
		return fmt.Errorf("%v handler: unexpected type", n.Method())
	}

	bs, err := GetCurBlock()
	if err != nil {
		return fmt.Errorf("%v handler: cannot get current block: %v", n.Method(), err)
	}

	rawTx, err := hex.DecodeString(rtx.HexTx)
	if err != nil {
		return fmt.Errorf("%v handler: bad hexstring: %v", n.Method(), err)
	}
	tx, err := btcutil.NewTxFromBytes(rawTx)
	if err != nil {
		return fmt.Errorf("%v handler: bad transaction bytes: %v", n.Method(), err)
	}

	block, txIdx, err := parseBlock(rtx.Block)
	if err != nil {
		return fmt.Errorf("%v handler: bad block: %v", n.Method(), err)
	}
	tx.SetIndex(txIdx)

	// For transactions originating from this wallet, the sent tx history should
	// be recorded before the received history.  If wallet created this tx, wait
	// for the sent history to finish being recorded before continuing.
	//
	// TODO(jrick) this is wrong due to tx malleability.  Cannot safely use the
	// txsha as an identifier.
	req := SendTxHistSyncRequest{
		txsha:    *tx.Sha(),
		response: make(chan SendTxHistSyncResponse),
	}
	SendTxHistSyncChans.access <- req
	resp := <-req.response
	if resp.ok {
		// Wait until send history has been recorded.
		<-resp.c
		SendTxHistSyncChans.remove <- *tx.Sha()
	}

	// For every output, find all accounts handling that output address (if any)
	// and record the received txout.
	for outIdx, txout := range tx.MsgTx().TxOut {
		var accounts []*Account
		_, addrs, _, _ := btcscript.ExtractPkScriptAddrs(txout.PkScript, cfg.Net())
		for _, addr := range addrs {
			a, err := AcctMgr.AccountByAddress(addr)
			if err != nil {
				continue
			}
			accounts = append(accounts, a)
		}

		for _, a := range accounts {
			txr, err := a.TxStore.InsertTx(tx, block)
			if err != nil {
				return err
			}
			cred, err := txr.AddCredit(uint32(outIdx), false)
			if err != nil {
				return err
			}
			AcctMgr.ds.ScheduleTxStoreWrite(a)

			// Notify frontends of tx.  If the tx is unconfirmed, it is always
			// notified and the outpoint is marked as notified.  If the outpoint
			// has already been notified and is now in a block, a txmined notifiction
			// should be sent once to let frontends that all previous send/recvs
			// for this unconfirmed tx are now confirmed.
			op := *cred.OutPoint()
			previouslyNotifiedReq := NotifiedRecvTxRequest{
				op:       op,
				response: make(chan NotifiedRecvTxResponse),
			}
			NotifiedRecvTxChans.access <- previouslyNotifiedReq
			if <-previouslyNotifiedReq.response {
				NotifiedRecvTxChans.remove <- op
			} else {
				// Notify frontends of new recv tx and mark as notified.
				NotifiedRecvTxChans.add <- op

				ltr, err := cred.ToJSON(a.Name(), bs.Height, a.Wallet.Net())
				if err != nil {
					return err
				}
				NotifyNewTxDetails(allClients, a.Name(), ltr)
			}

			// Notify frontends of new account balance.
			confirmed := a.CalculateBalance(1)
			unconfirmed := a.CalculateBalance(0) - confirmed
			NotifyWalletBalance(allClients, a.name, confirmed)
			NotifyWalletBalanceUnconfirmed(allClients, a.name, unconfirmed)
		}
	}

	return nil
}
Exemple #27
0
// NtfnProcessedTx handles the btcws.ProcessedTxNtfn notification.
func NtfnProcessedTx(n btcjson.Cmd, marshaled []byte) {
	ptn, ok := n.(*btcws.ProcessedTxNtfn)
	if !ok {
		log.Errorf("%v handler: unexpected type", n.Method())
		return
	}

	// Create useful types from the JSON strings.
	receiver, err := btcutil.DecodeAddr(ptn.Receiver)
	if err != nil {
		log.Errorf("%v handler: error parsing receiver: %v", n.Method(), err)
		return
	}
	txID, err := btcwire.NewShaHashFromStr(ptn.TxID)
	if err != nil {
		log.Errorf("%v handler: error parsing txid: %v", n.Method(), err)
		return
	}
	blockHash, err := btcwire.NewShaHashFromStr(ptn.BlockHash)
	if err != nil {
		log.Errorf("%v handler: error parsing block hash: %v", n.Method(), err)
		return
	}
	pkscript, err := hex.DecodeString(ptn.PkScript)
	if err != nil {
		log.Errorf("%v handler: error parsing pkscript: %v", n.Method(), err)
		return
	}

	// Lookup account for address in result.
	aname, err := LookupAccountByAddress(ptn.Receiver)
	if err == ErrNotFound {
		log.Warnf("Received rescan result for unknown address %v", ptn.Receiver)
		return
	}
	a, err := accountstore.Account(aname)
	if err == ErrAcctNotExist {
		log.Errorf("Missing account for rescaned address %v", ptn.Receiver)
	}

	// Create RecvTx to add to tx history.
	t := &tx.RecvTx{
		TxID:         *txID,
		TxOutIdx:     ptn.TxOutIndex,
		TimeReceived: time.Now().Unix(),
		BlockHeight:  ptn.BlockHeight,
		BlockHash:    *blockHash,
		BlockIndex:   int32(ptn.BlockIndex),
		BlockTime:    ptn.BlockTime,
		Amount:       ptn.Amount,
		ReceiverHash: receiver.ScriptAddress(),
	}

	// For transactions originating from this wallet, the sent tx history should
	// be recorded before the received history.  If wallet created this tx, wait
	// for the sent history to finish being recorded before continuing.
	req := SendTxHistSyncRequest{
		txid:     *txID,
		response: make(chan SendTxHistSyncResponse),
	}
	SendTxHistSyncChans.access <- req
	resp := <-req.response
	if resp.ok {
		// Wait until send history has been recorded.
		<-resp.c
		SendTxHistSyncChans.remove <- *txID
	}

	// Record the tx history.
	a.TxStore.Lock()
	a.TxStore.s.InsertRecvTx(t)
	a.TxStore.Unlock()
	a.ScheduleTxStoreWrite()

	// Notify frontends of tx.  If the tx is unconfirmed, it is always
	// notified and the outpoint is marked as notified.  If the outpoint
	// has already been notified and is now in a block, a txmined notifiction
	// should be sent once to let frontends that all previous send/recvs
	// for this unconfirmed tx are now confirmed.
	recvTxOP := btcwire.NewOutPoint(txID, ptn.TxOutIndex)
	previouslyNotifiedReq := NotifiedRecvTxRequest{
		op:       *recvTxOP,
		response: make(chan NotifiedRecvTxResponse),
	}
	NotifiedRecvTxChans.access <- previouslyNotifiedReq
	if <-previouslyNotifiedReq.response {
		NotifyMinedTx <- t
		NotifiedRecvTxChans.remove <- *recvTxOP
	} else {
		// Notify frontends of new recv tx and mark as notified.
		NotifiedRecvTxChans.add <- *recvTxOP
		NotifyNewTxDetails(frontendNotificationMaster, a.Name(), t.TxInfo(a.Name(),
			ptn.BlockHeight, a.Wallet.Net())[0])
	}

	if !ptn.Spent {
		u := &tx.Utxo{
			Amt:       uint64(ptn.Amount),
			Height:    ptn.BlockHeight,
			Subscript: pkscript,
		}
		copy(u.Out.Hash[:], txID[:])
		u.Out.Index = uint32(ptn.TxOutIndex)
		copy(u.AddrHash[:], receiver.ScriptAddress())
		copy(u.BlockHash[:], blockHash[:])
		a.UtxoStore.Lock()
		a.UtxoStore.s.Insert(u)
		a.UtxoStore.Unlock()
		a.ScheduleUtxoStoreWrite()

		// If this notification came from mempool, notify frontends of
		// the new unconfirmed balance immediately.  Otherwise, wait until
		// the blockconnected notifiation is processed.
		if u.Height == -1 {
			bal := a.CalculateBalance(0) - a.CalculateBalance(1)
			NotifyWalletBalanceUnconfirmed(frontendNotificationMaster,
				a.name, bal)
		}
	}

	// Notify frontends of new account balance.
	confirmed := a.CalculateBalance(1)
	unconfirmed := a.CalculateBalance(0) - confirmed
	NotifyWalletBalance(frontendNotificationMaster, a.name, confirmed)
	NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, a.name, unconfirmed)
}