// RecordMinedTx searches through each account's TxStore, searching for a // sent transaction with the same txid as from a txmined notification. If // the transaction IDs match, the record in the TxStore is updated with // the full information about the newly-mined tx, and the TxStore is // scheduled to be written to disk.. func (am *AccountManager) RecordMinedTx(txid *btcwire.ShaHash, blkhash *btcwire.ShaHash, blkheight int32, blkindex int, blktime int64) error { for _, a := range am.AllAccounts() { // Search in reverse order. Since more recently-created // transactions are appended to the end of the store, it's // more likely to find it when searching from the end. for i := len(a.TxStore) - 1; i >= 0; i-- { sendtx, ok := a.TxStore[i].(*tx.SendTx) if ok { if bytes.Equal(txid.Bytes(), sendtx.TxID[:]) { copy(sendtx.BlockHash[:], blkhash.Bytes()) sendtx.BlockHeight = blkheight sendtx.BlockIndex = int32(blkindex) sendtx.BlockTime = blktime am.ds.ScheduleTxStoreWrite(a) return nil } } } } return errors.New("txid does not match any recorded sent transaction") }
// hashMerkleBranches takes two hashes, treated as the left and right tree // nodes, and returns the hash of their concatenation. This is a helper // function used to during generatation of a merkle tree. func hashMerkleBranches(left *btcwire.ShaHash, right *btcwire.ShaHash) *btcwire.ShaHash { // Concatenate the left and right nodes. var sha [btcwire.HashSize * 2]byte copy(sha[:btcwire.HashSize], left.Bytes()) copy(sha[btcwire.HashSize:], right.Bytes()) // Create a new sha hash from the double sha 256. Ignore the error // here since SetBytes can't fail here due to the fact DoubleSha256 // always returns a []byte of the right size regardless of input. newSha, _ := btcwire.NewShaHash(btcwire.DoubleSha256(sha[:])) return newSha }
// ShaHashToBig converts a btcwire.ShaHash into a big.Int that can be used to // perform math comparisons. func ShaHashToBig(hash *btcwire.ShaHash) *big.Int { // A ShaHash is in little-endian, but the big package wants the bytes // in big-endian. Reverse them. ShaHash.Bytes makes a copy, so it // is safe to modify the returned buffer. buf := hash.Bytes() blen := len(buf) for i := 0; i < blen/2; i++ { buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i] } return new(big.Int).SetBytes(buf) }
func (db *LevelDb) setBlk(sha *btcwire.ShaHash, blkHeight int64, buf []byte) { // serialize var lw [8]byte binary.LittleEndian.PutUint64(lw[0:8], uint64(blkHeight)) shaKey := shaBlkToKey(sha) blkKey := int64ToKey(blkHeight) shaB := sha.Bytes() blkVal := make([]byte, len(shaB)+len(buf)) copy(blkVal[0:], shaB) copy(blkVal[len(shaB):], buf) db.lBatch().Put(shaKey, lw[:]) db.lBatch().Put(blkKey, blkVal) }
// blkExistsSha looks up the given block hash // returns true if it is present in the database. // CALLED WITH LOCK HELD func (db *SqliteDb) blkExistsSha(sha *btcwire.ShaHash) bool { var pver uint32 row := db.blkStmts[blkExistsSha].QueryRow(sha.Bytes()) err := row.Scan(&pver) if err == sql.ErrNoRows { return false } if err != nil { // ignore real errors? log.Warnf("blkExistsSha: fail %v", err) return false } return true }
// insertSha stores a block hash and its associated data block with a // previous sha of `prevSha' and a version of `pver'. // insertSha shall be called with db lock held func (db *SqliteDb) insertBlockData(sha *btcwire.ShaHash, prevSha *btcwire.ShaHash, pver uint32, buf []byte) (blockid int64, err error) { tx := &db.txState if tx.tx == nil { err = db.startTx() if err != nil { return } } var prevOk bool var blkid int64 prevOk = db.blkExistsSha(prevSha) // exists -> ok if !prevOk { return 0, btcdb.PrevShaMissing } result, err := db.blkStmts[blkInsertSha].Exec(sha.Bytes(), pver, buf) if err != nil { return } blkid, err = result.LastInsertId() if err != nil { return 0, err } blkid -= 1 // skew between btc blockid and sql // Because we don't know know what the last idx is, we don't // cache unless already cached if db.lastBlkShaCached == true { db.lastBlkSha = *sha db.lastBlkIdx++ } bid := tBlockInsertData{*sha, pver, buf} tx.txInsertList = append(tx.txInsertList, bid) tx.txDataSz += len(buf) blockid = blkid return }
// RecordMinedTx searches through each account's TxStore, searching for a // sent transaction with the same txid as from a txmined notification. If // the transaction IDs match, the record in the TxStore is updated with // the full information about the newly-mined tx, and the TxStore is // scheduled to be written to disk.. func (store *AccountStore) RecordMinedTx(txid *btcwire.ShaHash, blkhash *btcwire.ShaHash, blkheight int32, blkindex int, blktime int64) error { store.RLock() defer store.RUnlock() for _, account := range store.accounts { // The tx stores will be searched through while holding the // reader lock, and the writer will only be grabbed if necessary. account.TxStore.RLock() // Search in reverse order. Since more recently-created // transactions are appended to the end of the store, it's // more likely to find it when searching from the end. for i := len(account.TxStore.s) - 1; i >= 0; i-- { sendtx, ok := account.TxStore.s[i].(*tx.SendTx) if ok { if bytes.Equal(txid.Bytes(), sendtx.TxID[:]) { account.TxStore.RUnlock() account.TxStore.Lock() copy(sendtx.BlockHash[:], blkhash.Bytes()) sendtx.BlockHeight = blkheight sendtx.BlockIndex = int32(blkindex) sendtx.BlockTime = blktime account.TxStore.Unlock() account.ScheduleTxStoreWrite() return nil } } } account.TxStore.RUnlock() } return errors.New("txid does not match any recorded sent transaction") }
// fetchSha returns the datablock and pver for the given ShaHash. func (db *SqliteDb) fetchSha(sha btcwire.ShaHash) (buf []byte, pver uint32, blkid int64, err error) { db.dbLock.Lock() defer db.dbLock.Unlock() row := db.blkStmts[blkFetchSha].QueryRow(sha.Bytes()) var blockidx int64 var databytes []byte err = row.Scan(&pver, &databytes, &blockidx) if err == sql.ErrNoRows { return // no warning } if err != nil { log.Warnf("fail 2 %v", err) return } buf = databytes blkid = blockidx - 1 // skew between btc blockid and sql return }
func (db *LevelDb) setBlk(sha *btcwire.ShaHash, blkHeight int64, buf []byte) error { // serialize var lw bytes.Buffer err := binary.Write(&lw, binary.LittleEndian, blkHeight) if err != nil { err = fmt.Errorf("Write Fail") return err } shaKey := shaBlkToKey(sha) blkKey := int64ToKey(blkHeight) shaB := sha.Bytes() blkVal := make([]byte, len(shaB)+len(buf)) copy(blkVal[0:], shaB) copy(blkVal[len(shaB):], buf) db.lBatch().Put(shaKey, lw.Bytes()) db.lBatch().Put(blkKey, blkVal) return nil }
func shaSpentTxToKey(sha *btcwire.ShaHash) []byte { shaB := sha.Bytes() shaB = append(shaB, "sx"...) return shaB }
func shaBlkToKey(sha *btcwire.ShaHash) []byte { shaB := sha.Bytes() return shaB }
// AddShaHash adds the passed btcwire.ShaHash to the Filter. // // This function is safe for concurrent access. func (bf *Filter) AddShaHash(sha *btcwire.ShaHash) { bf.mtx.Lock() bf.add(sha.Bytes()) bf.mtx.Unlock() }
// DropAfterBlockBySha will remove any blocks from the database after the given block. // It terminates any existing transaction and performs its operations in an // atomic transaction, it is terminated (committed) before exit. func (db *SqliteDb) DropAfterBlockBySha(sha btcwire.ShaHash) (err error) { var row *sql.Row db.dbLock.Lock() defer db.dbLock.Unlock() db.InvalidateCache() // This is a destructive operation and involves multiple requests // so requires a transaction, terminate any transaction to date // and start a new transaction err = db.endTx(true) if err != nil { return err } err = db.startTx() if err != nil { return err } // also drop any cached sha data db.lastBlkShaCached = false querystr := "SELECT blockid FROM block WHERE key = ?;" tx := &db.txState row = tx.tx.QueryRow(querystr, sha.Bytes()) var blockidx uint64 err = row.Scan(&blockidx) if err != nil { // XXX db.endTx(false) return err } _, err = tx.tx.Exec("DELETE FROM txtmp WHERE blockid > ?", blockidx) if err != nil { // XXX db.endTx(false) return err } _, err = tx.tx.Exec("DELETE FROM tx WHERE blockid > ?", blockidx) if err != nil { // XXX db.endTx(false) return err } // delete from block last in case of foreign keys _, err = tx.tx.Exec("DELETE FROM block WHERE blockid > ?", blockidx) if err != nil { // XXX db.endTx(false) return err } err = db.endTx(true) if err != nil { return err } return }