// FetchHeightRange looks up a range of blocks by the start and ending // heights. Fetch is inclusive of the start height and exclusive of the // ending height. To fetch all hashes from the start height until no // more are present, use the special id `AllShas'. func (db *LevelDb) FetchHeightRange(startHeight, endHeight int64) (rshalist []btcwire.ShaHash, err error) { db.dbLock.Lock() defer db.dbLock.Unlock() var endidx int64 if endHeight == database.AllShas { endidx = startHeight + 500 } else { endidx = endHeight } shalist := make([]btcwire.ShaHash, 0, endidx-startHeight) for height := startHeight; height < endidx; height++ { // TODO(drahn) fix blkFile from height key := int64ToKey(height) blkVal, lerr := db.lDb.Get(key, db.ro) if lerr != nil { break } var sha btcwire.ShaHash sha.SetBytes(blkVal[0:32]) shalist = append(shalist, sha) } if err != nil { return } //log.Tracef("FetchIdxRange idx %v %v returned %v shas err %v", startHeight, endHeight, len(shalist), err) return shalist, nil }
func parsesha(argstr string) (argtype int, height int64, psha *btcwire.ShaHash, err error) { var sha btcwire.ShaHash var hashbuf string switch len(argstr) { case 64: hashbuf = argstr case 66: if argstr[0:2] != "0x" { log.Infof("prefix is %v", argstr[0:2]) err = errBadShaPrefix return } hashbuf = argstr[2:] default: if len(argstr) <= 16 { // assume value is height argtype = argHeight var h int h, err = strconv.Atoi(argstr) if err == nil { height = int64(h) return } log.Infof("Unable to parse height %v, err %v", height, err) } err = errBadShaLen return } var buf [32]byte for idx, ch := range hashbuf { var val rune switch { case ch >= '0' && ch <= '9': val = ch - '0' case ch >= 'a' && ch <= 'f': val = ch - 'a' + rune(10) case ch >= 'A' && ch <= 'F': val = ch - 'A' + rune(10) default: err = errBadShaChar return } b := buf[31-idx/2] if idx&1 == 1 { b |= byte(val) } else { b |= (byte(val) << 4) } buf[31-idx/2] = b } sha.SetBytes(buf[0:32]) psha = &sha return }
// 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) }
// 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 aid in the generation 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 }
// fetchBlockShaByHeight returns a block hash based on its height in the // block chain. func (db *LevelDb) fetchBlockShaByHeight(height int64) (rsha *btcwire.ShaHash, err error) { key := int64ToKey(height) blkVal, err := db.lDb.Get(key, db.ro) if err != nil { log.Tracef("failed to find height %v", height) return // exists ??? } var sha btcwire.ShaHash sha.SetBytes(blkVal[0:32]) return &sha, nil }
// fetchAddrIndexTip returns the last block height and block sha to be indexed. // Meta-data about the address tip is currently cached in memory, and will be // updated accordingly by functions that modify the state. This function is // used on start up to load the info into memory. Callers will use the public // version of this function below, which returns our cached copy. func (db *LevelDb) fetchAddrIndexTip() (*btcwire.ShaHash, int64, error) { db.dbLock.Lock() defer db.dbLock.Unlock() data, err := db.lDb.Get(addrIndexMetaDataKey, db.ro) if err != nil { return &btcwire.ShaHash{}, -1, database.ErrAddrIndexDoesNotExist } var blkSha btcwire.ShaHash blkSha.SetBytes(data[0:32]) blkHeight := binary.LittleEndian.Uint64(data[32:]) return &blkSha, int64(blkHeight), nil }
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) }
func (db *LevelDb) getBlkByHeight(blkHeight int64) (rsha *btcwire.ShaHash, rbuf []byte, err error) { var blkVal []byte key := int64ToKey(blkHeight) blkVal, err = db.lDb.Get(key, db.ro) if err != nil { log.Tracef("failed to find height %v", blkHeight) return // exists ??? } var sha btcwire.ShaHash sha.SetBytes(blkVal[0:32]) blockdata := make([]byte, len(blkVal[32:])) copy(blockdata[:], blkVal[32:]) return &sha, blockdata, nil }
// UpdateAddrIndexForBlock updates the stored addrindex with passed // index information for a particular block height. Additionally, it // will update the stored meta-data related to the curent tip of the // addr index. These two operations are performed in an atomic // transaction which is commited before the function returns. // Transactions indexed by address are stored with the following format: // * prefix || hash160 || blockHeight || txoffset || txlen // Indexes are stored purely in the key, with blank data for the actual value // in order to facilitate ease of iteration by their shared prefix and // also to allow limiting the number of returned transactions (RPC). // Alternatively, indexes for each address could be stored as an // append-only list for the stored value. However, this add unnecessary // overhead when storing and retrieving since the entire list must // be fetched each time. func (db *LevelDb) UpdateAddrIndexForBlock(blkSha *btcwire.ShaHash, blkHeight int64, addrIndex database.BlockAddrIndex) error { db.dbLock.Lock() defer db.dbLock.Unlock() var blankData []byte batch := db.lBatch() defer db.lbatch.Reset() // Write all data for the new address indexes in a single batch // transaction. for addrKey, indexes := range addrIndex { for _, txLoc := range indexes { index := &txAddrIndex{ hash160: addrKey, blkHeight: blkHeight, txoffset: txLoc.TxStart, txlen: txLoc.TxLen, } // The index is stored purely in the key. packedIndex := addrIndexToKey(index) batch.Put(packedIndex, blankData) } } // Update tip of addrindex. newIndexTip := make([]byte, 40, 40) copy(newIndexTip[:32], blkSha.Bytes()) binary.LittleEndian.PutUint64(newIndexTip[32:], uint64(blkHeight)) batch.Put(addrIndexMetaDataKey, newIndexTip) if err := db.lDb.Write(batch, db.wo); err != nil { return err } db.lastAddrIndexBlkIdx = blkHeight db.lastAddrIndexBlkSha = *blkSha 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 }
// BlockLocatorFromHash returns a block locator for the passed block hash. // See BlockLocator for details on the algotirhm used to create a block locator. // // In addition to the general algorithm referenced above, there are a couple of // special cases which are handled: // // - If the genesis hash is passed, there are no previous hashes to add and // therefore the block locator will only consist of the genesis hash // - If the passed hash is not currently known, the block locator will only // consist of the passed hash func (b *BlockChain) BlockLocatorFromHash(hash *btcwire.ShaHash) BlockLocator { // The locator contains the requested hash at the very least. locator := make(BlockLocator, 0, btcwire.MaxBlockLocatorsPerMsg) locator = append(locator, hash) // Nothing more to do if a locator for the genesis hash was requested. if hash.IsEqual(b.netParams.GenesisHash) { return locator } // Attempt to find the height of the block that corresponds to the // passed hash, and if it's on a side chain, also find the height at // which it forks from the main chain. blockHeight := int64(-1) forkHeight := int64(-1) node, exists := b.index[*hash] if !exists { // Try to look up the height for passed block hash. Assume an // error means it doesn't exist and just return the locator for // the block itself. block, err := b.db.FetchBlockBySha(hash) if err != nil { return locator } blockHeight = block.Height() } else { blockHeight = node.height // Find the height at which this node forks from the main chain // if the node is on a side chain. if !node.inMainChain { for n := node; n.parent != nil; n = n.parent { if n.inMainChain { forkHeight = n.height break } } } } // Generate the block locators according to the algorithm described in // in the BlockLocator comment and make sure to leave room for the // final genesis hash. iterNode := node increment := int64(1) for len(locator) < btcwire.MaxBlockLocatorsPerMsg-1 { // Once there are 10 locators, exponentially increase the // distance between each block locator. if len(locator) > 10 { increment *= 2 } blockHeight -= increment if blockHeight < 1 { break } // As long as this is still on the side chain, walk backwards // along the side chain nodes to each block height. if forkHeight != -1 && blockHeight > forkHeight { // Intentionally use parent field instead of the // getPrevNodeFromNode function since we don't want to // dynamically load nodes when building block locators. // Side chain blocks should always be in memory already, // and if they aren't for some reason it's ok to skip // them. for iterNode != nil && blockHeight > iterNode.height { iterNode = iterNode.parent } if iterNode != nil && iterNode.height == blockHeight { locator = append(locator, iterNode.hash) } continue } // The desired block height is in the main chain, so look it up // from the main chain database. h, err := b.db.FetchBlockShaByHeight(blockHeight) if err != nil { // This shouldn't happen and it's ok to ignore block // locators, so just continue to the next one. log.Warnf("Lookup of known valid height failed %v", blockHeight) continue } locator = append(locator, h) } // Append the appropriate genesis block. locator = append(locator, b.netParams.GenesisHash) return locator }