View definition


Defined in


FetchBlockRegions returns the raw serialized bytes for the given block regions.

For example, it is possible to directly extract Bitcoin transactions and/or scripts from various blocks with this function. Depending on the backend implementation, this can provide significant savings by avoiding the need to load entire blocks.

The raw bytes are in the format returned by Serialize on a wire.MsgBlock and the Offset fields in the provided BlockRegions are zero-based and relative to the start of the block (byte 0).

Returns the following errors as required by the interface contract:

- ErrBlockNotFound if any of the request block hashes do not exist
- ErrBlockRegionInvalid if one or more region exceed the bounds of the
  associated block
- ErrTxClosed if the transaction has already been closed
- ErrCorruption if the database has somehow become corrupted

In addition, returns ErrDriverSpecific if any failures occur when reading the block files.

NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and allows support for memory-mapped database implementations.

This function is part of the database.Tx interface implementation.

FetchBlockRegions is referenced in 0 repositories


func (tx *transaction) FetchBlockRegions(regions []database.BlockRegion) ([][]byte, error) {
	// Ensure transaction state is valid.
	if err := tx.checkClosed(); err != nil {
		return nil, err

	// NOTE: This could check for the existence of all blocks before
	// deserializing the locations and building up the fetch list which
	// would be faster in the failure case, however callers will not
	// typically be calling this function with invalid values, so optimize
	// for the common case.

	// NOTE: A potential optimization here would be to combine adjacent
	// regions to reduce the number of reads.

	// In order to improve efficiency of loading the bulk data, first grab
	// the block location for all of the requested block hashes and sort
	// the reads by filenum:offset so that all reads are grouped by file
	// and linear within each file.  This can result in quite a significant
	// performance increase depending on how spread out the requested hashes
	// are by reducing the number of file open/closes and random accesses
	// needed.  The fetchList is intentionally allocated with a cap because
	// some of the regions might be fetched from the pending blocks and
	// hence there is no need to fetch those from disk.
	blockRegions := make([][]byte, len(regions))
	fetchList := make([]bulkFetchData, 0, len(regions))
	for i := range regions {
		region := &regions[i]

		// When the block is pending to be written on commit grab the
		// bytes from there.
		if tx.pendingBlocks != nil {
			regionBytes, err := tx.fetchPendingRegion(region)
			if err != nil {
				return nil, err
			if regionBytes != nil {
				blockRegions[i] = regionBytes

		// Lookup the location of the block in the files from the block
		// index.
		blockRow, err := tx.fetchBlockRow(region.Hash)
		if err != nil {
			return nil, err
		location := deserializeBlockLoc(blockRow)

		// Ensure the region is within the bounds of the block.
		endOffset := region.Offset + region.Len
		if endOffset < region.Offset || endOffset > location.blockLen {
			str := fmt.Sprintf("block %s region offset %d, length "+
				"%d exceeds block length of %d", region.Hash,
				region.Offset, region.Len, location.blockLen)
			return nil, makeDbErr(database.ErrBlockRegionInvalid, str, nil)

		fetchList = append(fetchList, bulkFetchData{&location, i})

	// Read all of the regions in the fetch list and set the results.
	for i := range fetchList {
		fetchData := &fetchList[i]
		ri := fetchData.replyIndex
		region := &regions[ri]
		location := fetchData.blockLocation
		regionBytes, err :=*location,
			region.Offset, region.Len)
		if err != nil {
			return nil, err
		blockRegions[ri] = regionBytes

	return blockRegions, nil