-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: add the wrapper layer of versa #2636
base: versa_base
Are you sure you want to change the base?
Changes from 1 commit
41ab497
d2e5c03
435410f
7f3aac3
6cf5169
e1a2b8e
4635a45
3a34c73
6b7c807
66bd590
bb167a9
60df2fa
6188aae
ed7aa3e
e8309ad
d8c3726
b00582c
d162a63
83287ce
ec2384a
04e5c17
f69ff95
657eced
2f0f91d
58dabc9
324e4a4
c572535
5b4e00c
f284358
3a70b35
412c5ee
bfe07c5
be0a305
26d7f62
b73a98c
0941eb6
023b092
bd9c2ae
a3f8c70
8d7ee3a
42b72fe
6dfcbc6
bbef397
dec27d1
cf04af0
7462078
c6d8d63
ac2d1a8
2e38d06
0a90118
2e4ec39
7bce07a
dc51003
9d20658
77dea68
90de1d7
8197064
3c4b452
b80d787
bf80891
84767d1
f67f494
23c60a3
e940e28
90aa2e1
997d7b5
1cbdf0a
7d5e6ce
55cdcfa
a0daf10
aa9d518
ef3f9df
f724941
3ad4700
1a40f79
fa7a43a
4764fdb
30b371d
d3854b4
9c239db
74ab9ab
3ad7c4e
b8fa476
ea76d5f
a1d4b06
b585984
d5a438a
d5e38ad
36044b0
8db789b
85a6b17
84da730
facbe6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
package state | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"sync/atomic" | ||
|
||
versa "github.com/bnb-chain/versioned-state-database" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/common/lru" | ||
"github.com/ethereum/go-ethereum/core/rawdb" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
"github.com/ethereum/go-ethereum/ethdb" | ||
"github.com/ethereum/go-ethereum/log" | ||
"github.com/ethereum/go-ethereum/rlp" | ||
"github.com/ethereum/go-ethereum/trie" | ||
"github.com/ethereum/go-ethereum/trie/trienode" | ||
"github.com/ethereum/go-ethereum/triedb" | ||
) | ||
|
||
type cachingVersaDB struct { | ||
triedb *triedb.Database | ||
versionDB versa.Database | ||
codeDB ethdb.KeyValueStore | ||
codeSizeCache *lru.Cache[common.Hash, int] | ||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte] | ||
|
||
accTree *VersaTree | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is VersaTree impl? And why to have a "accTree"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Facilitate OpenStorageTree to avoid reopen. |
||
state versa.StateHandler | ||
root common.Hash | ||
mode versa.StateMode | ||
hasState atomic.Bool | ||
} | ||
|
||
// NewVersaDatabase should be call by NewDatabaseWithNodeDB | ||
// TODO:: NewDatabaseWithNodeDB should add mode param. | ||
func NewVersaDatabase(db ethdb.Database, triedb *triedb.Database, mode versa.StateMode) Database { | ||
return &cachingVersaDB{ | ||
triedb: triedb, | ||
versionDB: triedb.VersaDB(), | ||
codeDB: db, | ||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize), | ||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), | ||
} | ||
} | ||
|
||
func (cv *cachingVersaDB) OpenTrie(root common.Hash) (Trie, error) { | ||
if cv.hasState.Load() { | ||
//TODO:: will change to log.Error after stabilization | ||
panic("account tree has open") | ||
} | ||
|
||
// TODO:: if root tree, versa db shouldb ignore check version | ||
state, err := cv.versionDB.OpenState(0, root, cv.mode) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest to use the block num as the version here, not 0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It’s fine, but currently, this version is of no use to the upper layer and has brought about a burden. The plan is to make the upper layer aware of the version only if needed in the future. The conclusion is that it can be transparent to the upper layer for the time being. |
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
handler, err := cv.versionDB.OpenTree(state, 0, common.Hash{}, root) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
tree := &VersaTree{ | ||
db: cv.versionDB, | ||
handler: handler, | ||
accountTree: true, | ||
root: root, | ||
} | ||
|
||
cv.state = state | ||
cv.hasState.Store(true) // if set, can't change | ||
cv.accTree = tree | ||
cv.root = root | ||
|
||
return tree, nil | ||
} | ||
|
||
func (cv *cachingVersaDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, _ Trie) (Trie, error) { | ||
if !cv.hasState.Load() { | ||
//TODO:: will change to log.Error after stabilization | ||
panic("open account tree, before open storage tree") | ||
} | ||
if cv.root.Cmp(root) != 0 { | ||
panic("account root mismatch, on open storage tree") | ||
} | ||
|
||
version, account, err := cv.accTree.getAccountWithVersion(address) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if account.Root.Cmp(stateRoot) != 0 { | ||
return nil, fmt.Errorf("state root mismatch") | ||
} | ||
|
||
handler, err := cv.versionDB.OpenTree(cv.state, version, crypto.Keccak256Hash(address.Bytes()), stateRoot) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
tree := &VersaTree{ | ||
db: cv.versionDB, | ||
handler: handler, | ||
version: version, | ||
root: root, | ||
stateRoot: stateRoot, | ||
address: address, | ||
} | ||
return tree, nil | ||
} | ||
|
||
// Flush unique to versa | ||
func (cv *cachingVersaDB) Flush() error { | ||
return cv.versionDB.Flush(cv.state) | ||
} | ||
|
||
// Release unique to versa | ||
func (cv *cachingVersaDB) Release() error { | ||
if err := cv.versionDB.CloseState(cv.state); err != nil { | ||
return nil | ||
} | ||
cv.release() | ||
return nil | ||
} | ||
|
||
func (cv *cachingVersaDB) release() { | ||
cv.hasState.Store(false) | ||
cv.accTree = nil | ||
} | ||
|
||
func (cv *cachingVersaDB) CopyTrie(Trie) Trie { | ||
//TODO:: support in the future | ||
return nil | ||
} | ||
|
||
func (cv *cachingVersaDB) ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error) { | ||
code, _ := cv.codeCache.Get(codeHash) | ||
if len(code) > 0 { | ||
return code, nil | ||
} | ||
code = rawdb.ReadCodeWithPrefix(cv.codeDB, codeHash) | ||
if len(code) > 0 { | ||
cv.codeCache.Add(codeHash, code) | ||
cv.codeSizeCache.Add(codeHash, len(code)) | ||
return code, nil | ||
} | ||
return nil, errors.New("not found") | ||
} | ||
|
||
func (cv *cachingVersaDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) { | ||
if cached, ok := cv.codeSizeCache.Get(codeHash); ok { | ||
return cached, nil | ||
} | ||
code, err := cv.ContractCode(addr, codeHash) | ||
return len(code), err | ||
} | ||
|
||
func (cv *cachingVersaDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) { | ||
code, _ := cv.codeCache.Get(codeHash) | ||
if len(code) > 0 { | ||
return code, nil | ||
} | ||
code = rawdb.ReadCodeWithPrefix(cv.codeDB, codeHash) | ||
if len(code) > 0 { | ||
cv.codeCache.Add(codeHash, code) | ||
cv.codeSizeCache.Add(codeHash, len(code)) | ||
return code, nil | ||
} | ||
return nil, errors.New("not found") | ||
} | ||
|
||
func (cv *cachingVersaDB) DiskDB() ethdb.KeyValueStore { | ||
return cv.codeDB | ||
} | ||
|
||
func (cv *cachingVersaDB) TrieDB() *triedb.Database { | ||
return cv.triedb | ||
} | ||
|
||
func (cv *cachingVersaDB) NoTries() bool { | ||
// TODO:: not support fastnode | ||
return false | ||
} | ||
|
||
type VersaTree struct { | ||
db versa.Database | ||
handler versa.TreeHandler | ||
version int64 | ||
accountTree bool | ||
|
||
// TODO:: debugging, used for logging | ||
root common.Hash | ||
stateRoot common.Hash | ||
address common.Address | ||
} | ||
|
||
func (vt *VersaTree) GetKey(key []byte) []byte { | ||
_, val, err := vt.db.Get(vt.handler, key) | ||
if err != nil { | ||
log.Warn("failed to get key from version db") | ||
} | ||
return val | ||
} | ||
|
||
func (vt *VersaTree) GetAccount(address common.Address) (*types.StateAccount, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the version of the account should be returned to the caller, and maintained by the account itself, so that it can be provided to versaDB when opening the storage tree. But if do this, a lot of changes will be required. Therefore, we need to do some trade off whether the version should be known to the caller. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, currently the upper layer does not have a strong dependency on the version, so it can be transparent to the upper layer for the time being. If needed in the future, it can be exposed then. The focus at this stage should not be on this. |
||
_, res, err := vt.getAccountWithVersion(address) | ||
return res, err | ||
} | ||
|
||
func (vt *VersaTree) getAccountWithVersion(address common.Address) (int64, *types.StateAccount, error) { | ||
vt.CheckAccountTree() | ||
ver, res, err := vt.db.Get(vt.handler, address.Bytes()) | ||
if res == nil || err != nil { | ||
return ver, nil, err | ||
} | ||
ret := new(types.StateAccount) | ||
err = rlp.DecodeBytes(res, ret) | ||
return ver, ret, err | ||
} | ||
|
||
func (vt *VersaTree) GetStorage(_ common.Address, key []byte) ([]byte, error) { | ||
vt.CheckStorageTree() | ||
_, res, err := vt.db.Get(vt.handler, key) | ||
if res == nil || err != nil { | ||
return nil, err | ||
} | ||
return res, err | ||
} | ||
|
||
func (vt *VersaTree) UpdateAccount(address common.Address, account *types.StateAccount) error { | ||
vt.CheckAccountTree() | ||
data, err := rlp.EncodeToBytes(account) | ||
if err != nil { | ||
return err | ||
} | ||
return vt.db.Put(vt.handler, address.Bytes(), data) | ||
} | ||
|
||
func (vt *VersaTree) UpdateStorage(_ common.Address, key, value []byte) error { | ||
vt.CheckStorageTree() | ||
return vt.db.Put(vt.handler, key, value) | ||
} | ||
|
||
func (vt *VersaTree) DeleteAccount(address common.Address) error { | ||
vt.CheckAccountTree() | ||
return vt.db.Delete(vt.handler, address.Bytes()) | ||
} | ||
|
||
func (vt *VersaTree) DeleteStorage(_ common.Address, key []byte) error { | ||
vt.CheckStorageTree() | ||
return vt.db.Delete(vt.handler, key) | ||
} | ||
|
||
func (vt *VersaTree) UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error { | ||
return nil | ||
} | ||
|
||
func (vt *VersaTree) Hash() common.Hash { | ||
hash, err := vt.db.CalcRootHash(vt.handler) | ||
if err != nil { | ||
log.Warn("failed to cacl versa tree hash", "error", err) | ||
} | ||
return hash | ||
} | ||
|
||
func (vt *VersaTree) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) { | ||
hash, err := vt.db.Commit(vt.handler) | ||
if err != nil { | ||
log.Warn("failed to commit versa tree", "error", err) | ||
} | ||
return hash, nil, nil | ||
} | ||
|
||
func (vt *VersaTree) NodeIterator(startKey []byte) (trie.NodeIterator, error) { | ||
panic("versa tree not support iterate") | ||
return nil, nil | ||
} | ||
|
||
func (vt *VersaTree) Prove(key []byte, proofDb ethdb.KeyValueWriter) error { | ||
panic("versa tree not support prove") | ||
return nil | ||
} | ||
|
||
// TODO:: test code, will be deleted after stabilization | ||
func (vt *VersaTree) CheckAccountTree() { | ||
if !vt.accountTree { | ||
panic("sub tree can't operate account") | ||
} | ||
} | ||
|
||
// TODO:: test code, will be deleted after stabilization | ||
func (vt *VersaTree) CheckStorageTree() { | ||
if vt.accountTree { | ||
panic("root tree can't operate storage") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why to have a "versionDB" here? Is there any difference with "triedb"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no essential difference; here, versiondb is for convenience.