interface.go raw

   1  package database
   2  
   3  import (
   4  	"github.com/p9c/p9/pkg/block"
   5  	"github.com/p9c/p9/pkg/chainhash"
   6  )
   7  
   8  // Cursor represents a cursor over key/value pairs and nested buckets of a bucket.
   9  //
  10  // Note that open cursors are not tracked on bucket changes and any modifications to the bucket, with the exception of
  11  // Cursor.Delete, invalidates the cursor. After invalidation, the cursor must be repositioned, or the keys and values
  12  // returned may be unpredictable.
  13  type Cursor interface {
  14  	// Bucket returns the bucket the cursor was created for.
  15  	Bucket() Bucket
  16  	// Delete removes the current key/value pair the cursor is at without invalidating the cursor.
  17  	//
  18  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
  19  	// errors are possible):
  20  	//
  21  	//   - ErrIncompatibleValue if attempted when the cursor points to a nested bucket
  22  	//
  23  	//   - ErrTxNotWritable if attempted against a read-only transaction
  24  	//
  25  	//   - ErrTxClosed if the transaction has already been closed
  26  	Delete() error
  27  	// First positions the cursor at the first key/value pair and returns whether or not the pair exists.
  28  	First() bool
  29  	// Last positions the cursor at the last key/value pair and returns whether or not the pair exists.
  30  	Last() bool
  31  	// Next moves the cursor one key/value pair forward and returns whether or not the pair exists.
  32  	Next() bool
  33  	// Prev moves the cursor one key/value pair backward and returns whether or not the pair exists.
  34  	Prev() bool
  35  	// Seek positions the cursor at the first key/value pair that is greater than or equal to the passed seek key.
  36  	// Returns whether or not the pair exists.
  37  	Seek(seek []byte) bool
  38  	// Key returns the current key the cursor is pointing to.
  39  	Key() []byte
  40  	// Value returns the current value the cursor is pointing to. This will be nil for nested buckets.
  41  	Value() []byte
  42  }
  43  
  44  // Bucket represents a collection of key/value pairs.
  45  type Bucket interface {
  46  	// Bucket retrieves a nested bucket with the given key. Returns nil if the bucket does not exist.
  47  	Bucket(key []byte) Bucket
  48  	// CreateBucket creates and returns a new nested bucket with the given key.
  49  	//
  50  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
  51  	// errors are possible):
  52  	//
  53  	//   - ErrBucketExists if the bucket already exists
  54  	//
  55  	//   - ErrBucketNameRequired if the key is empty
  56  	//
  57  	//   - ErrIncompatibleValue if the key is otherwise invalid for the particular implementation
  58  	//
  59  	//   - ErrTxNotWritable if attempted against a read-only transaction
  60  	//
  61  	//   - ErrTxClosed if the transaction has already been closed
  62  	CreateBucket(key []byte) (Bucket, error)
  63  	// CreateBucketIfNotExists creates and returns a new nested bucket with the given key if it does not already exist.
  64  	//
  65  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
  66  	// errors are possible):
  67  	//
  68  	//   - ErrBucketNameRequired if the key is empty
  69  	//
  70  	//   - ErrIncompatibleValue if the key is otherwise invalid for the particular implementation
  71  	//
  72  	//   - ErrTxNotWritable if attempted against a read-only transaction
  73  	//
  74  	//   - ErrTxClosed if the transaction has already been closed
  75  	CreateBucketIfNotExists(key []byte) (Bucket, error)
  76  	// DeleteBucket removes a nested bucket with the given key. This also includes removing all nested buckets and keys
  77  	// under the bucket being deleted.
  78  	//
  79  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
  80  	// errors are possible):
  81  	//
  82  	//   - ErrBucketNotFound if the specified bucket does not exist
  83  	//
  84  	//   - ErrTxNotWritable if attempted against a read-only transaction
  85  	//
  86  	//   - ErrTxClosed if the transaction has already been closed
  87  	DeleteBucket(key []byte) error
  88  	// ForEach invokes the passed function with every key/value pair in the bucket. This does not include nested buckets
  89  	// or the key/value pairs within those nested buckets.
  90  	//
  91  	// WARNING: It is not safe to mutate data while iterating with this method. Doing so may cause the underlying cursor
  92  	// to be invalidated and return unexpected keys and/or values.
  93  	//
  94  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
  95  	// errors are possible):
  96  	//
  97  	//   - ErrTxClosed if the transaction has already been closed
  98  	//
  99  	// NOTE: The slices returned by this function are only valid during a transaction. Attempting to access them after a
 100  	// transaction has ended results in undefined behavior. Additionally, the slices must NOT be modified by the caller.
 101  	// These constraints prevent additional data copies and allows support for memory-mapped database implementations.
 102  	ForEach(func(k, v []byte) error) error
 103  	// ForEachBucket invokes the passed function with the key of every nested bucket in the current bucket. This does
 104  	// not include any nested buckets within those nested buckets.
 105  	//
 106  	// WARNING: It is not safe to mutate data while iterating with this method. Doing so may cause the underlying cursor
 107  	// to be invalidated and return unexpected keys and/or values.
 108  	//
 109  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 110  	// errors are possible):
 111  	//
 112  	//   - ErrTxClosed if the transaction has already been closed
 113  	//
 114  	// NOTE: The keys returned by this function are only valid during a transaction. Attempting to access them after a
 115  	// transaction has ended results in undefined behavior. This constraint prevents additional data copies and allows
 116  	// support for memory-mapped database implementations.
 117  	ForEachBucket(func(k []byte) error) error
 118  	// Cursor returns a new cursor, allowing for iteration over the bucket's key/value pairs and nested buckets in
 119  	// forward or backward order.
 120  	//
 121  	// You must seek to a position using the First, Last, or Seek functions before calling the Next, Prev, Key, or value
 122  	// functions. Failure to do so will result in the same return values as an exhausted cursor, which is false for the
 123  	// Prev and Next functions and nil for Key and value functions.
 124  	Cursor() Cursor
 125  	// Writable returns whether or not the bucket is writable.
 126  	Writable() bool
 127  	// Put saves the specified key/value pair to the bucket. Keys that do not already exist are added and keys that
 128  	// already exist are overwritten.
 129  	//
 130  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 131  	// errors are possible):
 132  	//
 133  	//   - ErrKeyRequired if the key is empty
 134  	//
 135  	//   - ErrIncompatibleValue if the key is the same as an existing bucket
 136  	//
 137  	//   - ErrTxNotWritable if attempted against a read-only transaction
 138  	//
 139  	//   - ErrTxClosed if the transaction has already been closed
 140  	//
 141  	// NOTE: The slices passed to this function must NOT be modified by the caller. This constraint prevents the
 142  	// requirement for additional data copies and allows support for memory-mapped database implementations.
 143  	Put(key, value []byte) error
 144  	// Get returns the value for the given key. Returns nil if the key does not exist in this bucket. An empty slice is
 145  	// returned for keys that exist but have no value assigned.
 146  	//
 147  	// NOTE: The value returned by this function is only valid during a transaction. Attempting to access it after a
 148  	// transaction has ended results in undefined behavior. Additionally, the value must NOT be modified by the caller.
 149  	//
 150  	// These constraints prevent additional data copies and allows support for memory-mapped database implementations.
 151  	Get(key []byte) []byte
 152  	// Delete removes the specified key from the bucket. Deleting a key that does not exist does not return an error.
 153  	//
 154  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 155  	// errors are possible):
 156  	//
 157  	//   - ErrKeyRequired if the key is empty
 158  	//
 159  	//   - ErrIncompatibleValue if the key is the same as an existing bucket
 160  	//
 161  	//   - ErrTxNotWritable if attempted against a read-only transaction
 162  	//
 163  	//   - ErrTxClosed if the transaction has already been closed
 164  	Delete(key []byte) error
 165  }
 166  
 167  // BlockRegion specifies a particular region of a block identified by the specified hash, given an offset and length.
 168  type BlockRegion struct {
 169  	Hash   *chainhash.Hash
 170  	Offset uint32
 171  	Len    uint32
 172  }
 173  
 174  // Tx represents a database transaction. It can either by read-only or read-write. The transaction provides a metadata
 175  // bucket against which all read and writes occur. As would be expected with a transaction, no changes will be saved to
 176  // the database until it has been committed. The transaction will only provide a view of the database at the time it was
 177  // created. Transactions should not be long running operations.
 178  type Tx interface {
 179  	// Metadata returns the top-most bucket for all metadata storage.
 180  	Metadata() Bucket
 181  	// StoreBlock stores the provided block into the database. There are no checks to ensure the block connects to a
 182  	// previous block, contains double spends, or any additional functionality such as transaction indexing. It simply
 183  	// stores the block in the database.
 184  	//
 185  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 186  	// errors are possible):
 187  	//
 188  	//   - ErrBlockExists when the block hash already exists
 189  	//
 190  	//   - ErrTxNotWritable if attempted against a read-only transaction
 191  	//
 192  	//   - ErrTxClosed if the transaction has already been closed
 193  	//
 194  	// Other errors are possible depending on the implementation.
 195  	StoreBlock(block *block.Block) error
 196  	// HasBlock returns whether or not a block with the given hash exists in the database.
 197  	//
 198  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 199  	// errors are possible):
 200  	//
 201  	//   - ErrTxClosed if the transaction has already been closed
 202  	//
 203  	// Other errors are possible depending on the implementation.
 204  	HasBlock(hash *chainhash.Hash) (bool, error)
 205  	// HasBlocks returns whether or not the blocks with the provided hashes
 206  	// exist in the database.
 207  	//
 208  	// The interface contract guarantees at least the following errors will
 209  	// be returned (other implementation-specific errors are possible):
 210  	//
 211  	//   - ErrTxClosed if the transaction has already been closed
 212  	//
 213  	// Other errors are possible depending on the implementation.
 214  	HasBlocks(hashes []chainhash.Hash) ([]bool, error)
 215  	// FetchBlockHeader returns the raw serialized bytes for the block header identified by the given hash. The raw
 216  	// bytes are in the format returned by Serialize on a wire.BlockHeader.
 217  	//
 218  	// It is highly recommended to use this function (or FetchBlockHeaders) to obtain block headers over the
 219  	// FetchBlockRegion(s) functions since it provides the backend drivers the freedom to perform very specific
 220  	// optimizations which can result in significant speed advantages when working with headers.
 221  	//
 222  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 223  	// errors are possible):
 224  	//
 225  	//   - ErrBlockNotFound if the requested block hash does not exist
 226  	//
 227  	//   - ErrTxClosed if the transaction has already been closed
 228  	//
 229  	//   - ErrCorruption if the database has somehow become corrupted
 230  	//
 231  	// NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it
 232  	// after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and
 233  	// allows support for memory-mapped database implementations.
 234  	FetchBlockHeader(hash *chainhash.Hash) ([]byte, error)
 235  	// FetchBlockHeaders returns the raw serialized bytes for the block headers identified by the given hashes. The raw
 236  	// bytes are in the format returned by Serialize on a wire.BlockHeader.
 237  	//
 238  	// It is highly recommended to use this function (or FetchBlockHeader) to obtain block headers over the
 239  	// FetchBlockRegion(s) functions since it provides the backend drivers the freedom to perform very specific
 240  	// optimizations which can result in significant speed advantages when working with headers.
 241  	//
 242  	// Furthermore, depending on the specific implementation, this function can be more efficient for bulk loading
 243  	// multiple block headers than loading them one-by-one with FetchBlockHeader.
 244  	//
 245  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 246  	// errors are possible):
 247  	//
 248  	//   - ErrBlockNotFound if any of the request block hashes do not exist
 249  	//
 250  	//   - ErrTxClosed if the transaction has already been closed
 251  	//
 252  	//   - ErrCorruption if the database has somehow become corrupted
 253  	//
 254  	// NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it
 255  	// after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and
 256  	// allows support for memory-mapped database implementations.
 257  	FetchBlockHeaders(hashes []chainhash.Hash) ([][]byte, error)
 258  	// FetchBlock returns the raw serialized bytes for the block identified by the given hash. The raw bytes are in the
 259  	// format returned by Serialize on a wire.WireBlock.
 260  	//
 261  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 262  	// errors are possible):
 263  	//
 264  	//   - ErrBlockNotFound if the requested block hash does not exist
 265  	//
 266  	//   - ErrTxClosed if the transaction has already been closed
 267  	//
 268  	//   - ErrCorruption if the database has somehow become corrupted
 269  	//
 270  	// NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it
 271  	// after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and
 272  	// allows support for memory-mapped database implementations.
 273  	FetchBlock(hash *chainhash.Hash) ([]byte, error)
 274  	// FetchBlocks returns the raw serialized bytes for the blocks identified by the given hashes. The raw bytes are in
 275  	// the format returned by Serialize on a wire.WireBlock.
 276  	//
 277  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 278  	// errors are possible):
 279  	//
 280  	//   - ErrBlockNotFound if the any of the requested block hashes do not exist
 281  	//
 282  	//   - ErrTxClosed if the transaction has already been closed
 283  	//
 284  	//   - ErrCorruption if the database has somehow become corrupted
 285  	//
 286  	// NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it
 287  	// after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and
 288  	// allows support for memory-mapped database implementations.
 289  	FetchBlocks(hashes []chainhash.Hash) ([][]byte, error)
 290  	// FetchBlockRegion returns the raw serialized bytes for the given block region.
 291  	//
 292  	// For example, it is possible to directly extract Bitcoin transactions and/or scripts from a block with this
 293  	// function. Depending on the backend implementation, this can provide significant savings by avoiding the need to
 294  	// load entire blocks.
 295  	//
 296  	// The raw bytes are in the format returned by Serialize on a wire.WireBlock and the Offset field in the provided
 297  	// BlockRegion is zero-based and relative to the start of the block (byte 0).
 298  	//
 299  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 300  	// errors are possible):
 301  	//
 302  	//   - ErrBlockNotFound if the requested block hash does not exist
 303  	//
 304  	//   - ErrBlockRegionInvalid if the region exceeds the bounds of the
 305  	//   associated block
 306  	//
 307  	//   - ErrTxClosed if the transaction has already been closed
 308  	//
 309  	//   - ErrCorruption if the database has somehow become corrupted
 310  	//
 311  	// NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it
 312  	// after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and
 313  	// allows support for memory-mapped database implementations.
 314  	FetchBlockRegion(region *BlockRegion) ([]byte, error)
 315  	// FetchBlockRegions returns the raw serialized bytes for the given block regions.
 316  	//
 317  	// For example, it is possible to directly extract Bitcoin transactions and/or scripts from various blocks with this
 318  	// function. Depending on the backend implementation, this can provide significant savings by avoiding the need to
 319  	// load entire blocks.
 320  	//
 321  	// The raw bytes are in the format returned by Serialize on a wire.WireBlock and the Offset fields in the provided
 322  	// BlockRegions are zero-based and relative to the start of the block (byte 0).
 323  	//
 324  	// The interface contract guarantees at least the following errors will be returned (other implementation-specific
 325  	// errors are possible):
 326  	//
 327  	//   - ErrBlockNotFound if any of the requested block hashed do not exist
 328  	//
 329  	//   - ErrBlockRegionInvalid if one or more region exceed the bounds of the associated block
 330  	//
 331  	//   - ErrTxClosed if the transaction has already been closed
 332  	//
 333  	//   - ErrCorruption if the database has somehow become corrupted
 334  	//
 335  	// NOTE: The data returned by this function is only valid during a database transaction. Attempting to access it
 336  	// after a transaction has ended results in undefined behavior. This constraint prevents additional data copies and
 337  	// allows support for memory-mapped database implementations.
 338  	FetchBlockRegions(regions []BlockRegion) ([][]byte, error)
 339  	// Commit commits all changes that have been made to the metadata or block storage. Depending on the backend
 340  	// implementation this could be to a cache that is periodically synced to persistent storage or directly to
 341  	// persistent storage.
 342  	//
 343  	// In any case, all transactions which are started after the commit finishes will include all changes made by this
 344  	// transaction. Calling this function on a managed transaction will result in a panic.
 345  	Commit() error
 346  	// Rollback undoes all changes that have been made to the metadata or block storage. Calling this function on a
 347  	// managed transaction will result in a panic.
 348  	Rollback() error
 349  }
 350  
 351  // DB provides a generic interface that is used to store bitcoin blocks and related metadata. This interface is intended
 352  // to be agnostic to the actual mechanism used for backend data storage. The RegisterDriver function can be used to add
 353  // a new backend data storage method.
 354  //
 355  // This interface is divided into two distinct categories of functionality.
 356  //
 357  // The first category is atomic metadata storage with bucket support. This is accomplished through the use of database
 358  // transactions.
 359  //
 360  // The second category is generic block storage. This functionality is intentionally separate because the mechanism used
 361  // for block storage may or may not be the same mechanism used for metadata storage. For example, it is often more
 362  // efficient to store the block data as flat files while the metadata is kept in a database. However, this interface
 363  // aims to be generic enough to support blocks in the database too, if needed by a particular backend.
 364  type DB interface {
 365  	// Type returns the database driver type the current database instance was created with.
 366  	Type() string
 367  	// Begin starts a transaction which is either read-only or read-write depending on the specified flag. Multiple
 368  	// read-only transactions can be started simultaneously while only a single read-write transaction can be started at
 369  	// a time. The call will block when starting a read-write transaction when one is already open.
 370  	//
 371  	// NOTE: The transaction must be closed by calling Rollback or Commit on it when it is no longer needed. Failure to
 372  	// do so can result in unclaimed memory and/or inablity to close the database due to locks depending on the specific
 373  	// database implementation.
 374  	Begin(writable bool) (Tx, error)
 375  	// View invokes the passed function in the context of a managed read-only transaction. Any errors returned from the
 376  	// user-supplied function are returned from this function.
 377  	//
 378  	// Calling Rollback or Commit on the transaction passed to the user-supplied function will result in a panic.
 379  	View(fn func(tx Tx) error) error
 380  	// Update invokes the passed function in the context of a managed read-write transaction. Any errors returned from
 381  	// the user-supplied function will cause the transaction to be rolled back and are returned from this function.
 382  	// Otherwise, the transaction is committed when the user-supplied function returns a nil error.
 383  	//
 384  	// Calling Rollback or Commit on the transaction passed to the user-supplied function will result in a panic.
 385  	Update(fn func(tx Tx) error) error
 386  	// Close cleanly shuts down the database and syncs all data. It will block until all database transactions have been
 387  	// finalized (rolled back or committed).
 388  	Close() error
 389  }
 390