github.com/ledgerwatch/erigon-lib@v1.0.0/kv/temporal/historyv2/changeset.go (about)

     1  /*
     2     Copyright 2022 Erigon contributors
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package historyv2
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	math2 "math"
    24  	"reflect"
    25  
    26  	"github.com/ledgerwatch/erigon-lib/common/hexutility"
    27  	"github.com/ledgerwatch/erigon-lib/common/length"
    28  	"github.com/ledgerwatch/erigon-lib/kv"
    29  )
    30  
    31  func NewChangeSet() *ChangeSet {
    32  	return &ChangeSet{
    33  		Changes: make([]Change, 0),
    34  	}
    35  }
    36  
    37  type Change struct {
    38  	Key   []byte
    39  	Value []byte
    40  }
    41  
    42  // ChangeSet is a map with keys of the same size.
    43  // Both keys and values are byte strings.
    44  type ChangeSet struct {
    45  	// Invariant: all keys are of the same size.
    46  	Changes []Change
    47  	keyLen  int
    48  }
    49  
    50  // BEGIN sort.Interface
    51  
    52  func (s *ChangeSet) Len() int {
    53  	return len(s.Changes)
    54  }
    55  
    56  func (s *ChangeSet) Swap(i, j int) {
    57  	s.Changes[i], s.Changes[j] = s.Changes[j], s.Changes[i]
    58  }
    59  
    60  func (s *ChangeSet) Less(i, j int) bool {
    61  	cmp := bytes.Compare(s.Changes[i].Key, s.Changes[j].Key)
    62  	if cmp == 0 {
    63  		cmp = bytes.Compare(s.Changes[i].Value, s.Changes[j].Value)
    64  	}
    65  	return cmp < 0
    66  }
    67  
    68  // END sort.Interface
    69  func (s *ChangeSet) KeySize() int {
    70  	if s.keyLen != 0 {
    71  		return s.keyLen
    72  	}
    73  	for _, c := range s.Changes {
    74  		return len(c.Key)
    75  	}
    76  	return 0
    77  }
    78  
    79  func (s *ChangeSet) checkKeySize(key []byte) error {
    80  	if (s.Len() == 0 && s.KeySize() == 0) || (len(key) == s.KeySize() && len(key) > 0) {
    81  		return nil
    82  	}
    83  
    84  	return fmt.Errorf("wrong key size in AccountChangeSet: expected %d, actual %d", s.KeySize(), len(key))
    85  }
    86  
    87  // Add adds a new entry to the AccountChangeSet.
    88  // One must not add an existing key
    89  // and may add keys only of the same size.
    90  func (s *ChangeSet) Add(key []byte, value []byte) error {
    91  	if err := s.checkKeySize(key); err != nil {
    92  		return err
    93  	}
    94  
    95  	s.Changes = append(s.Changes, Change{
    96  		Key:   key,
    97  		Value: value,
    98  	})
    99  	return nil
   100  }
   101  
   102  func (s *ChangeSet) ChangedKeys() map[string]struct{} {
   103  	m := make(map[string]struct{}, len(s.Changes))
   104  	for i := range s.Changes {
   105  		m[string(s.Changes[i].Key)] = struct{}{}
   106  	}
   107  	return m
   108  }
   109  
   110  func (s *ChangeSet) Equals(s2 *ChangeSet) bool {
   111  	return reflect.DeepEqual(s.Changes, s2.Changes)
   112  }
   113  
   114  // Encoded Method
   115  func FromDBFormat(dbKey, dbValue []byte) (uint64, []byte, []byte, error) {
   116  	if len(dbKey) == 8 {
   117  		return DecodeAccounts(dbKey, dbValue)
   118  	} else {
   119  		return DecodeStorage(dbKey, dbValue)
   120  	}
   121  }
   122  
   123  func AvailableFrom(tx kv.Tx) (uint64, error) {
   124  	c, err := tx.Cursor(kv.AccountChangeSet)
   125  	if err != nil {
   126  		return math2.MaxUint64, err
   127  	}
   128  	defer c.Close()
   129  	k, _, err := c.First()
   130  	if err != nil {
   131  		return math2.MaxUint64, err
   132  	}
   133  	if len(k) == 0 {
   134  		return math2.MaxUint64, nil
   135  	}
   136  	return binary.BigEndian.Uint64(k), nil
   137  }
   138  func AvailableStorageFrom(tx kv.Tx) (uint64, error) {
   139  	c, err := tx.Cursor(kv.StorageChangeSet)
   140  	if err != nil {
   141  		return math2.MaxUint64, err
   142  	}
   143  	defer c.Close()
   144  	k, _, err := c.First()
   145  	if err != nil {
   146  		return math2.MaxUint64, err
   147  	}
   148  	if len(k) == 0 {
   149  		return math2.MaxUint64, nil
   150  	}
   151  	return binary.BigEndian.Uint64(k), nil
   152  }
   153  
   154  func ForEach(db kv.Tx, bucket string, startkey []byte, walker func(blockN uint64, k, v []byte) error) error {
   155  	var blockN uint64
   156  	return db.ForEach(bucket, startkey, func(k, v []byte) error {
   157  		var err error
   158  		blockN, k, v, err = FromDBFormat(k, v)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		return walker(blockN, k, v)
   163  	})
   164  }
   165  func ForPrefix(db kv.Tx, bucket string, startkey []byte, walker func(blockN uint64, k, v []byte) error) error {
   166  	var blockN uint64
   167  	return db.ForPrefix(bucket, startkey, func(k, v []byte) error {
   168  		var err error
   169  		blockN, k, v, err = FromDBFormat(k, v)
   170  		if err != nil {
   171  			return err
   172  		}
   173  		return walker(blockN, k, v)
   174  	})
   175  }
   176  
   177  func Truncate(tx kv.RwTx, from uint64) error {
   178  	keyStart := hexutility.EncodeTs(from)
   179  
   180  	{
   181  		c, err := tx.RwCursorDupSort(kv.AccountChangeSet)
   182  		if err != nil {
   183  			return err
   184  		}
   185  		defer c.Close()
   186  		for k, _, err := c.Seek(keyStart); k != nil; k, _, err = c.NextNoDup() {
   187  			if err != nil {
   188  				return err
   189  			}
   190  			if err = tx.Delete(kv.AccountChangeSet, k); err != nil {
   191  				return err
   192  			}
   193  			if err != nil {
   194  				return err
   195  			}
   196  		}
   197  	}
   198  	{
   199  		c, err := tx.RwCursorDupSort(kv.StorageChangeSet)
   200  		if err != nil {
   201  			return err
   202  		}
   203  		defer c.Close()
   204  		for k, _, err := c.Seek(keyStart); k != nil; k, _, err = c.NextNoDup() {
   205  			if err != nil {
   206  				return err
   207  			}
   208  			if err = tx.Delete(kv.StorageChangeSet, k); err != nil {
   209  				return err
   210  			}
   211  		}
   212  	}
   213  	return nil
   214  }
   215  
   216  type CSMapper struct {
   217  	IndexBucket   string
   218  	IndexChunkKey func([]byte, uint64) []byte
   219  	Find          func(cursor kv.CursorDupSort, blockNumber uint64, key []byte) ([]byte, error)
   220  	New           func() *ChangeSet
   221  	Encode        Encoder
   222  	Decode        Decoder
   223  }
   224  
   225  var Mapper = map[string]CSMapper{
   226  	kv.AccountChangeSet: {
   227  		IndexBucket:   kv.E2AccountsHistory,
   228  		IndexChunkKey: AccountIndexChunkKey,
   229  		New:           NewAccountChangeSet,
   230  		Find:          FindAccount,
   231  		Encode:        EncodeAccounts,
   232  		Decode:        DecodeAccounts,
   233  	},
   234  	kv.StorageChangeSet: {
   235  		IndexBucket:   kv.E2StorageHistory,
   236  		IndexChunkKey: StorageIndexChunkKey,
   237  		Find:          FindStorage,
   238  		New:           NewStorageChangeSet,
   239  		Encode:        EncodeStorage,
   240  		Decode:        DecodeStorage,
   241  	},
   242  }
   243  
   244  func AccountIndexChunkKey(key []byte, blockNumber uint64) []byte {
   245  	blockNumBytes := make([]byte, length.Addr+8)
   246  	copy(blockNumBytes, key)
   247  	binary.BigEndian.PutUint64(blockNumBytes[length.Addr:], blockNumber)
   248  
   249  	return blockNumBytes
   250  }
   251  
   252  func StorageIndexChunkKey(key []byte, blockNumber uint64) []byte {
   253  	//remove incarnation and add block number
   254  	blockNumBytes := make([]byte, length.Addr+length.Hash+8)
   255  	copy(blockNumBytes, key[:length.Addr])
   256  	copy(blockNumBytes[length.Addr:], key[length.Addr+length.Incarnation:])
   257  	binary.BigEndian.PutUint64(blockNumBytes[length.Addr+length.Hash:], blockNumber)
   258  
   259  	return blockNumBytes
   260  }