1// Copyright (c) 2015-2016 The btcsuite developers
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5package database_test
6
7import (
8	"bytes"
9	"fmt"
10	"os"
11	"path/filepath"
12
13	"github.com/btcsuite/btcd/chaincfg"
14	"github.com/btcsuite/btcd/database"
15	_ "github.com/btcsuite/btcd/database/ffldb"
16	"github.com/btcsuite/btcd/wire"
17	"github.com/btcsuite/btcutil"
18)
19
20// This example demonstrates creating a new database.
21func ExampleCreate() {
22	// This example assumes the ffldb driver is imported.
23	//
24	// import (
25	// 	"github.com/btcsuite/btcd/database"
26	// 	_ "github.com/btcsuite/btcd/database/ffldb"
27	// )
28
29	// Create a database and schedule it to be closed and removed on exit.
30	// Typically you wouldn't want to remove the database right away like
31	// this, nor put it in the temp directory, but it's done here to ensure
32	// the example cleans up after itself.
33	dbPath := filepath.Join(os.TempDir(), "examplecreate")
34	db, err := database.Create("ffldb", dbPath, wire.MainNet)
35	if err != nil {
36		fmt.Println(err)
37		return
38	}
39	defer os.RemoveAll(dbPath)
40	defer db.Close()
41
42	// Output:
43}
44
45// This example demonstrates creating a new database and using a managed
46// read-write transaction to store and retrieve metadata.
47func Example_basicUsage() {
48	// This example assumes the ffldb driver is imported.
49	//
50	// import (
51	// 	"github.com/btcsuite/btcd/database"
52	// 	_ "github.com/btcsuite/btcd/database/ffldb"
53	// )
54
55	// Create a database and schedule it to be closed and removed on exit.
56	// Typically you wouldn't want to remove the database right away like
57	// this, nor put it in the temp directory, but it's done here to ensure
58	// the example cleans up after itself.
59	dbPath := filepath.Join(os.TempDir(), "exampleusage")
60	db, err := database.Create("ffldb", dbPath, wire.MainNet)
61	if err != nil {
62		fmt.Println(err)
63		return
64	}
65	defer os.RemoveAll(dbPath)
66	defer db.Close()
67
68	// Use the Update function of the database to perform a managed
69	// read-write transaction.  The transaction will automatically be rolled
70	// back if the supplied inner function returns a non-nil error.
71	err = db.Update(func(tx database.Tx) error {
72		// Store a key/value pair directly in the metadata bucket.
73		// Typically a nested bucket would be used for a given feature,
74		// but this example is using the metadata bucket directly for
75		// simplicity.
76		key := []byte("mykey")
77		value := []byte("myvalue")
78		if err := tx.Metadata().Put(key, value); err != nil {
79			return err
80		}
81
82		// Read the key back and ensure it matches.
83		if !bytes.Equal(tx.Metadata().Get(key), value) {
84			return fmt.Errorf("unexpected value for key '%s'", key)
85		}
86
87		// Create a new nested bucket under the metadata bucket.
88		nestedBucketKey := []byte("mybucket")
89		nestedBucket, err := tx.Metadata().CreateBucket(nestedBucketKey)
90		if err != nil {
91			return err
92		}
93
94		// The key from above that was set in the metadata bucket does
95		// not exist in this new nested bucket.
96		if nestedBucket.Get(key) != nil {
97			return fmt.Errorf("key '%s' is not expected nil", key)
98		}
99
100		return nil
101	})
102	if err != nil {
103		fmt.Println(err)
104		return
105	}
106
107	// Output:
108}
109
110// This example demonstrates creating a new database, using a managed read-write
111// transaction to store a block, and using a managed read-only transaction to
112// fetch the block.
113func Example_blockStorageAndRetrieval() {
114	// This example assumes the ffldb driver is imported.
115	//
116	// import (
117	// 	"github.com/btcsuite/btcd/database"
118	// 	_ "github.com/btcsuite/btcd/database/ffldb"
119	// )
120
121	// Create a database and schedule it to be closed and removed on exit.
122	// Typically you wouldn't want to remove the database right away like
123	// this, nor put it in the temp directory, but it's done here to ensure
124	// the example cleans up after itself.
125	dbPath := filepath.Join(os.TempDir(), "exampleblkstorage")
126	db, err := database.Create("ffldb", dbPath, wire.MainNet)
127	if err != nil {
128		fmt.Println(err)
129		return
130	}
131	defer os.RemoveAll(dbPath)
132	defer db.Close()
133
134	// Use the Update function of the database to perform a managed
135	// read-write transaction and store a genesis block in the database as
136	// and example.
137	err = db.Update(func(tx database.Tx) error {
138		genesisBlock := chaincfg.MainNetParams.GenesisBlock
139		return tx.StoreBlock(btcutil.NewBlock(genesisBlock))
140	})
141	if err != nil {
142		fmt.Println(err)
143		return
144	}
145
146	// Use the View function of the database to perform a managed read-only
147	// transaction and fetch the block stored above.
148	var loadedBlockBytes []byte
149	err = db.Update(func(tx database.Tx) error {
150		genesisHash := chaincfg.MainNetParams.GenesisHash
151		blockBytes, err := tx.FetchBlock(genesisHash)
152		if err != nil {
153			return err
154		}
155
156		// As documented, all data fetched from the database is only
157		// valid during a database transaction in order to support
158		// zero-copy backends.  Thus, make a copy of the data so it
159		// can be used outside of the transaction.
160		loadedBlockBytes = make([]byte, len(blockBytes))
161		copy(loadedBlockBytes, blockBytes)
162		return nil
163	})
164	if err != nil {
165		fmt.Println(err)
166		return
167	}
168
169	// Typically at this point, the block could be deserialized via the
170	// wire.MsgBlock.Deserialize function or used in its serialized form
171	// depending on need.  However, for this example, just display the
172	// number of serialized bytes to show it was loaded as expected.
173	fmt.Printf("Serialized block size: %d bytes\n", len(loadedBlockBytes))
174
175	// Output:
176	// Serialized block size: 285 bytes
177}
178