1 package wtxmgr
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7 "github.com/p9c/p9/pkg/amt"
8 "time"
9
10 "github.com/p9c/p9/pkg/chainhash"
11 "github.com/p9c/p9/pkg/walletdb"
12 "github.com/p9c/p9/pkg/wire"
13 )
14
15 // Naming
16 //
17 // The following variables are commonly used in this file and given reserved names:
18 //
19 // ns: The namespace bucket for this package
20 // b: The primary bucket being operated on
21 // k: A single bucket key
22 // v: A single bucket value
23 // c: A bucket cursor
24 // ck: The current cursor key
25 // cv: The current cursor value
26 //
27 // Functions use the naming scheme `Op[Raw]Type[Field]`, which performs the operation `Op` on the type `Type`,
28 // optionally dealing with raw keys and values if `Raw` is used. Fetch and extract operations may only need to read some
29 // portion of a key or value, in which case `Field` describes the component being returned. The following operations are
30 // used:
31 //
32 // key: return a db key for some data
33 // value: return a db value for some data
34 // put: insert or replace a value into a bucket
35 // fetch: read and return a value
36 // read: read a value into an out parameter
37 // exists: return the raw (nil if not found) value for some data
38 // delete: remove a k/v pair
39 // extract: perform an unchecked slice to extract a key or value
40 //
41 // Other operations which are specific to the types being operated on should be explained in a comment. Big endian is
42 // the preferred byte order, due to cursor scans over integer keys iterating in order.
43 var byteOrder = binary.BigEndian
44
45 // Database versions. Versions start at 1 and increment for each database change.
46 const (
47 // LatestVersion is the most recent store version.
48 LatestVersion = 1
49 )
50
51 var (
52 // This package makes assumptions that the width of a chainhash.Hash is always 32 bytes. If this is ever changed
53 // (unlikely for bitcoin, possible for alts), offsets have to be rewritten. Use a compile-time assertion that this
54 // assumption holds true.
55 _ [32]byte = chainhash.Hash{}
56 // Bucket names
57 bucketBlocks = []byte("b")
58 bucketTxRecords = []byte("t")
59 bucketCredits = []byte("c")
60 bucketUnspent = []byte("u")
61 bucketDebits = []byte("d")
62 bucketUnmined = []byte("m")
63 bucketUnminedCredits = []byte("mc")
64 bucketUnminedInputs = []byte("mi")
65 // Root (namespace) bucket keys
66 rootCreateDate = []byte("date")
67 rootVersion = []byte("vers")
68 rootMinedBalance = []byte("bal")
69 )
70
71 // The root bucket's mined balance k/v pair records the total balance for all unspent credits from mined transactions.
72 // This includes immature outputs, and outputs spent by mempool transactions, which must be considered when returning
73 // the actual balance for a given number of block confirmations. The value is the amount serialized as a uint64.
74 func fetchMinedBalance(ns walletdb.ReadBucket) (amt.Amount, error) {
75 v := ns.Get(rootMinedBalance)
76 if len(v) != 8 {
77 str := fmt.Sprintf(
78 "balance: short read (expected 8 bytes, "+
79 "read %v)", len(v),
80 )
81 return 0, storeError(ErrData, str, nil)
82 }
83 return amt.Amount(byteOrder.Uint64(v)), nil
84 }
85 func putMinedBalance(ns walletdb.ReadWriteBucket, amt amt.Amount) (e error) {
86 v := make([]byte, 8)
87 byteOrder.PutUint64(v, uint64(amt))
88 e = ns.Put(rootMinedBalance, v)
89 if e != nil {
90 str := "failed to put balance"
91 return storeError(ErrDatabase, str, e)
92 }
93 return nil
94 }
95
96 // Several data structures are given canonical serialization formats as either keys or values. These common formats
97 // allow keys and values to be reused across different buckets.
98 //
99 // The canonical outpoint serialization format is:
100 //
101 // [0:32] Trasaction hash (32 bytes)
102 // [32:36] Output index (4 bytes)
103 //
104 // The canonical transaction hash serialization is simply the hash.
105 func canonicalOutPoint(txHash *chainhash.Hash, index uint32) []byte {
106 k := make([]byte, 36)
107 copy(k, txHash[:])
108 byteOrder.PutUint32(k[32:36], index)
109 return k
110 }
111 func readCanonicalOutPoint(k []byte, op *wire.OutPoint) (e error) {
112 if len(k) < 36 {
113 str := "short canonical outpoint"
114 return storeError(ErrData, str, nil)
115 }
116 copy(op.Hash[:], k)
117 op.Index = byteOrder.Uint32(k[32:36])
118 return nil
119 }
120
121 // Details regarding blocks are saved as k/v pairs in the blocks bucket. blockRecords are keyed by their height. The
122 // value is serialized as such:
123 //
124 // [0:32] Hash (32 bytes)
125 // [32:40] Unix time (8 bytes)
126 // [40:44] Number of transaction hashes (4 bytes)
127 // [44:] For each transaction hash:
128 // Hash (32 bytes)
129 func keyBlockRecord(height int32) []byte {
130 k := make([]byte, 4)
131 byteOrder.PutUint32(k, uint32(height))
132 return k
133 }
134 func valueBlockRecord(block *BlockMeta, txHash *chainhash.Hash) []byte {
135 v := make([]byte, 76)
136 copy(v, block.Hash[:])
137 byteOrder.PutUint64(v[32:40], uint64(block.Time.Unix()))
138 byteOrder.PutUint32(v[40:44], 1)
139 copy(v[44:76], txHash[:])
140 return v
141 }
142
143 // appendRawBlockRecord returns a new block record value with a transaction hash appended to the end and an incremented
144 // number of transactions.
145 func appendRawBlockRecord(v []byte, txHash *chainhash.Hash) ([]byte, error) {
146 if len(v) < 44 {
147 str := fmt.Sprintf(
148 "%s: short read (expected %d bytes, read %d)",
149 bucketBlocks, 44, len(v),
150 )
151 return nil, storeError(ErrData, str, nil)
152 }
153 newv := append(v[:len(v):len(v)], txHash[:]...)
154 n := byteOrder.Uint32(newv[40:44])
155 byteOrder.PutUint32(newv[40:44], n+1)
156 return newv, nil
157 }
158 func putRawBlockRecord(ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
159 e = ns.NestedReadWriteBucket(bucketBlocks).Put(k, v)
160 if e != nil {
161 str := "failed to store block"
162 return storeError(ErrDatabase, str, e)
163 }
164 return nil
165 }
166 func putBlockRecord(ns walletdb.ReadWriteBucket, block *BlockMeta, txHash *chainhash.Hash) (e error) {
167 k := keyBlockRecord(block.Height)
168 v := valueBlockRecord(block, txHash)
169 return putRawBlockRecord(ns, k, v)
170 }
171 func fetchBlockTime(ns walletdb.ReadBucket, height int32) (time.Time, error) {
172 k := keyBlockRecord(height)
173 v := ns.NestedReadBucket(bucketBlocks).Get(k)
174 if len(v) < 44 {
175 str := fmt.Sprintf(
176 "%s: short read (expected %d bytes, read %d)",
177 bucketBlocks, 44, len(v),
178 )
179 return time.Time{}, storeError(ErrData, str, nil)
180 }
181 return time.Unix(int64(byteOrder.Uint64(v[32:40])), 0), nil
182 }
183 func existsBlockRecord(ns walletdb.ReadBucket, height int32) (k, v []byte) {
184 k = keyBlockRecord(height)
185 v = ns.NestedReadBucket(bucketBlocks).Get(k)
186 return
187 }
188 func readRawBlockRecord(k, v []byte, block *blockRecord) (e error) {
189 if len(k) < 4 {
190 str := fmt.Sprintf(
191 "%s: short key (expected %d bytes, read %d)",
192 bucketBlocks, 4, len(k),
193 )
194 return storeError(ErrData, str, nil)
195 }
196 if len(v) < 44 {
197 str := fmt.Sprintf(
198 "%s: short read (expected %d bytes, read %d)",
199 bucketBlocks, 44, len(v),
200 )
201 return storeError(ErrData, str, nil)
202 }
203 numTransactions := int(byteOrder.Uint32(v[40:44]))
204 expectedLen := 44 + chainhash.HashSize*numTransactions
205 if len(v) < expectedLen {
206 str := fmt.Sprintf(
207 "%s: short read (expected %d bytes, read %d)",
208 bucketBlocks, expectedLen, len(v),
209 )
210 return storeError(ErrData, str, nil)
211 }
212 block.Height = int32(byteOrder.Uint32(k))
213 copy(block.Hash[:], v)
214 block.Time = time.Unix(int64(byteOrder.Uint64(v[32:40])), 0)
215 block.transactions = make([]chainhash.Hash, numTransactions)
216 off := 44
217 for i := range block.transactions {
218 copy(block.transactions[i][:], v[off:])
219 off += chainhash.HashSize
220 }
221 return nil
222 }
223
224 type blockIterator struct {
225 c walletdb.ReadWriteCursor
226 seek []byte
227 ck []byte
228 cv []byte
229 elem blockRecord
230 err error
231 }
232
233 // func makeBlockIterator(ns walletdb.ReadWriteBucket, height int32) blockIterator {
234 // seek := make([]byte, 4)
235 // byteOrder.PutUint32(seek, uint32(height))
236 // c := ns.NestedReadWriteBucket(bucketBlocks).ReadWriteCursor()
237 // return blockIterator{c: c, seek: seek}
238 // }
239 func makeReadBlockIterator(ns walletdb.ReadBucket, height int32) blockIterator {
240 seek := make([]byte, 4)
241 byteOrder.PutUint32(seek, uint32(height))
242 c := ns.NestedReadBucket(bucketBlocks).ReadCursor()
243 return blockIterator{c: readCursor{c}, seek: seek}
244 }
245
246 // Works just like makeBlockIterator but will initially position the cursor at the last k/v pair. Use this with
247 // blockIterator.prev.
248 func makeReverseBlockIterator(ns walletdb.ReadWriteBucket) blockIterator {
249 seek := make([]byte, 4)
250 byteOrder.PutUint32(seek, ^uint32(0))
251 c := ns.NestedReadWriteBucket(bucketBlocks).ReadWriteCursor()
252 return blockIterator{c: c, seek: seek}
253 }
254 func makeReadReverseBlockIterator(ns walletdb.ReadBucket) blockIterator {
255 seek := make([]byte, 4)
256 byteOrder.PutUint32(seek, ^uint32(0))
257 c := ns.NestedReadBucket(bucketBlocks).ReadCursor()
258 return blockIterator{c: readCursor{c}, seek: seek}
259 }
260 func (it *blockIterator) next() bool {
261 if it.c == nil {
262 return false
263 }
264 if it.ck == nil {
265 it.ck, it.cv = it.c.Seek(it.seek)
266 } else {
267 it.ck, it.cv = it.c.Next()
268 }
269 if it.ck == nil {
270 it.c = nil
271 return false
272 }
273 e := readRawBlockRecord(it.ck, it.cv, &it.elem)
274 if e != nil {
275 it.c = nil
276 it.err = e
277 return false
278 }
279 return true
280 }
281 func (it *blockIterator) prev() bool {
282 if it.c == nil {
283 return false
284 }
285 if it.ck == nil {
286 it.ck, it.cv = it.c.Seek(it.seek)
287 // Seek positions the cursor at the next k/v pair if one with this prefix was not found. If this happened (the
288 // prefixes won't match in this case) move the cursor backward.
289 //
290 // This technically does not correct for multiple keys with matching prefixes by moving the cursor to the last
291 // matching key, but this doesn't need to be considered when dealing with block records since the key (and seek
292 // prefix) is just the block height.
293 if !bytes.HasPrefix(it.ck, it.seek) {
294 it.ck, it.cv = it.c.Prev()
295 }
296 } else {
297 it.ck, it.cv = it.c.Prev()
298 }
299 if it.ck == nil {
300 it.c = nil
301 return false
302 }
303 e := readRawBlockRecord(it.ck, it.cv, &it.elem)
304 if e != nil {
305 it.c = nil
306 it.err = e
307 return false
308 }
309 return true
310 }
311
312 // unavailable until https://github.com/boltdb/bolt/issues/620 is fixed.
313 // func (it *blockIterator) delete() (e error) {
314 // e := it.c.Delete()
315 // if e != nil {
316 // DB// str := "failed to delete block record"
317 // storeError(ErrDatabase, str, err)
318 // }
319 // return nil
320 // }
321 func (it *blockIterator) reposition(height int32) {
322 it.c.Seek(keyBlockRecord(height))
323 }
324 func deleteBlockRecord(ns walletdb.ReadWriteBucket, height int32) (e error) {
325 k := keyBlockRecord(height)
326 return ns.NestedReadWriteBucket(bucketBlocks).Delete(k)
327 }
328
329 // Transaction records are keyed as such:
330 //
331 // [0:32] Transaction hash (32 bytes)
332 // [32:36] Block height (4 bytes)
333 // [36:68] Block hash (32 bytes)
334 //
335 // The leading transaction hash allows to prefix filter for all records with a matching hash. The block height and hash
336 // records a particular incidence of the transaction in the blockchain.
337 //
338 // The record value is serialized as such:
339 //
340 // [0:8] Received time (8 bytes)
341 // [8:] Serialized transaction (varies)
342 func keyTxRecord(txHash *chainhash.Hash, block *Block) []byte {
343 k := make([]byte, 68)
344 copy(k, txHash[:])
345 byteOrder.PutUint32(k[32:36], uint32(block.Height))
346 copy(k[36:68], block.Hash[:])
347 return k
348 }
349 func valueTxRecord(rec *TxRecord) ([]byte, error) {
350 var v []byte
351 if rec.SerializedTx == nil {
352 txSize := rec.MsgTx.SerializeSize()
353 v = make([]byte, 8, 8+txSize)
354 e := rec.MsgTx.Serialize(bytes.NewBuffer(v[8:]))
355 if e != nil {
356 str := fmt.Sprintf("unable to serialize transaction %v", rec.Hash)
357 return nil, storeError(ErrInput, str, e)
358 }
359 v = v[:cap(v)]
360 } else {
361 v = make([]byte, 8+len(rec.SerializedTx))
362 copy(v[8:], rec.SerializedTx)
363 }
364 byteOrder.PutUint64(v, uint64(rec.Received.Unix()))
365 return v, nil
366 }
367 func putTxRecord(ns walletdb.ReadWriteBucket, rec *TxRecord, block *Block) (e error) {
368 k := keyTxRecord(&rec.Hash, block)
369 v, e := valueTxRecord(rec)
370 if e != nil {
371 return e
372 }
373 e = ns.NestedReadWriteBucket(bucketTxRecords).Put(k, v)
374 if e != nil {
375 str := fmt.Sprintf("%s: put failed for %v", bucketTxRecords, rec.Hash)
376 return storeError(ErrDatabase, str, e)
377 }
378 return nil
379 }
380
381 // func putRawTxRecord(// ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
382 // e := ns.NestedReadWriteBucket(bucketTxRecords).Put(k, v)
383 // if e != nil {
384 // DB// str := fmt.Sprintf("%s: put failed", bucketTxRecords)
385 // return storeError(ErrDatabase, str, err)
386 // }
387 // return nil
388 // }
389 func readRawTxRecord(txHash *chainhash.Hash, v []byte, rec *TxRecord) (e error) {
390 if len(v) < 8 {
391 str := fmt.Sprintf(
392 "%s: short read (expected %d bytes, read %d)",
393 bucketTxRecords, 8, len(v),
394 )
395 return storeError(ErrData, str, nil)
396 }
397 rec.Hash = *txHash
398 rec.Received = time.Unix(int64(byteOrder.Uint64(v)), 0)
399 e = rec.MsgTx.Deserialize(bytes.NewReader(v[8:]))
400 if e != nil {
401 str := fmt.Sprintf(
402 "%s: failed to deserialize transaction %v",
403 bucketTxRecords, txHash,
404 )
405 return storeError(ErrData, str, e)
406 }
407 return nil
408 }
409 func readRawTxRecordBlock(k []byte, block *Block) (e error) {
410 if len(k) < 68 {
411 str := fmt.Sprintf(
412 "%s: short key (expected %d bytes, read %d)",
413 bucketTxRecords, 68, len(k),
414 )
415 return storeError(ErrData, str, nil)
416 }
417 block.Height = int32(byteOrder.Uint32(k[32:36]))
418 copy(block.Hash[:], k[36:68])
419 return nil
420 }
421
422 func fetchTxRecord(ns walletdb.ReadBucket, txHash *chainhash.Hash, block *Block) (rec *TxRecord, e error) {
423 k := keyTxRecord(txHash, block)
424 v := ns.NestedReadBucket(bucketTxRecords).Get(k)
425 rec = new(TxRecord)
426 e = readRawTxRecord(txHash, v, rec)
427 return rec, e
428 }
429
430 // TODO: This reads more than necessary. Pass the pkscript location instead to
431 // avoid the wire.MsgTx deserialization.
432 func fetchRawTxRecordPkScript(k, v []byte, index uint32) ([]byte, error) {
433 var rec TxRecord
434 copy(rec.Hash[:], k) // Silly but need an array
435 e := readRawTxRecord(&rec.Hash, v, &rec)
436 if e != nil {
437 return nil, e
438 }
439 if int(index) >= len(rec.MsgTx.TxOut) {
440 str := "missing transaction output for credit index"
441 return nil, storeError(ErrData, str, nil)
442 }
443 return rec.MsgTx.TxOut[index].PkScript, nil
444 }
445 func existsTxRecord(ns walletdb.ReadBucket, txHash *chainhash.Hash, block *Block) (k, v []byte) {
446 k = keyTxRecord(txHash, block)
447 v = ns.NestedReadBucket(bucketTxRecords).Get(k)
448 return
449 }
450
451 func existsRawTxRecord(ns walletdb.ReadBucket, k []byte) (v []byte) {
452 return ns.NestedReadBucket(bucketTxRecords).Get(k)
453 }
454
455 func deleteTxRecord(ns walletdb.ReadWriteBucket, txHash *chainhash.Hash, block *Block) (e error) {
456 k := keyTxRecord(txHash, block)
457 return ns.NestedReadWriteBucket(bucketTxRecords).Delete(k)
458 }
459
460 // latestTxRecord searches for the newest recorded mined transaction record with a matching hash. In case of a hash
461 // collision, the record from the newest block is returned. Returns (nil, nil) if no matching transactions are found.
462 func latestTxRecord(ns walletdb.ReadBucket, txHash *chainhash.Hash) (k, v []byte) {
463 prefix := txHash[:]
464 c := ns.NestedReadBucket(bucketTxRecords).ReadCursor()
465 ck, cv := c.Seek(prefix)
466 var lastKey, lastVal []byte
467 for bytes.HasPrefix(ck, prefix) {
468 lastKey, lastVal = ck, cv
469 ck, cv = c.Next()
470 }
471 return lastKey, lastVal
472 }
473
474 // All transaction credits (outputs) are keyed as such:
475 //
476 // [0:32] Transaction hash (32 bytes)
477 // [32:36] Block height (4 bytes)
478 // [36:68] Block hash (32 bytes)
479 // [68:72] Output index (4 bytes)
480 //
481 // The first 68 bytes match the key for the transaction record and may be used as a prefix filter to iterate through all
482 // credits in order.
483 //
484 // The credit value is serialized as such:
485 //
486 // [0:8] Amount (8 bytes)
487 // [8] Flags (1 byte)
488 // 0x01: Spent
489 // 0x02: Change
490 // [9:81] OPTIONAL Debit bucket key (72 bytes)
491 // [9:41] Spender transaction hash (32 bytes)
492 // [41:45] Spender block height (4 bytes)
493 // [45:77] Spender block hash (32 bytes)
494 // [77:81] Spender transaction input index (4 bytes)
495 //
496 // The optional debits key is only included if the credit is spent by another
497 // mined debit.
498 func keyCredit(txHash *chainhash.Hash, index uint32, block *Block) []byte {
499 k := make([]byte, 72)
500 copy(k, txHash[:])
501 byteOrder.PutUint32(k[32:36], uint32(block.Height))
502 copy(k[36:68], block.Hash[:])
503 byteOrder.PutUint32(k[68:72], index)
504 return k
505 }
506
507 // valueUnspentCredit creates a new credit value for an unspent credit. All credits are created unspent, and are only
508 // marked spent later, so there is no value function to create either spent or unspent credits.
509 func valueUnspentCredit(cred *credit) []byte {
510 v := make([]byte, 9)
511 byteOrder.PutUint64(v, uint64(cred.amount))
512 if cred.change {
513 v[8] |= 1 << 1
514 }
515 return v
516 }
517 func putRawCredit(ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
518 e = ns.NestedReadWriteBucket(bucketCredits).Put(k, v)
519 if e != nil {
520 str := "failed to put credit"
521 return storeError(ErrDatabase, str, e)
522 }
523 return nil
524 }
525
526 // putUnspentCredit puts a credit record for an unspent credit. It may only be used when the credit is already know to
527 // be unspent, or spent by an unconfirmed transaction.
528 func putUnspentCredit(ns walletdb.ReadWriteBucket, cred *credit) (e error) {
529 k := keyCredit(&cred.outPoint.Hash, cred.outPoint.Index, &cred.block)
530 v := valueUnspentCredit(cred)
531 return putRawCredit(ns, k, v)
532 }
533 func extractRawCreditTxRecordKey(k []byte) []byte {
534 return k[0:68]
535 }
536 func extractRawCreditIndex(k []byte) uint32 {
537 return byteOrder.Uint32(k[68:72])
538 }
539
540 // fetchRawCreditAmount returns the amount of the credit.
541 func fetchRawCreditAmount(v []byte) (amt.Amount, error) {
542 if len(v) < 9 {
543 str := fmt.Sprintf(
544 "%s: short read (expected %d bytes, read %d)",
545 bucketCredits, 9, len(v),
546 )
547 return 0, storeError(ErrData, str, nil)
548 }
549 return amt.Amount(byteOrder.Uint64(v)), nil
550 }
551
552 // fetchRawCreditAmountSpent returns the amount of the credit and whether the credit is spent.
553 func fetchRawCreditAmountSpent(v []byte) (amt.Amount, bool, error) {
554 if len(v) < 9 {
555 str := fmt.Sprintf(
556 "%s: short read (expected %d bytes, read %d)",
557 bucketCredits, 9, len(v),
558 )
559 return 0, false, storeError(ErrData, str, nil)
560 }
561 return amt.Amount(byteOrder.Uint64(v)), v[8]&(1<<0) != 0, nil
562 }
563
564 // fetchRawCreditAmountChange returns the amount of the credit and whether the credit is marked as change.
565 func fetchRawCreditAmountChange(v []byte) (amt.Amount, bool, error) {
566 if len(v) < 9 {
567 str := fmt.Sprintf(
568 "%s: short read (expected %d bytes, read %d)",
569 bucketCredits, 9, len(v),
570 )
571 return 0, false, storeError(ErrData, str, nil)
572 }
573 return amt.Amount(byteOrder.Uint64(v)), v[8]&(1<<1) != 0, nil
574 }
575
576 // fetchRawCreditUnspentValue returns the unspent value for a raw credit key. This may be used to mark a credit as
577 // unspent.
578 func fetchRawCreditUnspentValue(k []byte) ([]byte, error) {
579 if len(k) < 72 {
580 str := fmt.Sprintf(
581 "%s: short key (expected %d bytes, read %d)",
582 bucketCredits, 72, len(k),
583 )
584 return nil, storeError(ErrData, str, nil)
585 }
586 return k[32:68], nil
587 }
588
589 // spendRawCredit marks the credit with a given key as mined at some particular block as spent by the input at some
590 // transaction incidence. The debited amount is returned.
591 func spendCredit(ns walletdb.ReadWriteBucket, k []byte, spender *indexedIncidence) (amt.Amount, error) {
592 v := ns.NestedReadBucket(bucketCredits).Get(k)
593 newv := make([]byte, 81)
594 copy(newv, v)
595 v = newv
596 v[8] |= 1 << 0
597 copy(v[9:41], spender.txHash[:])
598 byteOrder.PutUint32(v[41:45], uint32(spender.block.Height))
599 copy(v[45:77], spender.block.Hash[:])
600 byteOrder.PutUint32(v[77:81], spender.index)
601 return amt.Amount(byteOrder.Uint64(v[0:8])), putRawCredit(ns, k, v)
602 }
603
604 // unspendRawCredit rewrites the credit for the given key as unspent. The output amount of the credit is returned. It
605 // returns without error if no credit exists for the key.
606 func unspendRawCredit(ns walletdb.ReadWriteBucket, k []byte) (amt.Amount, error) {
607 b := ns.NestedReadWriteBucket(bucketCredits)
608 v := b.Get(k)
609 if v == nil {
610 return 0, nil
611 }
612 newv := make([]byte, 9)
613 copy(newv, v)
614 newv[8] &^= 1 << 0
615 e := b.Put(k, newv)
616 if e != nil {
617 str := "failed to put credit"
618 return 0, storeError(ErrDatabase, str, e)
619 }
620 return amt.Amount(byteOrder.Uint64(v[0:8])), nil
621 }
622
623 func existsCredit(ns walletdb.ReadBucket, txHash *chainhash.Hash, index uint32, block *Block) (k, v []byte) {
624 k = keyCredit(txHash, index, block)
625 v = ns.NestedReadBucket(bucketCredits).Get(k)
626 return
627 }
628
629 func existsRawCredit(ns walletdb.ReadBucket, k []byte) []byte {
630 return ns.NestedReadBucket(bucketCredits).Get(k)
631 }
632
633 func deleteRawCredit(ns walletdb.ReadWriteBucket, k []byte) (e error) {
634 e = ns.NestedReadWriteBucket(bucketCredits).Delete(k)
635 if e != nil {
636 str := "failed to delete credit"
637 return storeError(ErrDatabase, str, e)
638 }
639 return nil
640 }
641
642 // creditIterator allows for in-order iteration of all credit records for a mined transaction.
643 //
644 // Example usage:
645 //
646 // prefix := keyTxRecord(txHash, block)
647 // it := makeCreditIterator(ns, prefix)
648 // for it.next() {
649 // // Use it.elem
650 // // If necessary, read additional details from it.ck, it.cv
651 // }
652 // if it.err != nil {
653 // // Handle error
654 // }
655 //
656 // The elem's Spent field is not set to true if the credit is spent by an unmined transaction. To check for this case:
657 //
658 // k := canonicalOutPoint(&txHash, it.elem.Index)
659 // it.elem.Spent = existsRawUnminedInput(ns, k) != nil
660 type creditIterator struct {
661 c walletdb.ReadWriteCursor // Set to nil after final iteration
662 prefix []byte
663 ck []byte
664 cv []byte
665 elem CreditRecord
666 err error
667 }
668
669 // func makeCreditIterator(// ns walletdb.ReadWriteBucket, prefix []byte) creditIterator {
670 // c := ns.NestedReadWriteBucket(bucketCredits).ReadWriteCursor()
671 // return creditIterator{c: c, prefix: prefix}
672 // }
673 func makeReadCreditIterator(ns walletdb.ReadBucket, prefix []byte) creditIterator {
674 c := ns.NestedReadBucket(bucketCredits).ReadCursor()
675 return creditIterator{c: readCursor{c}, prefix: prefix}
676 }
677 func (it *creditIterator) readElem() (e error) {
678 if len(it.ck) < 72 {
679 str := fmt.Sprintf(
680 "%s: short key (expected %d bytes, read %d)",
681 bucketCredits, 72, len(it.ck),
682 )
683 return storeError(ErrData, str, nil)
684 }
685 if len(it.cv) < 9 {
686 str := fmt.Sprintf(
687 "%s: short read (expected %d bytes, read %d)",
688 bucketCredits, 9, len(it.cv),
689 )
690 return storeError(ErrData, str, nil)
691 }
692 it.elem.Index = byteOrder.Uint32(it.ck[68:72])
693 it.elem.Amount = amt.Amount(byteOrder.Uint64(it.cv))
694 it.elem.Spent = it.cv[8]&(1<<0) != 0
695 it.elem.Change = it.cv[8]&(1<<1) != 0
696 return nil
697 }
698 func (it *creditIterator) next() bool {
699 if it.c == nil {
700 return false
701 }
702 if it.ck == nil {
703 it.ck, it.cv = it.c.Seek(it.prefix)
704 } else {
705 it.ck, it.cv = it.c.Next()
706 }
707 if !bytes.HasPrefix(it.ck, it.prefix) {
708 it.c = nil
709 return false
710 }
711 e := it.readElem()
712 if e != nil {
713 it.err = e
714 return false
715 }
716 return true
717 }
718
719 // The unspent index records all outpoints for mined credits which are not spent by any other mined transaction records
720 // (but may be spent by a mempool transaction).
721 //
722 // Keys are use the canonical outpoint serialization:
723 //
724 // [0:32] Transaction hash (32 bytes)
725 // [32:36] Output index (4 bytes)
726 //
727 // Values are serialized as such:
728 //
729 // [0:4] Block height (4 bytes)
730 // [4:36] Block hash (32 bytes)
731 func valueUnspent(block *Block) []byte {
732 v := make([]byte, 36)
733 byteOrder.PutUint32(v, uint32(block.Height))
734 copy(v[4:36], block.Hash[:])
735 return v
736 }
737 func putUnspent(ns walletdb.ReadWriteBucket, outPoint *wire.OutPoint, block *Block) (e error) {
738 k := canonicalOutPoint(&outPoint.Hash, outPoint.Index)
739 v := valueUnspent(block)
740 e = ns.NestedReadWriteBucket(bucketUnspent).Put(k, v)
741 if e != nil {
742 str := "cannot put unspent"
743 return storeError(ErrDatabase, str, e)
744 }
745 return nil
746 }
747 func putRawUnspent(ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
748 e = ns.NestedReadWriteBucket(bucketUnspent).Put(k, v)
749 if e != nil {
750 str := "cannot put unspent"
751 return storeError(ErrDatabase, str, e)
752 }
753 return nil
754 }
755 func readUnspentBlock(v []byte, block *Block) (e error) {
756 if len(v) < 36 {
757 str := "short unspent value"
758 return storeError(ErrData, str, nil)
759 }
760 block.Height = int32(byteOrder.Uint32(v))
761 copy(block.Hash[:], v[4:36])
762 return nil
763 }
764
765 // existsUnspent returns the key for the unspent output and the corresponding key for the credits bucket. If there is no
766 // unspent output recorded, the credit key is nil.
767 func existsUnspent(ns walletdb.ReadBucket, outPoint *wire.OutPoint) (k, credKey []byte) {
768 k = canonicalOutPoint(&outPoint.Hash, outPoint.Index)
769 credKey = existsRawUnspent(ns, k)
770 return k, credKey
771 }
772
773 // existsRawUnspent returns the credit key if there exists an output recorded for the raw unspent key. It returns nil if
774 // the k/v pair does not exist.
775 func existsRawUnspent(ns walletdb.ReadBucket, k []byte) (credKey []byte) {
776 if len(k) < 36 {
777 return nil
778 }
779 v := ns.NestedReadBucket(bucketUnspent).Get(k)
780 if len(v) < 36 {
781 return nil
782 }
783 credKey = make([]byte, 72)
784 copy(credKey, k[:32])
785 copy(credKey[32:68], v)
786 copy(credKey[68:72], k[32:36])
787 return credKey
788 }
789 func deleteRawUnspent(ns walletdb.ReadWriteBucket, k []byte) (e error) {
790 e = ns.NestedReadWriteBucket(bucketUnspent).Delete(k)
791 if e != nil {
792 str := "failed to delete unspent"
793 return storeError(ErrDatabase, str, e)
794 }
795 return nil
796 }
797
798 // All transaction debits (inputs which spend credits) are keyed as such:
799 //
800 // [0:32] Transaction hash (32 bytes)
801 // [32:36] Block height (4 bytes)
802 // [36:68] Block hash (32 bytes)
803 // [68:72] Input index (4 bytes)
804 //
805 // The first 68 bytes match the key for the transaction record and may be used as a prefix filter to iterate through all
806 // debits in order.
807 //
808 // The debit value is serialized as such:
809 //
810 // [0:8] Amount (8 bytes)
811 // [8:80] Credits bucket key (72 bytes)
812 // [8:40] Transaction hash (32 bytes)
813 // [40:44] Block height (4 bytes)
814 // [44:76] Block hash (32 bytes)
815 // [76:80] Output index (4 bytes)
816 func keyDebit(txHash *chainhash.Hash, index uint32, block *Block) []byte {
817 k := make([]byte, 72)
818 copy(k, txHash[:])
819 byteOrder.PutUint32(k[32:36], uint32(block.Height))
820 copy(k[36:68], block.Hash[:])
821 byteOrder.PutUint32(k[68:72], index)
822 return k
823 }
824
825 func putDebit(
826 ns walletdb.ReadWriteBucket,
827 txHash *chainhash.Hash,
828 index uint32,
829 amount amt.Amount,
830 block *Block,
831 credKey []byte,
832 ) (e error) {
833 k := keyDebit(txHash, index, block)
834 v := make([]byte, 80)
835 byteOrder.PutUint64(v, uint64(amount))
836 copy(v[8:80], credKey)
837 e = ns.NestedReadWriteBucket(bucketDebits).Put(k, v)
838 if e != nil {
839 str := fmt.Sprintf(
840 "failed to update debit %s input %d",
841 txHash, index,
842 )
843 return storeError(ErrDatabase, str, e)
844 }
845 return nil
846 }
847
848 func extractRawDebitCreditKey(v []byte) []byte {
849 return v[8:80]
850 }
851
852 // existsDebit checks for the existance of a debit. If found, the debit and previous credit keys are returned. If the
853 // debit does not exist, both keys are nil.
854 func existsDebit(ns walletdb.ReadBucket, txHash *chainhash.Hash, index uint32, block *Block) (
855 k, credKey []byte,
856 e error,
857 ) {
858 k = keyDebit(txHash, index, block)
859 v := ns.NestedReadBucket(bucketDebits).Get(k)
860 if v == nil {
861 return nil, nil, nil
862 }
863 if len(v) < 80 {
864 str := fmt.Sprintf(
865 "%s: short read (expected 80 bytes, read %v)",
866 bucketDebits, len(v),
867 )
868 return nil, nil, storeError(ErrData, str, nil)
869 }
870 return k, v[8:80], nil
871 }
872
873 func deleteRawDebit(ns walletdb.ReadWriteBucket, k []byte) (e error) {
874 e = ns.NestedReadWriteBucket(bucketDebits).Delete(k)
875 if e != nil {
876 str := "failed to delete debit"
877 return storeError(ErrDatabase, str, e)
878 }
879 return nil
880 }
881
882 // debitIterator allows for in-order iteration of all debit records for a mined transaction.
883 //
884 // Example usage:
885 //
886 // prefix := keyTxRecord(txHash, block)
887 // it := makeDebitIterator(ns, prefix)
888 // for it.next() {
889 // // Use it.elem
890 // // If necessary, read additional details from it.ck, it.cv
891 // }
892 // if it.err != nil {
893 // // Handle error
894 // }
895 type debitIterator struct {
896 c walletdb.ReadWriteCursor // Set to nil after final iteration
897 prefix []byte
898 ck []byte
899 cv []byte
900 elem DebitRecord
901 err error
902 }
903
904 // func makeDebitIterator(// ns walletdb.ReadWriteBucket, prefix []byte) debitIterator {
905 // c := ns.NestedReadWriteBucket(bucketDebits).ReadWriteCursor()
906 // return debitIterator{c: c, prefix: prefix}
907 // }
908 func makeReadDebitIterator(ns walletdb.ReadBucket, prefix []byte) debitIterator {
909 c := ns.NestedReadBucket(bucketDebits).ReadCursor()
910 return debitIterator{c: readCursor{c}, prefix: prefix}
911 }
912
913 func (it *debitIterator) readElem() (e error) {
914 if len(it.ck) < 72 {
915 str := fmt.Sprintf(
916 "%s: short key (expected %d bytes, read %d)",
917 bucketDebits, 72, len(it.ck),
918 )
919 return storeError(ErrData, str, nil)
920 }
921 if len(it.cv) < 80 {
922 str := fmt.Sprintf(
923 "%s: short read (expected %d bytes, read %d)",
924 bucketDebits, 80, len(it.cv),
925 )
926 return storeError(ErrData, str, nil)
927 }
928 it.elem.Index = byteOrder.Uint32(it.ck[68:72])
929 it.elem.Amount = amt.Amount(byteOrder.Uint64(it.cv))
930 return nil
931 }
932
933 func (it *debitIterator) next() bool {
934 if it.c == nil {
935 return false
936 }
937 if it.ck == nil {
938 it.ck, it.cv = it.c.Seek(it.prefix)
939 } else {
940 it.ck, it.cv = it.c.Next()
941 }
942 if !bytes.HasPrefix(it.ck, it.prefix) {
943 it.c = nil
944 return false
945 }
946 e := it.readElem()
947 if e != nil {
948 it.err = e
949 return false
950 }
951 return true
952 }
953
954 // All unmined transactions are saved in the unmined bucket keyed by the transaction hash. The value matches that of
955 // mined transaction records:
956 //
957 // [0:8] Received time (8 bytes)
958 // [8:] Serialized transaction (varies)
959 func putRawUnmined(ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
960 e = ns.NestedReadWriteBucket(bucketUnmined).Put(k, v)
961 if e != nil {
962 str := "failed to put unmined record"
963 return storeError(ErrDatabase, str, e)
964 }
965 return nil
966 }
967
968 func readRawUnminedHash(k []byte, txHash *chainhash.Hash) (e error) {
969 if len(k) < 32 {
970 str := "short unmined key"
971 return storeError(ErrData, str, nil)
972 }
973 copy(txHash[:], k)
974 return nil
975 }
976
977 func existsRawUnmined(ns walletdb.ReadBucket, k []byte) (v []byte) {
978 return ns.NestedReadBucket(bucketUnmined).Get(k)
979 }
980
981 func deleteRawUnmined(ns walletdb.ReadWriteBucket, k []byte) (e error) {
982 e = ns.NestedReadWriteBucket(bucketUnmined).Delete(k)
983 if e != nil {
984 str := "failed to delete unmined record"
985 return storeError(ErrDatabase, str, e)
986 }
987 return nil
988 }
989
990 // Unmined transaction credits use the canonical serialization format:
991 //
992 // [0:32] Transaction hash (32 bytes)
993 // [32:36] Output index (4 bytes)
994 //
995 // The value matches the format used by mined credits, but the spent flag is never set and the optional debit record is
996 // never included. The simplified format is thus:
997 //
998 // [0:8] Amount (8 bytes)
999 // [8] Flags (1 byte)
1000 // 0x02: Change
1001 func valueUnminedCredit(amount amt.Amount, change bool) []byte {
1002 v := make([]byte, 9)
1003 byteOrder.PutUint64(v, uint64(amount))
1004 if change {
1005 v[8] = 1 << 1
1006 }
1007 return v
1008 }
1009 func putRawUnminedCredit(ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
1010 e = ns.NestedReadWriteBucket(bucketUnminedCredits).Put(k, v)
1011 if e != nil {
1012 str := "cannot put unmined credit"
1013 return storeError(ErrDatabase, str, e)
1014 }
1015 return nil
1016 }
1017 func fetchRawUnminedCreditIndex(k []byte) (uint32, error) {
1018 if len(k) < 36 {
1019 str := "short unmined credit key"
1020 return 0, storeError(ErrData, str, nil)
1021 }
1022 return byteOrder.Uint32(k[32:36]), nil
1023 }
1024 func fetchRawUnminedCreditAmount(v []byte) (amt.Amount, error) {
1025 if len(v) < 9 {
1026 str := "short unmined credit value"
1027 return 0, storeError(ErrData, str, nil)
1028 }
1029 return amt.Amount(byteOrder.Uint64(v)), nil
1030 }
1031 func fetchRawUnminedCreditAmountChange(v []byte) (amt.Amount, bool, error) {
1032 if len(v) < 9 {
1033 str := "short unmined credit value"
1034 return 0, false, storeError(ErrData, str, nil)
1035 }
1036 amt := amt.Amount(byteOrder.Uint64(v))
1037 change := v[8]&(1<<1) != 0
1038 return amt, change, nil
1039 }
1040 func existsRawUnminedCredit(ns walletdb.ReadBucket, k []byte) []byte {
1041 return ns.NestedReadBucket(bucketUnminedCredits).Get(k)
1042 }
1043 func deleteRawUnminedCredit(ns walletdb.ReadWriteBucket, k []byte) (e error) {
1044 e = ns.NestedReadWriteBucket(bucketUnminedCredits).Delete(k)
1045 if e != nil {
1046 str := "failed to delete unmined credit"
1047 return storeError(ErrDatabase, str, e)
1048 }
1049 return nil
1050 }
1051
1052 // unminedCreditIterator allows for cursor iteration over all credits, in order, from a single unmined transaction.
1053 //
1054 // Example usage:
1055 //
1056 // it := makeUnminedCreditIterator(ns, txHash)
1057 // for it.next() {
1058 // // Use it.elem, it.ck and it.cv
1059 // // Optionally, use it.delete() to remove this k/v pair
1060 // }
1061 // if it.err != nil {
1062 // // Handle error
1063 // }
1064 //
1065 // The spentness of the credit is not looked up for performance reasons (because for unspent credits, it requires
1066 // another lookup in another bucket). If this is needed, it may be checked like this:
1067 //
1068 // spent := existsRawUnminedInput(ns, it.ck) != nil
1069 type unminedCreditIterator struct {
1070 c walletdb.ReadWriteCursor
1071 prefix []byte
1072 ck []byte
1073 cv []byte
1074 elem CreditRecord
1075 err error
1076 }
1077
1078 type readCursor struct {
1079 walletdb.ReadCursor
1080 }
1081
1082 // Delete ...
1083 func (r readCursor) Delete() (e error) {
1084 str := "failed to delete current cursor item from read-only cursor"
1085 return storeError(ErrDatabase, str, walletdb.ErrTxNotWritable)
1086 }
1087
1088 func makeUnminedCreditIterator(ns walletdb.ReadWriteBucket, txHash *chainhash.Hash) unminedCreditIterator {
1089 c := ns.NestedReadWriteBucket(bucketUnminedCredits).ReadWriteCursor()
1090 return unminedCreditIterator{c: c, prefix: txHash[:]}
1091 }
1092
1093 func makeReadUnminedCreditIterator(ns walletdb.ReadBucket, txHash *chainhash.Hash) unminedCreditIterator {
1094 c := ns.NestedReadBucket(bucketUnminedCredits).ReadCursor()
1095 return unminedCreditIterator{c: readCursor{c}, prefix: txHash[:]}
1096 }
1097
1098 func (it *unminedCreditIterator) readElem() (e error) {
1099 index, e := fetchRawUnminedCreditIndex(it.ck)
1100 if e != nil {
1101 return e
1102 }
1103 amount, change, e := fetchRawUnminedCreditAmountChange(it.cv)
1104 if e != nil {
1105 return e
1106 }
1107 it.elem.Index = index
1108 it.elem.Amount = amount
1109 it.elem.Change = change
1110 // Spent intentionally not set
1111 return nil
1112 }
1113
1114 func (it *unminedCreditIterator) next() bool {
1115 if it.c == nil {
1116 return false
1117 }
1118 if it.ck == nil {
1119 it.ck, it.cv = it.c.Seek(it.prefix)
1120 } else {
1121 it.ck, it.cv = it.c.Next()
1122 }
1123 if !bytes.HasPrefix(it.ck, it.prefix) {
1124 it.c = nil
1125 return false
1126 }
1127 e := it.readElem()
1128 if e != nil {
1129 it.err = e
1130 return false
1131 }
1132 return true
1133 }
1134
1135 // // unavailable until https://github.com/boltdb/bolt/issues/620 is fixed.
1136 // // func (it *unminedCreditIterator) delete() (e error) {
1137 // // e := it.c.Delete()
1138 // // if e != nil {
1139 // DB// // str := "failed to delete unmined credit"
1140 // // return storeError(ErrDatabase, str, err)
1141 // // }
1142 // // return nil
1143 // // }
1144 // func (it *unminedCreditIterator) reposition(txHash *chainhash.Hash, index uint32) {
1145 // it.c.Seek(canonicalOutPoint(txHash, index))
1146 // }
1147
1148 // Outpoints spent by unmined transactions are saved in the unmined inputs bucket. This bucket maps between each
1149 // previous output spent, for both mined and unmined transactions, to the hash of the unmined transaction.
1150 //
1151 // The key is serialized as such:
1152 //
1153 // [0:32] Transaction hash (32 bytes)
1154 // [32:36] Output index (4 bytes)
1155 //
1156 // The value is serialized as such:
1157 //
1158 // [0:32] Transaction hash (32 bytes)
1159
1160 // putRawUnminedInput maintains a list of unmined transaction hashes that have spent an outpoint. Each entry in the
1161 // bucket is keyed by the outpoint being spent.
1162 func putRawUnminedInput(ns walletdb.ReadWriteBucket, k, v []byte) (e error) {
1163 spendTxHashes := ns.NestedReadBucket(bucketUnminedInputs).Get(k)
1164 spendTxHashes = append(spendTxHashes, v...)
1165 e = ns.NestedReadWriteBucket(bucketUnminedInputs).Put(k, spendTxHashes)
1166 if e != nil {
1167 str := "failed to put unmined input"
1168 return storeError(ErrDatabase, str, e)
1169 }
1170 return nil
1171 }
1172
1173 func existsRawUnminedInput(ns walletdb.ReadBucket, k []byte) (v []byte) {
1174 return ns.NestedReadBucket(bucketUnminedInputs).Get(k)
1175 }
1176
1177 // fetchUnminedInputSpendTxHashes fetches the list of unmined transactions that spend the serialized outpoint.
1178 func fetchUnminedInputSpendTxHashes(ns walletdb.ReadBucket, k []byte) []chainhash.Hash {
1179 rawSpendTxHashes := ns.NestedReadBucket(bucketUnminedInputs).Get(k)
1180 if rawSpendTxHashes == nil {
1181 return nil
1182 }
1183 // Each transaction hash is 32 bytes.
1184 spendTxHashes := make([]chainhash.Hash, 0, len(rawSpendTxHashes)/32)
1185 for len(rawSpendTxHashes) > 0 {
1186 var spendTxHash chainhash.Hash
1187 copy(spendTxHash[:], rawSpendTxHashes[:32])
1188 spendTxHashes = append(spendTxHashes, spendTxHash)
1189 rawSpendTxHashes = rawSpendTxHashes[32:]
1190 }
1191 return spendTxHashes
1192 }
1193
1194 func deleteRawUnminedInput(ns walletdb.ReadWriteBucket, k []byte) (e error) {
1195 e = ns.NestedReadWriteBucket(bucketUnminedInputs).Delete(k)
1196 if e != nil {
1197 str := "failed to delete unmined input"
1198 return storeError(ErrDatabase, str, e)
1199 }
1200 return nil
1201 }
1202
1203 // openStore opens an existing transaction store from the passed namespace.
1204 func openStore(ns walletdb.ReadBucket) (e error) {
1205 v := ns.Get(rootVersion)
1206 if len(v) != 4 {
1207 str := "no transaction store exists in namespace"
1208 return storeError(ErrNoExists, str, nil)
1209 }
1210 version := byteOrder.Uint32(v)
1211 if version < LatestVersion {
1212 str := fmt.Sprintf(
1213 "a database upgrade is required to upgrade "+
1214 "wtxmgr from recorded version %d to the latest version %d",
1215 version, LatestVersion,
1216 )
1217 return storeError(ErrNeedsUpgrade, str, nil)
1218 }
1219 if version > LatestVersion {
1220 str := fmt.Sprintf(
1221 "version recorded version %d is newer that latest "+
1222 "understood version %d", version, LatestVersion,
1223 )
1224 return storeError(ErrUnknownVersion, str, nil)
1225 }
1226 // Upgrade the tx store as needed, one version at a time, until LatestVersion is reached. Versions are not skipped
1227 // when performing database upgrades, and each upgrade is done in its own transaction.
1228 //
1229 // No upgrades yet.
1230 // if version < LatestVersion {
1231 // e := scopedUpdate(namespace, func(ns walletdb.Bucket) (e error) {
1232 // })
1233 // if e != nil {
1234 // DB // // Handle err
1235 // }
1236 // }
1237 return nil
1238 }
1239
1240 // createStore creates the tx store (with the latest db version) in the passed namespace. If a store already exists,
1241 // ErrAlreadyExists is returned.
1242 func createStore(ns walletdb.ReadWriteBucket) (e error) {
1243 // Ensure that nothing currently exists in the namespace bucket.
1244 ck, cv := ns.ReadCursor().First()
1245 if ck != nil || cv != nil {
1246 const str = "namespace is not empty"
1247 return storeError(ErrAlreadyExists, str, nil)
1248 }
1249 // Write the latest store version.
1250 v := make([]byte, 4)
1251 byteOrder.PutUint32(v, LatestVersion)
1252 e = ns.Put(rootVersion, v)
1253 if e != nil {
1254 str := "failed to store latest database version"
1255 return storeError(ErrDatabase, str, e)
1256 }
1257 // Save the creation date of the store.
1258 v = make([]byte, 8)
1259 byteOrder.PutUint64(v, uint64(time.Now().Unix()))
1260 e = ns.Put(rootCreateDate, v)
1261 if e != nil {
1262 str := "failed to store database creation time"
1263 return storeError(ErrDatabase, str, e)
1264 }
1265 // Write a zero balance.
1266 v = make([]byte, 8)
1267 e = ns.Put(rootMinedBalance, v)
1268 if e != nil {
1269 str := "failed to write zero balance"
1270 return storeError(ErrDatabase, str, e)
1271 }
1272 _, e = ns.CreateBucket(bucketBlocks)
1273 if e != nil {
1274 str := "failed to create blocks bucket"
1275 return storeError(ErrDatabase, str, e)
1276 }
1277 _, e = ns.CreateBucket(bucketTxRecords)
1278 if e != nil {
1279 str := "failed to create tx records bucket"
1280 return storeError(ErrDatabase, str, e)
1281 }
1282 _, e = ns.CreateBucket(bucketCredits)
1283 if e != nil {
1284 str := "failed to create credits bucket"
1285 return storeError(ErrDatabase, str, e)
1286 }
1287 _, e = ns.CreateBucket(bucketDebits)
1288 if e != nil {
1289 str := "failed to create debits bucket"
1290 return storeError(ErrDatabase, str, e)
1291 }
1292 _, e = ns.CreateBucket(bucketUnspent)
1293 if e != nil {
1294 str := "failed to create unspent bucket"
1295 return storeError(ErrDatabase, str, e)
1296 }
1297 _, e = ns.CreateBucket(bucketUnmined)
1298 if e != nil {
1299 str := "failed to create unmined bucket"
1300 return storeError(ErrDatabase, str, e)
1301 }
1302 _, e = ns.CreateBucket(bucketUnminedCredits)
1303 if e != nil {
1304 str := "failed to create unmined credits bucket"
1305 return storeError(ErrDatabase, str, e)
1306 }
1307 _, e = ns.CreateBucket(bucketUnminedInputs)
1308 if e != nil {
1309 str := "failed to create unmined inputs bucket"
1310 return storeError(ErrDatabase, str, e)
1311 }
1312 return nil
1313 }
1314
1315 // func scopedUpdate(// db walletdb.DB, namespaceKey []byte, f func(walletdb.ReadWriteBucket) error) (e error) {
1316 // tx, e := db.BeginReadWriteTx()
1317 // if e != nil {
1318 // DB// str := "cannot begin update"
1319 // return storeError(ErrDatabase, str, err)
1320 // }
1321 // e = f(tx.ReadWriteBucket(namespaceKey))
1322 // if e != nil {
1323 // DB// rollbackErr := tx.Rollback()
1324 // if rollbackErr != nil {
1325 // const desc = "rollback failed"
1326 // serr, ok := err.(DBError)
1327 // if !ok {
1328 // // This really shouldn't happen.
1329 // return storeError(ErrDatabase, desc, rollbackErr)
1330 // }
1331 // serr.Desc = desc + ": " + serr.Desc
1332 // return serr
1333 // }
1334 // return e
1335 // }
1336 // e = tx.Commit()
1337 // if e != nil {
1338 // DB// str := "commit failed"
1339 // return storeError(ErrDatabase, str, err)
1340 // }
1341 // return nil
1342 // }
1343 // func scopedView(// db walletdb.DB, namespaceKey []byte, f func(walletdb.ReadBucket) error) (e error) {
1344 // tx, e := db.BeginReadTx()
1345 // if e != nil {
1346 // DB// str := "cannot begin view"
1347 // return storeError(ErrDatabase, str, err)
1348 // }
1349 // e = f(tx.ReadBucket(namespaceKey))
1350 // rollbackErr := tx.Rollback()
1351 // if e != nil {
1352 // DB// return e
1353 // }
1354 // if rollbackErr != nil {
1355 // str := "cannot close view"
1356 // return storeError(ErrDatabase, str, rollbackErr)
1357 // }
1358 // return nil
1359 // }
1360