/
db.go
378 lines (306 loc) · 12.8 KB
/
db.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
package database
import (
"crypto/sha256"
"errors"
"github.com/fatzero/mass-core/interfaces"
"github.com/fatzero/mass-core/massutil"
"github.com/fatzero/mass-core/wire"
"golang.org/x/crypto/ripemd160"
)
// Errors that the various database functions may return.
var (
ErrAddrIndexDoesNotNeedInit = errors.New("address index has been initialized")
ErrAddrIndexDoesNotExist = errors.New("address index hasn't been built or is an older version")
ErrAddrIndexVersionNotFound = errors.New("address index version not found")
ErrAddrIndexInvalidVersion = errors.New("address index version is not allowed")
ErrUnsupportedAddressType = errors.New("address type is not supported by the address-index")
ErrTxShaMissing = errors.New("requested transaction does not exist")
ErrBlockShaMissing = errors.New("requested block does not exist")
ErrDuplicateSha = errors.New("duplicate insert attempted")
ErrDbDoesNotExist = errors.New("non-existent database")
ErrDbUnknownType = errors.New("non-existent database type")
ErrNotImplemented = errors.New("method has not yet been implemented")
ErrInvalidBlockStorageMeta = errors.New("invalid block storage meta")
ErrInvalidAddrIndexMeta = errors.New("invalid addr index meta")
ErrDeleteNonNewestBlock = errors.New("delete block that is not newest")
)
// Db defines a generic interface that is used to request and insert data into
// the mass block chain. This interface is intended to be agnostic to actual
// mechanism used for backend data storage. The AddDBDriver function can be
// used to add a new backend data storage method.
type Db interface {
// Close cleanly shuts down the database and syncs all data.
Close() (err error)
// RollbackClose discards the recent database changes to the previously
// saved data at last Sync and closes the database.
RollbackClose() (err error)
Rollback()
// Sync verifies that the database is coherent on disk and no
// outstanding transactions are in flight.
Sync() (err error)
// Commit commits batches in a single transaction
Commit(hash wire.Hash) error
// InitByGenesisBlock init database by setting genesis block
InitByGenesisBlock(block *massutil.Block) (err error)
// SubmitBlock inserts raw block and transaction data from a block
// into the database. The first block inserted into the database
// will be treated as the genesis block. Every subsequent block insert
// requires the referenced parent block to already exist.
SubmitBlock(block *massutil.Block) (err error)
// DeleteBlock will remove any blocks from the database after
// the given block. It terminates any existing transaction and performs
// its operations in an atomic transaction which is committed before
// the function returns.
DeleteBlock(hash *wire.Hash) (err error)
// ExistsSha returns whether or not the given block hash is present in
// the database.
ExistsSha(sha *wire.Hash) (exists bool, err error)
// FetchBlockBySha returns a massutil Block. The implementation may
// cache the underlying data if desired.
FetchBlockBySha(sha *wire.Hash) (blk *massutil.Block, err error)
// FetchBlockHeightBySha returns the block height for the given hash.
FetchBlockHeightBySha(sha *wire.Hash) (height uint64, err error)
// FetchBlockHeaderBySha returns a wire.BlockHeader for the given
// sha. The implementation may cache the underlying data if desired.
FetchBlockHeaderBySha(sha *wire.Hash) (bh *wire.BlockHeader, err error)
// FetchBlockShaByHeight returns a block hash based on its height in the
// block chain.
FetchBlockShaByHeight(height uint64) (sha *wire.Hash, err error)
FetchBlockLocByHeight(height uint64) (*BlockLoc, error)
// ExistsTxSha returns whether or not the given tx hash is present in
// the database
ExistsTxSha(sha *wire.Hash) (exists bool, err error)
FetchTxByLoc(blkHeight uint64, txOff int, txLen int) (*wire.MsgTx, error)
// FetchTxByFileLoc returns transactions saved in file, including
// those revoked with chain reorganization, for file is in APPEND mode.
FetchTxByFileLoc(blkLoc *BlockLoc, txLoc *wire.TxLoc) (*wire.MsgTx, error)
// FetchTxBySha returns some data for the given transaction hash. The
// implementation may cache the underlying data if desired.
FetchTxBySha(txsha *wire.Hash) ([]*TxReply, error)
// GetTxData returns the block height, txOffset, txLen for the given transaction hash,
// including unspent and fully spent transaction
GetUnspentTxData(txsha *wire.Hash) (uint64, int, int, error)
// FetchTxByShaList returns a TxReply given an array of transaction
// hashes. The implementation may cache the underlying data if desired.
// This differs from FetchUnSpentTxByShaList in that it will return
// the most recent known Tx, if it is fully spent or not.
//
// NOTE: This function does not return an error directly since it MUST
// return at least one TxReply instance for each requested
// transaction. Each TxReply instance then contains an Err field
// which can be used to detect errors.
FetchTxByShaList(txShaList []*wire.Hash) []*TxReply
// Returns database.ErrTxShaMissing if no transaction found
FetchLastFullySpentTxBeforeHeight(txsha *wire.Hash, height uint64) (tx *wire.MsgTx, blkheight uint64, blksha *wire.Hash, err error)
// FetchUnSpentTxByShaList returns a TxReply given an array of
// transaction hashes. The implementation may cache the underlying
// data if desired. Fully spent transactions will not normally not
// be returned in this operation.
//
// NOTE: This function does not return an error directly since it MUST
// return at least one TxReply instance for each requested
// transaction. Each TxReply instance then contains an Err field
// which can be used to detect errors.
FetchUnSpentTxByShaList(txShaList []*wire.Hash) []*TxReply
// FetchUnexpiredStakingRank returns only currently unexpired staking rank at
// target height. This function is for mining and validating block.
FetchUnexpiredStakingRank(height uint64, onlyOnList bool) ([]Rank, error)
// FetchStakingRank returns staking rank at any height. This
// function may be slow.
FetchStakingRank(height uint64, onlyOnList bool) ([]Rank, error)
// fetch a map of all staking transactions in database
FetchStakingTxMap() (StakingNodes, error)
FetchExpiredStakingTxListByHeight(height uint64) (StakingNodes, error)
FetchHeightRange(startHeight, endHeight uint64) ([]wire.Hash, error)
// NewestSha returns the hash and block height of the most recent (end)
// block of the block chain. It will return the zero hash, -1 for
// the block height, and no error (nil) if there are not any blocks in
// the database yet.
NewestSha() (sha *wire.Hash, height uint64, err error)
// FetchFaultPkBySha returns the FaultPubKey by Hash of that PubKey.
// Hash = DoubleHashB(PubKey.SerializeUnCompressed()).
// It will return ErrNotFound if this PubKey is not banned.
FetchFaultPkBySha(sha *wire.Hash) (fpk *wire.FaultPubKey, height uint64, err error)
FetchAllFaultPks() ([]*wire.FaultPubKey, []uint64, error)
// FetchFaultPkListByHeight returns the newly added FaultPubKey List
// on given height. It will return ErrNotFound if this height has not
// mined. And will return empty slice if there aren't any new banned Pks.
FetchFaultPkListByHeight(blkHeight uint64) ([]*wire.FaultPubKey, error)
// ExistsFaultPk returns whether or not the given FaultPubKey hash is present in
// the database.
ExistsFaultPk(sha *wire.Hash) (bool, error)
// FetchAllPunishment returns all faultPubKey stored in db, with random order.
FetchAllPunishment() ([]*wire.FaultPubKey, error)
// ExistsPunishment returns whether or not the given PoC PublicKey is present in
// the database.
ExistsPunishment(pk interfaces.PublicKey) (bool, error)
// InsertPunishment insert a fpk into punishment storage instantly.
InsertPunishment(fpk *wire.FaultPubKey) error
FetchMinedBlocks(pubKey interfaces.PublicKey) ([]uint64, error)
// FetchAddrIndexTip returns the hash and block height of the most recent
// block which has had its address index populated. It will return
// ErrAddrIndexDoesNotExist along with a zero hash, and math.MaxUint64 if
// it hasn't yet been built up.
FetchAddrIndexTip() (sha *wire.Hash, height uint64, err error)
SubmitAddrIndex(hash *wire.Hash, height uint64, addrIndexData *AddrIndexData) (err error)
DeleteAddrIndex(hash *wire.Hash, height uint64) (err error)
// FetchScriptHashRelatedTx returns all relevant txhash mapped by block height
FetchScriptHashRelatedTx(scriptHashes [][]byte, startBlock, stopBlock uint64) (map[uint64][]*wire.TxLoc, error)
CheckScriptHashUsed(scriptHash []byte) (bool, error)
// pubkeyHash is hash of MASS plot pubkey
FetchOldBinding(pubkeyHash []byte) ([]*BindingTxReply, error)
// For testing purpose
TestExportDbEntries() map[string][]byte
}
// DriverDB defines a structure for backend drivers to use when they registered
// themselves as a backend which implements the Db interface.
type DriverDB struct {
DbType string
CreateDB func(args ...interface{}) (pbdb Db, err error)
OpenDB func(path string, readonly bool, args ...interface{}) (pbdb Db, err error)
}
// driverList holds all of the registered database backends.
var driverList []DriverDB
// AddDBDriver adds a back end database driver to available interfaces.
func AddDBDriver(instance DriverDB) {
for _, drv := range driverList {
if drv.DbType == instance.DbType {
return
}
}
driverList = append(driverList, instance)
}
// CreateDB intializes and opens a database.
func CreateDB(dbtype string, args ...interface{}) (pbdb Db, err error) {
for _, drv := range driverList {
if drv.DbType == dbtype {
return drv.CreateDB(args...)
}
}
return nil, ErrDbUnknownType
}
// OpenDB opens an existing database.
func OpenDB(dbtype, path string, readonly bool, args ...interface{}) (pbdb Db, err error) {
for _, drv := range driverList {
if drv.DbType == dbtype {
return drv.OpenDB(path, readonly, args...)
}
}
return nil, ErrDbUnknownType
}
// SupportedDBs returns a slice of strings that represent the database drivers
// that have been registered and are therefore supported.
func SupportedDBs() []string {
var supportedDBs []string
for _, drv := range driverList {
supportedDBs = append(supportedDBs, drv.DbType)
}
return supportedDBs
}
// TxReply is used to return individual transaction information when
// data about multiple transactions is requested in a single call.
type TxReply struct {
Sha *wire.Hash
Tx *wire.MsgTx
BlkSha *wire.Hash
Height uint64
TxSpent []bool
Err error
}
type UtxoReply struct {
TxSha *wire.Hash
Height uint64
Coinbase bool
Index uint32
Value massutil.Amount
}
// AddrIndexKeySize is the number of bytes used by keys into the BlockAddrIndex.
const AddrIndexKeySize = ripemd160.Size + 1
type AddrIndexOutPoint struct {
TxLoc *wire.TxLoc
Index uint32
}
type BindingTxSpent struct {
SpentTxLoc *wire.TxLoc
BTxBlkHeight uint64
BTxLoc *wire.TxLoc
BTxIndex uint32
}
type BindingTxReply struct {
Height uint64
TxSha *wire.Hash
IsCoinbase bool
Value int64
Index uint32
}
type StakingTxOutAtHeight map[uint64]map[wire.OutPoint]StakingTxInfo
// Returns false if already exists
func (sh StakingTxOutAtHeight) Put(op wire.OutPoint, stk StakingTxInfo) (success bool) {
m, ok := sh[stk.BlkHeight]
if !ok {
m = make(map[wire.OutPoint]StakingTxInfo)
sh[stk.BlkHeight] = m
}
if _, exist := m[op]; exist {
return false
}
m[op] = stk
return true
}
type StakingNodes map[[sha256.Size]byte]StakingTxOutAtHeight
func (nodes StakingNodes) Get(key [sha256.Size]byte) StakingTxOutAtHeight {
m, ok := nodes[key]
if !ok {
m = make(StakingTxOutAtHeight)
nodes[key] = m
}
return m
}
func (nodes StakingNodes) IsEmpty(key [sha256.Size]byte) bool {
for _, v := range nodes[key] {
if len(v) > 0 {
return false
}
}
return true
}
func (nodes StakingNodes) Delete(key [sha256.Size]byte, blkHeight uint64, op wire.OutPoint) bool {
m1, ok := nodes[key]
if !ok {
return false
}
m2, ok := m1[blkHeight]
if !ok {
return false
}
_, ok = m2[op]
if !ok {
return false
}
delete(m2, op)
return true
}
// BlockAddrIndex represents the indexing structure for addresses.
// It maps a hash160 to a list of transaction locations within a block that
// either pays to or spends from the passed UTXO for the hash160.
type BlockAddrIndex map[[AddrIndexKeySize]byte][]*AddrIndexOutPoint
// BlockTxAddrIndex represents the indexing structure for txs
type TxAddrIndex map[[sha256.Size]byte][]*wire.TxLoc
type BindingTxAddrIndex map[[ripemd160.Size]byte][]*AddrIndexOutPoint
type BindingTxSpentAddrIndex map[[ripemd160.Size]byte][]*BindingTxSpent
type AddrIndexData struct {
TxIndex TxAddrIndex
BindingTxIndex BindingTxAddrIndex
BindingTxSpentIndex BindingTxSpentAddrIndex
}
type BLHeight struct {
BitLength int
BlkHeight uint64
}
type BlockLoc struct {
Height uint64
Hash wire.Hash
File uint32
Offset uint64
Length uint64
}