github.com/ethersphere/bee/v2@v2.2.0/pkg/statestore/storeadapter/storeadapter.go (about)

     1  // Copyright 2023 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package storeadapter
     6  
     7  import (
     8  	"encoding"
     9  	"encoding/json"
    10  	"fmt"
    11  	"strings"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/storage"
    14  	"github.com/ethersphere/bee/v2/pkg/storage/migration"
    15  	"github.com/ethersphere/bee/v2/pkg/storage/storageutil"
    16  )
    17  
    18  // stateStoreNamespace is the namespace used for state storage.
    19  const stateStoreNamespace = "ss"
    20  
    21  var _ storage.Item = (*proxyItem)(nil)
    22  
    23  // proxyItem is a proxy object that implements the Item interface.
    24  // It is an intermediary object between StateStorer and Store interfaces calls.
    25  type proxyItem struct {
    26  	ns  string
    27  	key string
    28  	obj interface{}
    29  }
    30  
    31  // ID implements Item interface.
    32  func (pi *proxyItem) ID() string {
    33  	return pi.key
    34  }
    35  
    36  // Namespace implements Item interface.
    37  func (pi *proxyItem) Namespace() string {
    38  	return pi.ns
    39  }
    40  
    41  // Marshal implements Item interface.
    42  func (pi *proxyItem) Marshal() ([]byte, error) {
    43  	if pi == nil || pi.obj == nil {
    44  		return nil, nil
    45  	}
    46  
    47  	switch m := pi.obj.(type) {
    48  	case encoding.BinaryMarshaler:
    49  		return m.MarshalBinary()
    50  	case storage.Marshaler:
    51  		return m.Marshal()
    52  	}
    53  	return json.Marshal(pi.obj)
    54  }
    55  
    56  // Unmarshal implements Item interface.
    57  func (pi *proxyItem) Unmarshal(data []byte) error {
    58  	if pi == nil || pi.obj == nil {
    59  		return nil
    60  	}
    61  
    62  	switch m := pi.obj.(type) {
    63  	case encoding.BinaryUnmarshaler:
    64  		return m.UnmarshalBinary(data)
    65  	case storage.Unmarshaler:
    66  		return m.Unmarshal(data)
    67  	}
    68  	return json.Unmarshal(data, &pi.obj)
    69  }
    70  
    71  // Clone implements Item interface.
    72  func (pi *proxyItem) Clone() storage.Item {
    73  	if pi == nil {
    74  		return nil
    75  	}
    76  
    77  	obj := pi.obj
    78  	if cloner, ok := pi.obj.(storage.Cloner); ok {
    79  		obj = cloner.Clone()
    80  	}
    81  	return &proxyItem{
    82  		ns:  pi.ns,
    83  		key: pi.key,
    84  		obj: obj,
    85  	}
    86  }
    87  
    88  // String implements Item interface.
    89  func (pi proxyItem) String() string {
    90  	return storageutil.JoinFields(pi.Namespace(), pi.ID())
    91  }
    92  
    93  // newProxyItem creates a new proxyItem.
    94  func newProxyItem(key string, obj interface{}) *proxyItem {
    95  	return &proxyItem{ns: stateStoreNamespace, key: key, obj: obj}
    96  }
    97  
    98  var _ storage.Item = (*rawItem)(nil)
    99  
   100  // rawItem is a proxy object that implements the Item interface.
   101  // It is an intermediary object between StateStorer and Store iterator calls.
   102  type rawItem struct {
   103  	*proxyItem
   104  }
   105  
   106  // Marshal implements Item interface.
   107  func (ri *rawItem) Marshal() ([]byte, error) {
   108  	if ri == nil || ri.proxyItem == nil || ri.proxyItem.obj == nil {
   109  		return nil, nil
   110  	}
   111  
   112  	if buf, ok := ri.proxyItem.obj.([]byte); ok {
   113  		return buf, nil
   114  	}
   115  
   116  	return ri.proxyItem.Marshal()
   117  }
   118  
   119  // Unmarshal implements Item interface.
   120  func (ri *rawItem) Unmarshal(data []byte) error {
   121  	if ri == nil || ri.proxyItem == nil || ri.proxyItem.obj == nil || len(data) == 0 {
   122  		return nil
   123  	}
   124  
   125  	if buf, ok := ri.proxyItem.obj.([]byte); ok {
   126  		ri.proxyItem.obj = append(buf[:0], data...)
   127  		return nil
   128  	}
   129  
   130  	return ri.proxyItem.Unmarshal(data)
   131  }
   132  
   133  var (
   134  	_ storage.StateStorer        = (*StateStorerAdapter)(nil)
   135  	_ storage.StateStorerCleaner = (*StateStorerAdapter)(nil)
   136  )
   137  
   138  // StateStorerAdapter is an adapter from Store to the StateStorer.
   139  type StateStorerAdapter struct {
   140  	storage storage.Store
   141  }
   142  
   143  // Close implements StateStorer interface.
   144  func (s *StateStorerAdapter) Close() error {
   145  	return s.storage.Close()
   146  }
   147  
   148  // Get implements StateStorer interface.
   149  func (s *StateStorerAdapter) Get(key string, obj interface{}) (err error) {
   150  	return s.storage.Get(newProxyItem(key, obj))
   151  }
   152  
   153  // Put implements StateStorer interface.
   154  func (s *StateStorerAdapter) Put(key string, obj interface{}) (err error) {
   155  	return s.storage.Put(newProxyItem(key, obj))
   156  }
   157  
   158  // Delete implements StateStorer interface.
   159  func (s *StateStorerAdapter) Delete(key string) (err error) {
   160  	return s.storage.Delete(newProxyItem(key, nil))
   161  }
   162  
   163  // Iterate implements StateStorer interface.
   164  func (s *StateStorerAdapter) Iterate(prefix string, iterFunc storage.StateIterFunc) (err error) {
   165  	return s.storage.Iterate(
   166  		storage.Query{
   167  			Factory: func() storage.Item { return &rawItem{newProxyItem("", []byte(nil))} },
   168  			Prefix:  prefix,
   169  		},
   170  		func(res storage.Result) (stop bool, err error) {
   171  			key := []byte(prefix + res.ID)
   172  			val, err := res.Entry.(*rawItem).Marshal()
   173  			if err != nil {
   174  				return false, err
   175  			}
   176  			return iterFunc(key, val)
   177  		},
   178  	)
   179  }
   180  
   181  func (s *StateStorerAdapter) Nuke() error {
   182  	var (
   183  		prefixesToPreserve = []string{
   184  			"non-mineable-overlay",
   185  			"overlayV2_nonce",
   186  			"pseudosettle",
   187  			"accounting",
   188  			"swap",
   189  		}
   190  		keys []string
   191  		err  error
   192  	)
   193  
   194  	keys, err = s.collectKeysExcept(prefixesToPreserve)
   195  	if err != nil {
   196  		return fmt.Errorf("collect keys except: %w", err)
   197  	}
   198  	return s.deleteKeys(keys)
   199  }
   200  
   201  func (s *StateStorerAdapter) ClearForHopping() error {
   202  	var (
   203  		prefixesToPreserve = []string{
   204  			"swap_chequebook", // to not redeploy chequebook contract
   205  			"batchstore",      // avoid unnecessary syncing
   206  			"transaction",     // to not resync blockchain transactions
   207  		}
   208  		keys []string
   209  		err  error
   210  	)
   211  
   212  	keys, err = s.collectKeysExcept(prefixesToPreserve)
   213  	if err != nil {
   214  		return fmt.Errorf("collect keys except: %w", err)
   215  	}
   216  	return s.deleteKeys(keys)
   217  }
   218  
   219  func (s *StateStorerAdapter) collectKeysExcept(prefixesToPreserve []string) (keys []string, err error) {
   220  	if err := s.Iterate("", func(k, v []byte) (bool, error) {
   221  		stk := string(k)
   222  		has := false
   223  		for _, v := range prefixesToPreserve {
   224  			if strings.HasPrefix(stk, v) {
   225  				has = true
   226  				break
   227  			}
   228  		}
   229  		if !has {
   230  			keys = append(keys, stk)
   231  		}
   232  		return false, nil
   233  	}); err != nil {
   234  		return nil, err
   235  	}
   236  	return keys, nil
   237  }
   238  
   239  func (s *StateStorerAdapter) deleteKeys(keys []string) error {
   240  	for _, v := range keys {
   241  		err := s.Delete(v)
   242  		if err != nil {
   243  			return fmt.Errorf("deleting key %s: %w", v, err)
   244  		}
   245  	}
   246  	return nil
   247  }
   248  
   249  // NewStateStorerAdapter creates a new StateStorerAdapter.
   250  func NewStateStorerAdapter(storage storage.Store) (*StateStorerAdapter, error) {
   251  	err := migration.Migrate(storage, "migration", allSteps(storage))
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	return &StateStorerAdapter{storage: storage}, nil
   256  }