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