2019-05-27 13:51:49 +00:00
|
|
|
package ethdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-11-11 19:28:27 +00:00
|
|
|
"sync"
|
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
2019-10-31 10:59:00 +00:00
|
|
|
"github.com/ledgerwatch/turbo-geth/log"
|
2019-05-27 13:51:49 +00:00
|
|
|
"github.com/petar/GoLLRB/llrb"
|
|
|
|
)
|
|
|
|
|
2019-10-30 17:33:01 +00:00
|
|
|
type PutItem struct {
|
|
|
|
key, value []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *PutItem) Less(b llrb.Item) bool {
|
|
|
|
bi := b.(*PutItem)
|
|
|
|
return bytes.Compare(a.key, bi.key) < 0
|
|
|
|
}
|
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
type mutation struct {
|
|
|
|
puts map[string]*llrb.LLRB // Map buckets to RB tree containing items
|
|
|
|
//map[timestamp]map[hBucket]listOfChangedKeys
|
2019-11-07 15:51:25 +00:00
|
|
|
changeSetByBlock map[uint64]map[string][]dbutils.Change
|
|
|
|
mu sync.RWMutex
|
|
|
|
db Database
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) getMem(bucket, key []byte) ([]byte, bool) {
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
if t, ok := m.puts[string(bucket)]; ok {
|
|
|
|
i := t.Get(&PutItem{key: key})
|
|
|
|
if i == nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
if item, ok := i.(*PutItem); ok {
|
|
|
|
if item.value == nil {
|
|
|
|
return nil, true
|
|
|
|
}
|
|
|
|
v := make([]byte, len(item.value))
|
|
|
|
copy(v, item.value)
|
|
|
|
return v, true
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can only be called from the worker thread
|
|
|
|
func (m *mutation) Get(bucket, key []byte) ([]byte, error) {
|
|
|
|
if value, ok := m.getMem(bucket, key); ok {
|
|
|
|
if value == nil {
|
|
|
|
return nil, ErrKeyNotFound
|
|
|
|
}
|
|
|
|
return value, nil
|
|
|
|
}
|
|
|
|
if m.db != nil {
|
|
|
|
return m.db.Get(bucket, key)
|
|
|
|
}
|
|
|
|
return nil, ErrKeyNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) GetS(hBucket, key []byte, timestamp uint64) ([]byte, error) {
|
|
|
|
composite, _ := dbutils.CompositeKeySuffix(key, timestamp)
|
|
|
|
return m.Get(hBucket, composite)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) getNoLock(bucket, key []byte) ([]byte, error) {
|
|
|
|
if t, ok := m.puts[string(bucket)]; ok {
|
|
|
|
i := t.Get(&PutItem{key: key})
|
|
|
|
if i != nil {
|
|
|
|
if item, ok := i.(*PutItem); ok {
|
|
|
|
if item.value == nil {
|
|
|
|
return nil, ErrKeyNotFound
|
|
|
|
}
|
|
|
|
return common.CopyBytes(item.value), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if m.db != nil {
|
|
|
|
return m.db.Get(bucket, key)
|
|
|
|
}
|
|
|
|
return nil, ErrKeyNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) hasMem(bucket, key []byte) bool {
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
if t, ok := m.puts[string(bucket)]; ok {
|
|
|
|
return t.Has(&PutItem{key: key})
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Has(bucket, key []byte) (bool, error) {
|
|
|
|
if m.hasMem(bucket, key) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
if m.db != nil {
|
|
|
|
return m.db.Has(bucket, key)
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Size() int {
|
|
|
|
if m.db == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return m.db.Size()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Put(bucket, key []byte, value []byte) error {
|
|
|
|
bb := make([]byte, len(bucket))
|
|
|
|
copy(bb, bucket)
|
|
|
|
k := make([]byte, len(key))
|
|
|
|
copy(k, key)
|
|
|
|
v := make([]byte, len(value))
|
|
|
|
copy(v, value)
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
var t *llrb.LLRB
|
|
|
|
var ok bool
|
|
|
|
if t, ok = m.puts[string(bb)]; !ok {
|
|
|
|
t = llrb.New()
|
|
|
|
m.puts[string(bb)] = t
|
|
|
|
}
|
|
|
|
t.ReplaceOrInsert(&PutItem{key: k, value: v})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assumes that bucket, key, and value won't be modified
|
2019-10-31 10:59:00 +00:00
|
|
|
func (m *mutation) PutS(hBucket, key, value []byte, timestamp uint64, noHistory bool) error {
|
2019-05-27 13:51:49 +00:00
|
|
|
//fmt.Printf("PutS bucket %x key %x value %x timestamp %d\n", bucket, key, value, timestamp)
|
|
|
|
composite, _ := dbutils.CompositeKeySuffix(key, timestamp)
|
2019-11-07 15:51:25 +00:00
|
|
|
changesByBucket, ok := m.changeSetByBlock[timestamp]
|
2019-05-27 13:51:49 +00:00
|
|
|
if !ok {
|
2019-11-07 15:51:25 +00:00
|
|
|
changesByBucket = make(map[string][]dbutils.Change)
|
|
|
|
m.changeSetByBlock[timestamp] = changesByBucket
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
2019-11-07 15:51:25 +00:00
|
|
|
changes, ok := changesByBucket[string(hBucket)]
|
2019-05-27 13:51:49 +00:00
|
|
|
if !ok {
|
2019-11-07 15:51:25 +00:00
|
|
|
changes = make([]dbutils.Change, 0)
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
2019-11-07 15:51:25 +00:00
|
|
|
changes = append(changes, dbutils.Change{
|
2019-10-31 10:59:00 +00:00
|
|
|
Key: key,
|
|
|
|
Value: value,
|
|
|
|
})
|
2019-11-07 15:51:25 +00:00
|
|
|
changesByBucket[string(hBucket)] = changes
|
2019-10-31 10:59:00 +00:00
|
|
|
if noHistory {
|
|
|
|
return nil
|
|
|
|
}
|
2019-05-27 13:51:49 +00:00
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
var ht *llrb.LLRB
|
|
|
|
if ht, ok = m.puts[string(hBucket)]; !ok {
|
|
|
|
ht = llrb.New()
|
|
|
|
m.puts[string(hBucket)] = ht
|
|
|
|
}
|
|
|
|
ht.ReplaceOrInsert(&PutItem{key: composite, value: value})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) MultiPut(tuples ...[]byte) (uint64, error) {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
l := len(tuples)
|
|
|
|
for i := 0; i < l; i += 3 {
|
|
|
|
var t *llrb.LLRB
|
|
|
|
var ok bool
|
|
|
|
if t, ok = m.puts[string(tuples[i])]; !ok {
|
|
|
|
t = llrb.New()
|
|
|
|
m.puts[string(tuples[i])] = t
|
|
|
|
}
|
|
|
|
t.ReplaceOrInsert(&PutItem{key: tuples[i+1], value: tuples[i+2]})
|
|
|
|
}
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) BatchSize() int {
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
size := 0
|
|
|
|
for _, t := range m.puts {
|
|
|
|
size += t.Len()
|
|
|
|
}
|
|
|
|
return size
|
|
|
|
}
|
|
|
|
|
2019-11-21 15:12:38 +00:00
|
|
|
// IdealBatchSize defines the size of the data batches should ideally add in one write.
|
|
|
|
func (m *mutation) IdealBatchSize() int {
|
|
|
|
return m.db.IdealBatchSize()
|
|
|
|
}
|
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
func (m *mutation) GetAsOf(bucket, hBucket, key []byte, timestamp uint64) ([]byte, error) {
|
|
|
|
if m.db == nil {
|
|
|
|
panic("Not implemented")
|
|
|
|
} else {
|
|
|
|
return m.db.GetAsOf(bucket, hBucket, key, timestamp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) walkMem(bucket, startkey []byte, fixedbits uint, walker func([]byte, []byte) (bool, error)) error {
|
|
|
|
fixedbytes, mask := bytesmask(fixedbits)
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
var t *llrb.LLRB
|
|
|
|
var ok bool
|
|
|
|
if t, ok = m.puts[string(bucket)]; !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for nextkey := startkey; nextkey != nil; {
|
|
|
|
from := nextkey
|
|
|
|
nextkey = nil
|
|
|
|
var extErr error
|
|
|
|
t.AscendGreaterOrEqual(&PutItem{key: from}, func(i llrb.Item) bool {
|
|
|
|
item := i.(*PutItem)
|
|
|
|
if item.value == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if fixedbits > 0 && (!bytes.Equal(item.key[:fixedbytes-1], startkey[:fixedbytes-1]) || (item.key[fixedbytes-1]&mask) != (startkey[fixedbytes-1]&mask)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
goOn, err := walker(item.key, item.value)
|
|
|
|
if err != nil {
|
|
|
|
extErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return goOn
|
|
|
|
})
|
|
|
|
if extErr != nil {
|
|
|
|
return extErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:33:01 +00:00
|
|
|
// WARNING: Merged mem/DB walk is not implemented
|
2019-05-27 13:51:49 +00:00
|
|
|
func (m *mutation) Walk(bucket, startkey []byte, fixedbits uint, walker func([]byte, []byte) (bool, error)) error {
|
|
|
|
if m.db == nil {
|
|
|
|
return m.walkMem(bucket, startkey, fixedbits, walker)
|
|
|
|
}
|
|
|
|
return m.db.Walk(bucket, startkey, fixedbits, walker)
|
|
|
|
}
|
|
|
|
|
2019-11-21 15:12:38 +00:00
|
|
|
func (m *mutation) multiWalkMem(bucket []byte, startkeys [][]byte, fixedbits []uint, walker func(int, []byte, []byte) error) error {
|
2019-05-27 13:51:49 +00:00
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:33:01 +00:00
|
|
|
// WARNING: Merged mem/DB walk is not implemented
|
2019-11-21 15:12:38 +00:00
|
|
|
func (m *mutation) MultiWalk(bucket []byte, startkeys [][]byte, fixedbits []uint, walker func(int, []byte, []byte) error) error {
|
2019-05-27 13:51:49 +00:00
|
|
|
if m.db == nil {
|
|
|
|
return m.multiWalkMem(bucket, startkeys, fixedbits, walker)
|
|
|
|
}
|
|
|
|
return m.db.MultiWalk(bucket, startkeys, fixedbits, walker)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) WalkAsOf(bucket, hBucket, startkey []byte, fixedbits uint, timestamp uint64, walker func([]byte, []byte) (bool, error)) error {
|
|
|
|
if m.db == nil {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.db.WalkAsOf(bucket, hBucket, startkey, fixedbits, timestamp, walker)
|
|
|
|
}
|
|
|
|
|
2019-11-21 15:12:38 +00:00
|
|
|
func (m *mutation) MultiWalkAsOf(bucket, hBucket []byte, startkeys [][]byte, fixedbits []uint, timestamp uint64, walker func(int, []byte, []byte) error) error {
|
2019-05-27 13:51:49 +00:00
|
|
|
if m.db == nil {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
return m.db.MultiWalkAsOf(bucket, hBucket, startkeys, fixedbits, timestamp, walker)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) RewindData(timestampSrc, timestampDst uint64, df func(hBucket, key, value []byte) error) error {
|
|
|
|
return rewindData(m, timestampSrc, timestampDst, df)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Delete(bucket, key []byte) error {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
bb := make([]byte, len(bucket))
|
|
|
|
copy(bb, bucket)
|
|
|
|
var t *llrb.LLRB
|
|
|
|
var ok bool
|
|
|
|
if t, ok = m.puts[string(bb)]; !ok {
|
|
|
|
t = llrb.New()
|
|
|
|
m.puts[string(bb)] = t
|
|
|
|
}
|
|
|
|
k := make([]byte, len(key))
|
|
|
|
copy(k, key)
|
|
|
|
t.ReplaceOrInsert(&PutItem{key: k, value: nil})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-07 15:51:25 +00:00
|
|
|
// Deletes all keys with specified suffix(blockNum) from all the buckets
|
2019-05-27 13:51:49 +00:00
|
|
|
func (m *mutation) DeleteTimestamp(timestamp uint64) error {
|
2019-11-07 15:51:25 +00:00
|
|
|
encodedTS := dbutils.EncodeTimestamp(timestamp)
|
2019-05-27 13:51:49 +00:00
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
var t *llrb.LLRB
|
|
|
|
var ok bool
|
2019-10-31 10:59:00 +00:00
|
|
|
if t, ok = m.puts[string(dbutils.ChangeSetBucket)]; !ok {
|
2019-05-27 13:51:49 +00:00
|
|
|
t = llrb.New()
|
2019-10-31 10:59:00 +00:00
|
|
|
m.puts[string(dbutils.ChangeSetBucket)] = t
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
2019-11-07 15:51:25 +00:00
|
|
|
err := m.Walk(dbutils.ChangeSetBucket, encodedTS, uint(8*len(encodedTS)), func(k, v []byte) (bool, error) {
|
|
|
|
// k = encodedTS + hBucket
|
|
|
|
hBucket := k[len(encodedTS):]
|
2019-10-31 10:59:00 +00:00
|
|
|
changedAccounts, err := dbutils.Decode(v)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
keycount := changedAccounts.KeyCount()
|
|
|
|
var ht *llrb.LLRB
|
|
|
|
var ok bool
|
|
|
|
if keycount > 0 {
|
|
|
|
hBucketStr := string(common.CopyBytes(hBucket))
|
|
|
|
if ht, ok = m.puts[hBucketStr]; !ok {
|
|
|
|
ht = llrb.New()
|
|
|
|
m.puts[hBucketStr] = ht
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-31 10:59:00 +00:00
|
|
|
err = changedAccounts.Walk(func(kk, _ []byte) error {
|
2019-11-07 15:51:25 +00:00
|
|
|
kk = append(kk, encodedTS...)
|
2019-05-27 13:51:49 +00:00
|
|
|
ht.ReplaceOrInsert(&PutItem{key: kk, value: nil})
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
t.ReplaceOrInsert(&PutItem{key: common.CopyBytes(k), value: nil})
|
|
|
|
return true, nil
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Commit() (uint64, error) {
|
|
|
|
if m.db == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
var t *llrb.LLRB
|
|
|
|
var ok bool
|
2019-11-07 15:51:25 +00:00
|
|
|
if len(m.changeSetByBlock) > 0 {
|
2019-10-31 10:59:00 +00:00
|
|
|
if t, ok = m.puts[string(dbutils.ChangeSetBucket)]; !ok {
|
2019-05-27 13:51:49 +00:00
|
|
|
t = llrb.New()
|
2019-10-31 10:59:00 +00:00
|
|
|
m.puts[string(dbutils.ChangeSetBucket)] = t
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
2019-11-07 15:51:25 +00:00
|
|
|
for timestamp, changesByBucket := range m.changeSetByBlock {
|
|
|
|
encodedTS := dbutils.EncodeTimestamp(timestamp)
|
|
|
|
for bucketStr, changes := range changesByBucket {
|
2019-05-27 13:51:49 +00:00
|
|
|
hBucket := []byte(bucketStr)
|
2019-11-07 15:51:25 +00:00
|
|
|
changeSetKey := dbutils.CompositeChangeSetKey(encodedTS, hBucket)
|
|
|
|
dat, err := m.getNoLock(dbutils.ChangeSetBucket, changeSetKey)
|
2019-05-27 13:51:49 +00:00
|
|
|
if err != nil && err != ErrKeyNotFound {
|
|
|
|
return 0, err
|
|
|
|
}
|
2019-10-31 10:59:00 +00:00
|
|
|
|
|
|
|
changedAccounts, err := dbutils.Decode(dat)
|
|
|
|
if err != nil {
|
2019-11-07 15:51:25 +00:00
|
|
|
log.Error("Decode changedAccounts error on commit", "err", err)
|
2019-10-31 10:59:00 +00:00
|
|
|
}
|
|
|
|
|
2019-11-07 15:51:25 +00:00
|
|
|
changedAccounts = changedAccounts.MultiAdd(changes)
|
2019-10-31 10:59:00 +00:00
|
|
|
changedRLP, err := dbutils.Encode(changedAccounts)
|
|
|
|
if err != nil {
|
2019-11-07 15:51:25 +00:00
|
|
|
log.Error("Encode changedAccounts error on commit", "err", err)
|
2019-10-31 10:59:00 +00:00
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2019-11-07 15:51:25 +00:00
|
|
|
t.ReplaceOrInsert(&PutItem{key: changeSetKey, value: changedRLP})
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-07 15:51:25 +00:00
|
|
|
m.changeSetByBlock = make(map[uint64]map[string][]dbutils.Change)
|
2019-05-27 13:51:49 +00:00
|
|
|
size := 0
|
|
|
|
for _, t := range m.puts {
|
|
|
|
size += t.Len()
|
|
|
|
}
|
|
|
|
tuples := make([][]byte, size*3)
|
|
|
|
var index int
|
|
|
|
for bucketStr, bt := range m.puts {
|
|
|
|
bucketB := []byte(bucketStr)
|
|
|
|
bt.AscendGreaterOrEqual(&PutItem{}, func(i llrb.Item) bool {
|
|
|
|
item := i.(*PutItem)
|
|
|
|
tuples[index] = bucketB
|
|
|
|
index++
|
|
|
|
tuples[index] = item.key
|
|
|
|
index++
|
|
|
|
tuples[index] = item.value
|
|
|
|
index++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
var written uint64
|
|
|
|
var putErr error
|
|
|
|
if written, putErr = m.db.MultiPut(tuples...); putErr != nil {
|
|
|
|
return 0, putErr
|
|
|
|
}
|
|
|
|
m.puts = make(map[string]*llrb.LLRB)
|
|
|
|
return written, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Rollback() {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
2019-11-07 15:51:25 +00:00
|
|
|
m.changeSetByBlock = make(map[uint64]map[string][]dbutils.Change)
|
2019-05-27 13:51:49 +00:00
|
|
|
m.puts = make(map[string]*llrb.LLRB)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Keys() ([][]byte, error) {
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
size := 0
|
|
|
|
for _, t := range m.puts {
|
|
|
|
size += t.Len()
|
|
|
|
}
|
|
|
|
pairs := make([][]byte, 2*size)
|
|
|
|
idx := 0
|
|
|
|
for bucketStr, bt := range m.puts {
|
|
|
|
bucketB := []byte(bucketStr)
|
|
|
|
bt.AscendGreaterOrEqual(&PutItem{}, func(i llrb.Item) bool {
|
|
|
|
item := i.(*PutItem)
|
|
|
|
pairs[idx] = bucketB
|
|
|
|
idx++
|
|
|
|
pairs[idx] = item.key
|
|
|
|
idx++
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return pairs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Close() {
|
|
|
|
m.Rollback()
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:33:01 +00:00
|
|
|
func (m *mutation) NewBatch() DbWithPendingMutations {
|
2019-05-27 13:51:49 +00:00
|
|
|
mm := &mutation{
|
2019-11-07 15:51:25 +00:00
|
|
|
db: m,
|
|
|
|
puts: make(map[string]*llrb.LLRB),
|
|
|
|
changeSetByBlock: make(map[uint64]map[string][]dbutils.Change),
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
return mm
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) MemCopy() Database {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
// [TURBO-GETH] Freezer support (not implemented yet)
|
|
|
|
// Ancients returns an error as we don't have a backing chain freezer.
|
|
|
|
func (m *mutation) Ancients() (uint64, error) {
|
|
|
|
return 0, errNotSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
// TruncateAncients returns an error as we don't have a backing chain freezer.
|
|
|
|
func (m *mutation) TruncateAncients(items uint64) error {
|
|
|
|
return errNotSupported
|
|
|
|
}
|