-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdatabase.go
193 lines (159 loc) · 4.24 KB
/
database.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package main
//Be careful of the .go file directory and the package name
//Modify package name if you move this .go file
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"github.com/dgraph-io/badger"
"log"
"os"
"path/filepath"
"strings"
)
// 2 functions are provided to be used for accessing the blockchain database.
// SaveBlock(block *Block, nodeID string) error ---Create a database and store, or store a block to the existing database
// LoadChain(nodeID string) ([]*Block, error) ---Load all blocks (ordering depends whether the chain is complete) stored in the node's database
const (
dbPath = "./database/nodes/" //Path to store nodes' database files.
)
//Logger to reset badgerdb's loggger
type nopLog struct {
*log.Logger
}
func SaveBlock(block *Block, nodeID string) error{
nodeDBFilePath := dbPath+nodeID
db := initDatabase(nodeDBFilePath)
defer db.Close()
err := db.Update(func(txn *badger.Txn) error {
//if current block found
if _, err := txn.Get(block.CurrentBlockHash); err == nil {
return errors.New("The Block Already Exists In Node DB File")
}
blockData := block.serialize()
err := txn.Set(block.CurrentBlockHash, blockData)
return err
})
if err !=nil{
println(err.Error())
}
return err
}
func LoadChain(nodeID string) ([]*Block, error) {
nodeDBFilePath := dbPath+nodeID
if ! nodeDBExists(nodeDBFilePath) {
err :=errors.New("Node DB file Not Exists")
println(err.Error())
return nil, err
}
db := initDatabase(nodeDBFilePath)
defer db.Close()
blocks := []*Block{}
err := db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 10
it := txn.NewIterator(opts)
defer it.Close()
// Loop over all data in DB
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
_ = item.KeyCopy(nil) //hash
blockData, err := item.ValueCopy(nil) //blockData
handle(err)
block := deserialize(blockData)
blocks = append([]*Block{block}, blocks...)
}
return nil
})
if err != nil{
handle(err)
}
blocks = sortBlocks(blocks)
return blocks, err
}
func nodeDBExists(nodeDBFilePath string) bool {
if _, err := os.Stat(nodeDBFilePath + "/MANIFEST"); os.IsNotExist(err) {
return false
}
return true
}
func initDatabase(nodeDBFilePath string) *badger.DB {
//Create path if not exist
_, err := os.Stat(nodeDBFilePath)
if err == nil || os.IsNotExist(err) {
os.MkdirAll(nodeDBFilePath, os.ModePerm)
}
nopLogger := &nopLog{Logger: log.New(os.Stderr, "", log.LstdFlags)} //Reset logger
badger.SetLogger(nopLogger)
opts := badger.DefaultOptions
opts.Dir = nodeDBFilePath
opts.ValueDir = nodeDBFilePath
db, err := openDB(nodeDBFilePath, opts)
handle(err)
return db
}
func retry(dir string, originalOpts badger.Options) (*badger.DB, error) {
lockPath := filepath.Join(dir, "LOCK")
if err := os.Remove(lockPath); err != nil {
return nil, fmt.Errorf(`removing "LOCK": %s`, err)
}
retryOpts := originalOpts
retryOpts.Truncate = true
db, err := badger.Open(retryOpts)
return db, err
}
func openDB(dir string, opts badger.Options) (*badger.DB, error) {
if db, err := badger.Open(opts); err != nil {
if strings.Contains(err.Error(), "LOCK") {
if db, err := retry(dir, opts); err == nil {
log.Println("database unlocked, value log truncated")
return db, nil
}
log.Println("could not unlock database:", err)
}
return nil, err
} else {
return db, nil
}
}
func (b *Block) serialize() []byte {
var res bytes.Buffer
encoder := gob.NewEncoder(&res)
err := encoder.Encode(b)
handle(err)
return res.Bytes()
}
func deserialize(data []byte) *Block {
var block Block
decoder := gob.NewDecoder(bytes.NewReader(data))
err := decoder.Decode(&block)
handle(err)
return &block
}
// Sort the blocks with bubble sort
func sortBlocks(blocks []*Block) []*Block {
l := len(blocks)
for i := 0; i < l; i++{
for j := 0; j < (l-1-i); j++{
if !bytes.Equal(blocks[j].CurrentBlockHash, blocks[j+1].PrevBlockHash){
blocks[j], blocks[j+1] = blocks[j+1], blocks[j]
}
}
}
return blocks
}
func (l *nopLog) Errorf(f string, v ...interface{}) {
// noop
}
func (l *nopLog) Infof(f string, v ...interface{}) {
// noop
}
func (l *nopLog) Warningf(f string, v ...interface{}) {
// noop
}
func handle(err error) {
if err != nil {
log.Panic(err)
}
}