forked from aergoio/aergo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
statedb.go
509 lines (452 loc) · 12.6 KB
/
statedb.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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
/**
* @file
* @copyright defined in aergo/LICENSE.txt
*/
package state
import (
"errors"
"fmt"
"math/big"
"sync"
"github.com/aergoio/aergo-lib/db"
"github.com/aergoio/aergo-lib/log"
"github.com/aergoio/aergo/internal/common"
"github.com/aergoio/aergo/internal/enc"
"github.com/aergoio/aergo/pkg/trie"
"github.com/aergoio/aergo/types"
)
const (
stateName = "state"
stateLatest = stateName + ".latest"
)
var (
logger = log.NewLogger(stateName)
)
var (
emptyHashID = types.HashID{}
emptyBlockID = types.BlockID{}
emptyAccountID = types.AccountID{}
)
var (
errSaveData = errors.New("Failed to save data: invalid key")
errLoadData = errors.New("Failed to load data: invalid key")
errLoadStateData = errors.New("Failed to load StateData: invalid HashID")
// errSaveStateData = errors.New("Failed to save StateData: invalid HashID")
// errInvalidArgs = errors.New("invalid arguments")
// errInvalidRoot = errors.New("invalid root")
// errSetRoot = errors.New("Failed to set root: invalid root")
// errLoadRoot = errors.New("Failed to load root: invalid root")
errGetState = errors.New("Failed to get state: invalid account id")
errPutState = errors.New("Failed to put state: invalid account id")
)
// StateDB manages trie of states
type StateDB struct {
lock sync.RWMutex
buffer *stateBuffer
cache *storageCache
trie *trie.Trie
store *db.DB
batchtx db.Transaction
testmode bool
}
// NewStateDB craete StateDB instance
func NewStateDB(dbstore *db.DB, root []byte, test bool) *StateDB {
sdb := StateDB{
buffer: newStateBuffer(),
cache: newStorageCache(),
trie: trie.NewTrie(root, common.Hasher, *dbstore),
store: dbstore,
testmode: test,
}
return &sdb
}
// Clone returns a new StateDB which has same store and Root
func (states *StateDB) Clone() *StateDB {
states.lock.RLock()
defer states.lock.RUnlock()
return NewStateDB(states.store, states.GetRoot(), states.testmode)
}
// GetRoot returns root hash of trie
func (states *StateDB) GetRoot() []byte {
states.lock.RLock()
defer states.lock.RUnlock()
return states.trie.Root
}
// SetRoot updates root node of trie as a given root hash
func (states *StateDB) SetRoot(root []byte) error {
states.lock.Lock()
defer states.lock.Unlock()
// update root node
states.trie.Root = root
// reset buffer
return states.buffer.reset()
}
// LoadCache reads first layer of trie given root hash
// and also updates root node of trie as a given root hash
func (states *StateDB) LoadCache(root []byte) error {
states.lock.Lock()
defer states.lock.Unlock()
// update root node and load cache
err := states.trie.LoadCache(root)
if err != nil {
return err
}
// reset buffer
return states.buffer.reset()
}
// Revert rollbacks trie to previous root hash
func (states *StateDB) Revert(root types.HashID) error {
states.lock.Lock()
defer states.lock.Unlock()
// // handle nil bytes
// targetRoot := root.Bytes()
// // revert trie
// err := states.trie.Revert(targetRoot)
// if err != nil {
// // when targetRoot is not contained in the cached tries.
// states.trie.Root = targetRoot
// }
// just update root node as targetRoot.
// revert trie consumes unnecessarily long time.
states.trie.Root = root.Bytes()
// reset buffer
return states.buffer.reset()
}
// PutState puts account id and its state into state buffer.
func (states *StateDB) PutState(id types.AccountID, state *types.State) error {
states.lock.Lock()
defer states.lock.Unlock()
if id == emptyAccountID {
return errPutState
}
states.buffer.put(newValueEntry(types.HashID(id), state))
return nil
}
// GetAccountState gets state of account id from statedb.
// empty state is returned when there is no state corresponding to account id.
func (states *StateDB) GetAccountState(id types.AccountID) (*types.State, error) {
st, err := states.GetState(id)
if err != nil {
return nil, err
}
if st == nil {
if states.testmode {
amount := new(big.Int).Add(types.StakingMinimum, types.StakingMinimum)
return &types.State{Balance: amount.Bytes()}, nil
}
return &types.State{}, nil
}
return st, nil
}
type V struct {
sdb *StateDB
id []byte
aid types.AccountID
oldV *types.State
newV *types.State
newOne bool
create bool
buffer *stateBuffer
}
func (v *V) ID() []byte {
if len(v.id) < types.AddressLength {
v.id = types.AddressPadding(v.id)
}
return v.id
}
func (v *V) AccountID() types.AccountID {
return v.aid
}
func (v *V) State() *types.State {
return v.newV
}
func (v *V) SetNonce(nonce uint64) {
v.newV.Nonce = nonce
}
func (v *V) Balance() *big.Int {
return new(big.Int).SetBytes(v.newV.Balance)
}
func (v *V) AddBalance(amount *big.Int) {
balance := new(big.Int).SetBytes(v.newV.Balance)
v.newV.Balance = new(big.Int).Add(balance, amount).Bytes()
}
func (v *V) SubBalance(amount *big.Int) {
balance := new(big.Int).SetBytes(v.newV.Balance)
v.newV.Balance = new(big.Int).Sub(balance, amount).Bytes()
}
func (v *V) RP() uint64 {
return v.newV.SqlRecoveryPoint
}
func (v *V) IsNew() bool {
return v.newOne
}
func (v *V) IsCreate() bool {
return v.create
}
func (v *V) Reset() {
*v.newV = types.State(*v.oldV)
}
func (v *V) PutState() error {
return v.sdb.PutState(v.aid, v.newV)
}
func (states *StateDB) CreateAccountStateV(id []byte) (*V, error) {
v, err := states.GetAccountStateV(id)
if err != nil {
return nil, err
}
if !v.newOne {
return nil, fmt.Errorf("account(%s) aleardy exists", types.EncodeAddress(v.ID()))
}
v.newV.SqlRecoveryPoint = 1
v.create = true
return v, nil
}
func (states *StateDB) GetAccountStateV(id []byte) (*V, error) {
aid := types.ToAccountID(id)
st, err := states.GetState(aid)
if err != nil {
return nil, err
}
if st == nil {
if states.testmode {
amount := new(big.Int).Add(types.StakingMinimum, types.StakingMinimum)
return &V{
sdb: states,
id: id,
aid: aid,
oldV: &types.State{},
newV: &types.State{Balance: amount.Bytes()},
newOne: true,
}, nil
}
return &V{
sdb: states,
id: id,
aid: aid,
oldV: &types.State{},
newV: &types.State{},
newOne: true,
}, nil
}
newV := new(types.State)
*newV = types.State(*st)
return &V{
sdb: states,
id: id,
aid: aid,
oldV: st,
newV: newV,
}, nil
}
func (states *StateDB) InitAccountStateV(id []byte, old *types.State, new *types.State) *V {
return &V{
sdb: states,
id: id,
oldV: old,
newV: new,
}
}
// GetState gets state of account id from state buffer and trie.
// nil value is returned when there is no state corresponding to account id.
func (states *StateDB) GetState(id types.AccountID) (*types.State, error) {
states.lock.RLock()
defer states.lock.RUnlock()
if id == emptyAccountID {
return nil, errGetState
}
return states.getState(id)
}
// getState returns state of account id from buffer and trie.
// nil value is returned when there is no state corresponding to account id.
func (states *StateDB) getState(id types.AccountID) (*types.State, error) {
// get state from buffer
if entry := states.buffer.get(types.HashID(id)); entry != nil {
return entry.Value().(*types.State), nil
}
// get state from trie
return states.getTrieState(id)
}
// getTrieState gets state of account id from trie.
// nil value is returned when there is no state corresponding to account id.
func (states *StateDB) getTrieState(id types.AccountID) (*types.State, error) {
key, err := states.trie.Get(id[:])
if err != nil {
return nil, err
}
if key == nil || len(key) == 0 {
return nil, nil
}
return states.loadStateData(key)
}
func (states *StateDB) TrieQuery(id []byte, root []byte, compressed bool) ([]byte, [][]byte, int, bool, []byte, []byte, error) {
var ap [][]byte
var proofKey, proofVal, bitmap []byte
var isIncluded bool
var err error
var height int
states.lock.RLock()
defer states.lock.RUnlock()
if len(root) != 0 {
if compressed {
bitmap, ap, height, isIncluded, proofKey, proofVal, err = states.trie.MerkleProofCompressedR(id, root)
} else {
// Get the state and proof of the account for a past state
ap, isIncluded, proofKey, proofVal, err = states.trie.MerkleProofR(id, root)
}
} else {
// Get the state and proof of the account at the latest trie
// The wallet should check that state hashes to proofVal and verify the audit path,
// The returned proofVal shouldn't be trusted by the wallet, it is used to proove non inclusion
if compressed {
bitmap, ap, height, isIncluded, proofKey, proofVal, err = states.trie.MerkleProofCompressed(id)
} else {
ap, isIncluded, proofKey, proofVal, err = states.trie.MerkleProof(id)
}
}
return bitmap, ap, height, isIncluded, proofKey, proofVal, err
}
// GetVarAndProof gets the value of a variable in the given contract trie root.
func (states *StateDB) GetVarAndProof(id []byte, root []byte, compressed bool) (*types.ContractVarProof, error) {
var value []byte
bitmap, ap, height, isIncluded, proofKey, dbKey, err := states.TrieQuery(id, root, compressed)
if err != nil {
return nil, err
}
if isIncluded {
value = []byte{}
if err := loadData(states.store, dbKey, &value); err != nil {
return nil, err
}
// proofKey and proofVal are only not nil for prooving exclusion with another leaf on the path
dbKey = nil
}
contractVarProof := &types.ContractVarProof{
Value: value,
Inclusion: isIncluded,
ProofKey: proofKey,
ProofVal: dbKey,
Bitmap: bitmap,
Height: uint32(height),
AuditPath: ap,
}
logger.Debug().Str("contract root : ", enc.ToString(root)).Msg("Get contract variable and Proof")
return contractVarProof, nil
}
// GetAccountAndProof gets the state and associated proof of an account
// in the given trie root. If the account doesnt exist, a proof of
// non existence is returned.
func (states *StateDB) GetAccountAndProof(id []byte, root []byte, compressed bool) (*types.AccountProof, error) {
var state *types.State
bitmap, ap, height, isIncluded, proofKey, dbKey, err := states.TrieQuery(id, root, compressed)
if err != nil {
return nil, err
}
if isIncluded {
state, err = states.loadStateData(dbKey)
if err != nil {
return nil, err
}
dbKey = nil
}
accountProof := &types.AccountProof{
State: state,
Inclusion: isIncluded,
ProofKey: proofKey,
ProofVal: dbKey,
Bitmap: bitmap,
Height: uint32(height),
AuditPath: ap,
}
logger.Debug().Str("state root : ", enc.ToString(root)).Msg("Get Account and Proof")
return accountProof, nil
}
// Snapshot represents revision number of statedb
type Snapshot int
// Snapshot returns revision number of state buffer
func (states *StateDB) Snapshot() Snapshot {
states.lock.RLock()
defer states.lock.RUnlock()
return Snapshot(states.buffer.snapshot())
}
// Rollback discards changes of state buffer to revision number
func (states *StateDB) Rollback(revision Snapshot) error {
states.lock.Lock()
defer states.lock.Unlock()
return states.buffer.rollback(int(revision))
}
// Update applies changes of state buffer to trie
func (states *StateDB) Update() error {
states.lock.Lock()
defer states.lock.Unlock()
if err := states.update(); err != nil {
return err
}
return nil
}
func (states *StateDB) update() error {
// update storage and put state with changed storage root
if err := states.updateStorage(); err != nil {
return err
}
// export buffer and update to trie
if err := states.buffer.updateTrie(states.trie); err != nil {
return err
}
return nil
}
func (states *StateDB) updateStorage() error {
before := states.buffer.snapshot()
for id, storage := range states.cache.storages {
// update storage
if err := storage.update(); err != nil {
states.buffer.rollback(before)
return err
}
// update state if storage root changed
if storage.isDirty() {
st, err := states.getState(id)
if err != nil {
states.buffer.rollback(before)
return err
}
if st == nil {
st = &types.State{}
}
// put state with storage root
st.StorageRoot = storage.trie.Root
states.buffer.put(newValueEntry(types.HashID(id), st))
}
}
return nil
}
// Commit writes state buffer and trie to db
func (states *StateDB) Commit() error {
states.lock.Lock()
defer states.lock.Unlock()
bulk := (*states.store).NewBulk()
for _, storage := range states.cache.storages {
// stage changes
if err := storage.stage(bulk); err != nil {
bulk.DiscardLast()
return err
}
}
if err := states.stage(bulk); err != nil {
bulk.DiscardLast()
return err
}
bulk.Flush()
return nil
}
func (states *StateDB) stage(txn trie.DbTx) error {
// stage trie and buffer
states.trie.StageUpdates(txn)
if err := states.buffer.stage(txn); err != nil {
return err
}
// reset buffer
if err := states.buffer.reset(); err != nil {
return err
}
return nil
}