erigon-pulse/ethdb/history_index.go
Giulio rebuffo 32375df7af made changeSet an helper type (#314)
* made changeSet an helper type

* added comment

* removed dbutils.Add

* remove common.CopyBytes from walk

* fixes
2020-01-15 20:51:10 +00:00

197 lines
4.2 KiB
Go

package ethdb
import (
"sort"
"github.com/ledgerwatch/bolt"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
"github.com/ledgerwatch/turbo-geth/rlp"
)
//
type HistoryIndex []uint64
func (hi *HistoryIndex) Encode() ([]byte, error) {
return rlp.EncodeToBytes(hi)
}
func (hi *HistoryIndex) Decode(s []byte) error {
if len(s) == 0 {
return nil
}
return rlp.DecodeBytes(s, &hi)
}
func (hi *HistoryIndex) Append(v uint64) *HistoryIndex {
*hi = append(*hi, v)
if !sort.SliceIsSorted(*hi, func(i, j int) bool {
return (*hi)[i] <= (*hi)[j]
}) {
sort.Slice(*hi, func(i, j int) bool {
return (*hi)[i] <= (*hi)[j]
})
}
return hi
}
//most common operation is remove one from the tail
func (hi *HistoryIndex) Remove(v uint64) *HistoryIndex {
for i := len(*hi) - 1; i >= 0; i-- {
if (*hi)[i] == v {
*hi = append((*hi)[:i], (*hi)[i+1:]...)
}
}
return hi
}
func (hi *HistoryIndex) Search(v uint64) (uint64, bool) {
ln := len(*hi)
if ln == 0 {
return 0, false
}
if (*hi)[ln-1] < v {
return 0, false
}
for i := ln - 1; i >= 0; i-- {
if v == (*hi)[i] {
return v, true
}
if (*hi)[i] < v {
return (*hi)[i+1], true
}
}
return (*hi)[0], true
}
func AppendToIndex(b []byte, timestamp uint64) ([]byte, error) {
v := new(HistoryIndex)
if err := v.Decode(b); err != nil {
return nil, err
}
v.Append(timestamp)
return v.Encode()
}
func RemoveFromIndex(b []byte, timestamp uint64) ([]byte, bool, error) {
v := new(HistoryIndex)
if err := v.Decode(b); err != nil {
return nil, false, err
}
v.Remove(timestamp)
res, err := v.Encode()
if len(*v) == 0 {
return res, true, err
}
return res, false, err
}
func BoltDBFindByHistory(tx *bolt.Tx, hBucket []byte, key []byte, timestamp uint64) ([]byte, error) {
//check
hB := tx.Bucket(hBucket)
if hB == nil {
return nil, ErrKeyNotFound
}
v, _ := hB.Get(key)
index := new(HistoryIndex)
err := index.Decode(v)
if err != nil {
return nil, err
}
changeSetBlock, ok := index.Search(timestamp)
if !ok {
return nil, ErrKeyNotFound
}
csB := tx.Bucket(dbutils.ChangeSetBucket)
if csB == nil {
return nil, ErrKeyNotFound
}
changeSetData, _ := csB.Get(dbutils.CompositeChangeSetKey(dbutils.EncodeTimestamp(changeSetBlock), hBucket))
var data []byte
data, err = dbutils.FindLast(changeSetData, key)
if err != nil {
return nil, ErrKeyNotFound
}
var acc accounts.Account
if err := acc.DecodeForStorage(data); err != nil {
return nil, err
}
if acc.Incarnation > 0 && acc.IsEmptyCodeHash() {
codeBucket := tx.Bucket(dbutils.ContractCodeBucket)
codeHash, _ := codeBucket.Get(dbutils.GenerateStoragePrefix(common.BytesToHash(key), acc.Incarnation))
if len(codeHash) > 0 {
acc.CodeHash = common.BytesToHash(codeHash)
}
data = make([]byte, acc.EncodingLengthForStorage())
acc.EncodeForStorage(data)
}
return data, nil
}
func BoltDBFindStorageByHistory(tx *bolt.Tx, hBucket []byte, key []byte, timestamp uint64) ([]byte, error) {
var k common.Hash
copy(k[:], key[common.HashLength+common.IncarnationLength:])
//check
hB := tx.Bucket(hBucket)
if hB == nil {
return nil, ErrKeyNotFound
}
v, _ := hB.Get(key)
index := NewStorageIndex()
err := index.Decode(v)
if err != nil {
return nil, err
}
changeSetBlock, ok := index.Search(k, timestamp)
if !ok {
return nil, ErrKeyNotFound
}
csB := tx.Bucket(dbutils.ChangeSetBucket)
if csB == nil {
return nil, ErrKeyNotFound
}
cs, _ := csB.Get(dbutils.CompositeChangeSetKey(dbutils.EncodeTimestamp(changeSetBlock), hBucket))
if err != nil {
return nil, err
}
var data []byte
data, err = dbutils.FindLast(cs, key)
if err != nil {
return nil, ErrKeyNotFound
}
var acc accounts.Account
if err := acc.DecodeForStorage(data); err != nil {
return nil, err
}
if acc.Incarnation > 0 && acc.IsEmptyCodeHash() {
codeBucket := tx.Bucket(dbutils.ContractCodeBucket)
codeHash, _ := codeBucket.Get(dbutils.GenerateStoragePrefix(common.BytesToHash(key), acc.Incarnation))
if len(codeHash) > 0 {
acc.CodeHash = common.BytesToHash(codeHash)
}
data = make([]byte, acc.EncodingLengthForStorage())
acc.EncodeForStorage(data)
}
return data, nil
}