// NotifyBlockConnected creates and marshalls a JSON message to notify // of a new block connected to the main chain. The notification is sent // to each connected wallet. func (s *rpcServer) NotifyBlockConnected(block *btcutil.Block) { s.ws.walletListeners.RLock() for wltNtfn := range s.ws.walletListeners.m { // Create notification with basic information filled in. // This data is the same for every connected wallet. hash, err := block.Sha() if err != nil { log.Error("Bad block; connected block notification dropped.") return } ntfnResult := struct { Hash string `json:"hash"` Height int64 `json:"height"` MinedTXs []string `json:"minedtxs"` }{ Hash: hash.String(), Height: block.Height(), } // Fill in additional wallet-specific notifications. If there // is no request context for this wallet, no need to give this // wallet any extra notifications. if cxt := s.ws.requests.m[wltNtfn]; cxt != nil { // Create list of all txs created by this wallet that appear in this // block. minedTxShas := make([]string, 0, len(cxt.minedTxRequests)) // TxShas does not return a non-nil error. txShaList, _ := block.TxShas() txList := s.server.db.FetchTxByShaList(txShaList) for _, txReply := range txList { if txReply.Err != nil { continue } if _, ok := cxt.minedTxRequests[*txReply.Sha]; ok { minedTxShas = append(minedTxShas, txReply.Sha.String()) s.ws.requests.RemoveMinedTxRequest(wltNtfn, txReply.Sha) } } ntfnResult.MinedTXs = minedTxShas } var id interface{} = "btcd:blockconnected" ntfn := btcjson.Reply{ Result: ntfnResult, Id: &id, } m, _ := json.Marshal(ntfn) wltNtfn <- m } s.ws.walletListeners.RUnlock() }
// checkBIP0030 ensures blocks do not contain duplicate transactions which // 'overwrite' older transactions that are not fully spent. This prevents an // attack where a coinbase and all of its dependent transactions could be // duplicated to effectively revert the overwritten transactions to a single // confirmation thereby making them vulnerable to a double spend. // // For more details, see https://en.bitcoin.it/wiki/BIP_0030 and // http://r6.ca/blog/20120206T005236Z.html. func (b *BlockChain) checkBIP0030(node *blockNode, block *btcutil.Block) error { // Attempt to fetch duplicate transactions for all of the transactions // in this block from the point of view of the parent node. fetchList, err := block.TxShas() if err != nil { return nil } fetchSet := make(map[btcwire.ShaHash]bool) for _, txHash := range fetchList { fetchSet[*txHash] = true } txResults, err := b.fetchTxStore(node, fetchSet) if err != nil { return err } // Examine the resulting data about the requested transactions. for _, txD := range txResults { switch txD.Err { // A duplicate transaction was not found. This is the most // common case. case btcdb.TxShaMissing: continue // A duplicate transaction was found. This is only allowed if // the duplicate transaction is fully spent. case nil: if !isTransactionSpent(txD) { str := fmt.Sprintf("tried to overwrite "+ "transaction %v at block height %d "+ "that is not fully spent", txD.Hash, txD.BlockHeight) return RuleError(str) } // Some other unexpected error occurred. Return it now. default: return txD.Err } } return nil }