// fetchLocationBySha look up the Tx sha information by name. // Must be called with db lock held. func (db *SqliteDb) fetchLocationBySha(txsha *btcwire.ShaHash) (blockidx int64, txoff int, txlen int, err error) { var row *sql.Row var blockid int64 var ttxoff int var ttxlen int rowBytes := txsha.String() txop := db.txop(txFetchLocationByShaStmt) row = txop.QueryRow(rowBytes) err = row.Scan(&blockid, &ttxoff, &ttxlen) if err == sql.ErrNoRows { txop = db.txop(txtmpFetchLocationByShaStmt) row = txop.QueryRow(rowBytes) err = row.Scan(&blockid, &ttxoff, &ttxlen) if err == sql.ErrNoRows { err = btcdb.TxShaMissing return } if err != nil { log.Warnf("txtmp FetchLocationBySha: fail %v", err) return } } if err != nil { log.Warnf("FetchLocationBySha: fail %v", err) return } blockidx = blockid - 1 txoff = ttxoff txlen = ttxlen return }
// FetchTxUsedBySha returns the used/spent buffer for a given transaction. func (db *SqliteDb) FetchTxUsedBySha(txsha *btcwire.ShaHash) (spentbuf []byte, err error) { var row *sql.Row db.dbLock.Lock() defer db.dbLock.Unlock() rowBytes := txsha.String() txop := db.txop(txFetchUsedByShaStmt) row = txop.QueryRow(rowBytes) var databytes []byte err = row.Scan(&databytes) if err == sql.ErrNoRows { txop := db.txop(txtmpFetchUsedByShaStmt) row = txop.QueryRow(rowBytes) err = row.Scan(&databytes) if err == sql.ErrNoRows { err = btcdb.TxShaMissing return } if err != nil { log.Warnf("txtmp FetchLocationBySha: fail %v", err) return } } if err != nil { log.Warnf("FetchUsedBySha: fail %v", err) return } spentbuf = databytes return }
// VerboseGetRawTransaction sends the verbose version of a getrawtransaction // request to receive details about a transaction. func VerboseGetRawTransaction(rpc ServerConn, txsha *btcwire.ShaHash) (*btcjson.TxRawResult, *btcjson.Error) { // NewGetRawTransactionCmd cannot fail with a single optarg. cmd, _ := btcjson.NewGetRawTransactionCmd(<-NewJSONID, txsha.String(), 1) response := <-rpc.SendRequest(NewServerRequest(cmd)) var resultData btcjson.TxRawResult _, jsonErr := response.FinishUnmarshal(&resultData) if jsonErr != nil { return nil, jsonErr } return &resultData, nil }
func notifySpentData(n ntfnChan, txhash *btcwire.ShaHash, index uint32, spender *btcutil.Tx) { var buf bytes.Buffer // Ignore Serialize's error, as writing to a bytes.buffer // cannot fail. spender.MsgTx().Serialize(&buf) txStr := hex.EncodeToString(buf.Bytes()) ntfn := btcws.NewTxSpentNtfn(txhash.String(), int(index), txStr) n <- ntfn }
// GetRawTransactionAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See GetRawTransaction for the blocking version and more details. func (c *Client) GetRawTransactionAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionResult { hash := "" if txHash != nil { hash = txHash.String() } id := c.NextID() cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 0) if err != nil { return newFutureError(err) } return c.sendCmd(cmd) }
// GetBlockAsync returns an instance of a type that can be used to get the // result of the RPC at some future time by invoking the Receive function on the // returned instance. // // See GetBlock for the blocking version and more details. func (c *Client) GetBlockAsync(blockHash *btcwire.ShaHash) FutureGetBlockResult { hash := "" if blockHash != nil { hash = blockHash.String() } id := c.NextID() cmd, err := btcjson.NewGetBlockCmd(id, hash, false) if err != nil { return newFutureError(err) } return c.sendCmd(cmd) }
// GetBlockVerboseAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See GetBlockVerbose for the blocking version and more details. func (c *Client) GetBlockVerboseAsync(blockHash *btcwire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { hash := "" if blockHash != nil { hash = blockHash.String() } id := c.NextID() cmd, err := btcjson.NewGetBlockCmd(id, hash, true, verboseTx) if err != nil { return newFutureError(err) } return c.sendCmd(cmd) }
// insertTx inserts a tx hash and its associated data into the database. // Must be called with db lock held. func (db *SqliteDb) insertTx(txsha *btcwire.ShaHash, blockidx int64, txoff int, txlen int, usedbuf []byte) (err error) { tx := &db.txState if tx.tx == nil { err = db.startTx() if err != nil { return } } blockid := blockidx + 1 txd := tTxInsertData{txsha: txsha, blockid: blockid, txoff: txoff, txlen: txlen, usedbuf: usedbuf} log.Tracef("inserting tx %v for block %v off %v len %v", txsha, blockid, txoff, txlen) rowBytes := txsha.String() var op int // which table to insert data into. if db.UseTempTX { var tblockid int64 var ttxoff int var ttxlen int txop := db.txop(txFetchLocationByShaStmt) row := txop.QueryRow(rowBytes) err = row.Scan(&tblockid, &ttxoff, &ttxlen) if err != sql.ErrNoRows { // sha already present err = btcdb.DuplicateSha return } op = txtmpInsertStmt } else { op = txInsertStmt } txop := db.txop(op) _, err = txop.Exec(rowBytes, blockid, txoff, txlen, usedbuf) if err != nil { log.Warnf("failed to insert %v %v %v", txsha, blockid, err) return } if db.UseTempTX { db.TempTblSz++ } // put in insert list for replay tx.txInsertList = append(tx.txInsertList, txd) return }
func notifySpentData(wallet walletChan, txhash *btcwire.ShaHash, index uint32, spender *btcutil.Tx) { var buf bytes.Buffer // Ignore Serialize's error, as writing to a bytes.buffer // cannot fail. spender.MsgTx().Serialize(&buf) txStr := hex.EncodeToString(buf.Bytes()) // TODO(jrick): create a new notification in btcws and use that. ntfn := btcws.NewTxSpentNtfn(txhash.String(), int(index), txStr) mntfn, _ := ntfn.MarshalJSON() wallet <- mntfn }
// RescanEndBlockAsync returns an instance of a type that can be used to get // the result of the RPC at some future time by invoking the Receive function on // the returned instance. // // See RescanEndBlock for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. func (c *Client) RescanEndBlockAsync(startBlock *btcwire.ShaHash, addresses []btcutil.Address, outpoints []*btcwire.OutPoint, endBlock *btcwire.ShaHash) FutureRescanResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { return newFutureError(ErrNotificationsNotSupported) } // Ignore the notification if the client is not interested in // notifications. if c.ntfnHandlers == nil { return newNilFutureResult() } // Convert block hashes to strings. var startBlockShaStr, endBlockShaStr string if startBlock != nil { startBlockShaStr = startBlock.String() } if endBlock != nil { endBlockShaStr = endBlock.String() } // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { addrs = append(addrs, addr.String()) } // Convert outpoints. ops := make([]btcws.OutPoint, 0, len(outpoints)) for _, op := range outpoints { ops = append(ops, *btcws.NewOutPointFromWire(op)) } id := c.NextID() cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops, endBlockShaStr) if err != nil { return newFutureError(err) } return c.sendCmd(cmd) }
// createTxRawResult converts the passed transaction and associated parameters // to a raw transaction JSON object. func createTxRawResult(net btcwire.BitcoinNet, txSha string, mtx *btcwire.MsgTx, blk *btcutil.Block, maxidx int64, blksha *btcwire.ShaHash) (*btcjson.TxRawResult, error) { mtxHex, err := messageToHex(mtx) if err != nil { return nil, err } vin, err := createVinList(mtx) if err != nil { return nil, err } vout, err := createVoutList(mtx, net) if err != nil { return nil, err } txReply := &btcjson.TxRawResult{ Hex: mtxHex, Txid: txSha, Vout: vout, Vin: vin, Version: mtx.Version, LockTime: mtx.LockTime, } if blk != nil { blockHeader := &blk.MsgBlock().Header idx := blk.Height() // This is not a typo, they are identical in bitcoind as well. txReply.Time = blockHeader.Timestamp.Unix() txReply.Blocktime = blockHeader.Timestamp.Unix() txReply.BlockHash = blksha.String() txReply.Confirmations = uint64(1 + maxidx - idx) } return txReply, nil }
// handleGetBlock implements the getblock command. func handleGetBlock(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) { c := cmd.(*btcjson.GetBlockCmd) sha, err := btcwire.NewShaHashFromStr(c.Hash) if err != nil { rpcsLog.Errorf("Error generating sha: %v", err) return nil, btcjson.ErrBlockNotFound } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { rpcsLog.Errorf("Error fetching sha: %v", err) return nil, btcjson.ErrBlockNotFound } // When the verbose flag isn't set, simply return the network-serialized // block as a hex-encoded string. if !c.Verbose { blkHex, err := messageToHex(blk.MsgBlock()) if err != nil { return nil, err } return blkHex, nil } // The verbose flag is set, so generate the JSON object and return it. buf, err := blk.Bytes() if err != nil { rpcsLog.Errorf("Error fetching block: %v", err) return nil, btcjson.Error{ Code: btcjson.ErrInternal.Code, Message: err.Error(), } } idx := blk.Height() _, maxidx, err := s.server.db.NewestSha() if err != nil { rpcsLog.Errorf("Cannot get newest sha: %v", err) return nil, btcjson.ErrBlockNotFound } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.BlockResult{ Hash: c.Hash, Version: blockHeader.Version, MerkleRoot: blockHeader.MerkleRoot.String(), PreviousHash: blockHeader.PrevBlock.String(), Nonce: blockHeader.Nonce, Time: blockHeader.Timestamp.Unix(), Confirmations: uint64(1 + maxidx - idx), Height: idx, Size: len(buf), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } if !c.VerboseTx { txList, _ := blk.TxShas() txNames := make([]string, len(txList)) for i, v := range txList { txNames[i] = v.String() } blockReply.Tx = txNames } else { txns := blk.Transactions() rawTxns := make([]btcjson.TxRawResult, len(txns)) for i, tx := range txns { txSha := tx.Sha().String() mtx := tx.MsgTx() rawTxn, err := createTxRawResult(s.server.btcnet, txSha, mtx, blk, maxidx, sha) if err != nil { rpcsLog.Errorf("Cannot create TxRawResult for "+ "transaction %s: %v", txSha, err) return nil, err } rawTxns[i] = *rawTxn } blockReply.RawTx = rawTxns } // Get next block unless we are already at the top. if idx < maxidx { var shaNext *btcwire.ShaHash shaNext, err = s.server.db.FetchBlockShaByHeight(int64(idx + 1)) if err != nil { rpcsLog.Errorf("No next block: %v", err) return nil, btcjson.ErrBlockNotFound } blockReply.NextHash = shaNext.String() } return blockReply, nil }
// GetRawTransaction returns a future representing a pending GetRawTransaction // command for txsha.. When the result of the request is required it may be // collected with GetRawTRansactionAsyncResult. func GetRawTransactionAsync(rpc ServerConn, txsha *btcwire.ShaHash) chan RawRPCResponse { // NewGetRawTransactionCmd cannot fail with no optargs. cmd, _ := btcjson.NewGetRawTransactionCmd(<-NewJSONID, txsha.String()) return rpc.SendRequest(NewServerRequest(cmd)) }
// handleGetBlock implements the getblock command. func handleGetBlock(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) { c := cmd.(*btcjson.GetBlockCmd) sha, err := btcwire.NewShaHashFromStr(c.Hash) if err != nil { log.Errorf("RPCS: Error generating sha: %v", err) return nil, btcjson.ErrBlockNotFound } blk, err := s.server.db.FetchBlockBySha(sha) if err != nil { log.Errorf("RPCS: Error fetching sha: %v", err) return nil, btcjson.ErrBlockNotFound } idx := blk.Height() buf, err := blk.Bytes() if err != nil { log.Errorf("RPCS: Error fetching block: %v", err) return nil, btcjson.ErrBlockNotFound } txList, _ := blk.TxShas() txNames := make([]string, len(txList)) for i, v := range txList { txNames[i] = v.String() } _, maxidx, err := s.server.db.NewestSha() if err != nil { log.Errorf("RPCS: Cannot get newest sha: %v", err) return nil, btcjson.ErrBlockNotFound } blockHeader := &blk.MsgBlock().Header blockReply := btcjson.BlockResult{ Hash: c.Hash, Version: blockHeader.Version, MerkleRoot: blockHeader.MerkleRoot.String(), PreviousHash: blockHeader.PrevBlock.String(), Nonce: blockHeader.Nonce, Time: blockHeader.Timestamp.Unix(), Confirmations: uint64(1 + maxidx - idx), Height: idx, Tx: txNames, Size: len(buf), Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), Difficulty: getDifficultyRatio(blockHeader.Bits), } // Get next block unless we are already at the top. if idx < maxidx { var shaNext *btcwire.ShaHash shaNext, err = s.server.db.FetchBlockShaByHeight(int64(idx + 1)) if err != nil { log.Errorf("RPCS: No next block: %v", err) return nil, btcjson.ErrBlockNotFound } blockReply.NextHash = shaNext.String() } return blockReply, nil }