github.com/onflow/flow-go@v0.33.17/fvm/storage/state/spock_state.go (about)

     1  package state
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  
     7  	"github.com/onflow/flow-go/crypto/hash"
     8  	"github.com/onflow/flow-go/fvm/storage/snapshot"
     9  	"github.com/onflow/flow-go/model/flow"
    10  )
    11  
    12  var (
    13  	// Note: encoding the operation type as part of the spock hash
    14  	// prevents operation injection/substitution attacks.
    15  	getMarker         = []byte("1")
    16  	setMarker         = []byte("2")
    17  	dropChangesMarker = []byte("3")
    18  	mergeMarker       = []byte("4")
    19  )
    20  
    21  type spockState struct {
    22  	*storageState
    23  
    24  	spockSecretHasher hash.Hasher
    25  
    26  	// NOTE: spockState is no longer accessible once Finalize is called.  We
    27  	// can't support access after Finalize since spockSecretHasher.SumHash is
    28  	// not idempotent.  Repeated calls to SumHash (without modifying the input)
    29  	// may return different hashes.
    30  	finalizedSpockSecret []byte
    31  }
    32  
    33  func newSpockState(base snapshot.StorageSnapshot) *spockState {
    34  	return &spockState{
    35  		storageState:      newStorageState(base),
    36  		spockSecretHasher: hash.NewSHA3_256(),
    37  	}
    38  }
    39  
    40  func (state *spockState) NewChild() *spockState {
    41  	return &spockState{
    42  		storageState:      state.storageState.NewChild(),
    43  		spockSecretHasher: hash.NewSHA3_256(),
    44  	}
    45  }
    46  
    47  func (state *spockState) Finalize() *snapshot.ExecutionSnapshot {
    48  	if state.finalizedSpockSecret == nil {
    49  		state.finalizedSpockSecret = state.spockSecretHasher.SumHash()
    50  	}
    51  
    52  	snapshot := state.storageState.Finalize()
    53  	snapshot.SpockSecret = state.finalizedSpockSecret
    54  	return snapshot
    55  }
    56  
    57  func (state *spockState) Merge(snapshot *snapshot.ExecutionSnapshot) error {
    58  	if state.finalizedSpockSecret != nil {
    59  		return fmt.Errorf("cannot Merge on a finalized state")
    60  	}
    61  
    62  	_, err := state.spockSecretHasher.Write(mergeMarker)
    63  	if err != nil {
    64  		return fmt.Errorf("merge SPoCK failed: %w", err)
    65  	}
    66  
    67  	_, err = state.spockSecretHasher.Write(snapshot.SpockSecret)
    68  	if err != nil {
    69  		return fmt.Errorf("merge SPoCK failed: %w", err)
    70  	}
    71  
    72  	return state.storageState.Merge(snapshot)
    73  }
    74  
    75  func (state *spockState) Set(
    76  	id flow.RegisterID,
    77  	value flow.RegisterValue,
    78  ) error {
    79  	if state.finalizedSpockSecret != nil {
    80  		return fmt.Errorf("cannot Set on a finalized state")
    81  	}
    82  
    83  	_, err := state.spockSecretHasher.Write(setMarker)
    84  	if err != nil {
    85  		return fmt.Errorf("set SPoCK failed: %w", err)
    86  	}
    87  
    88  	idBytes := id.Bytes()
    89  
    90  	// Note: encoding the register id / value length as part of spock hash
    91  	// to prevent string injection attacks.
    92  	err = binary.Write(
    93  		state.spockSecretHasher,
    94  		binary.LittleEndian,
    95  		int32(len(idBytes)))
    96  	if err != nil {
    97  		return fmt.Errorf("set SPoCK failed: %w", err)
    98  	}
    99  
   100  	_, err = state.spockSecretHasher.Write(idBytes)
   101  	if err != nil {
   102  		return fmt.Errorf("set SPoCK failed: %w", err)
   103  	}
   104  
   105  	err = binary.Write(
   106  		state.spockSecretHasher,
   107  		binary.LittleEndian,
   108  		int32(len(value)))
   109  	if err != nil {
   110  		return fmt.Errorf("set SPoCK failed: %w", err)
   111  	}
   112  
   113  	_, err = state.spockSecretHasher.Write(value)
   114  	if err != nil {
   115  		return fmt.Errorf("set SPoCK failed: %w", err)
   116  	}
   117  
   118  	return state.storageState.Set(id, value)
   119  }
   120  
   121  func (state *spockState) Get(
   122  	id flow.RegisterID,
   123  ) (
   124  	flow.RegisterValue,
   125  	error,
   126  ) {
   127  	if state.finalizedSpockSecret != nil {
   128  		return nil, fmt.Errorf("cannot Get on a finalized state")
   129  	}
   130  
   131  	_, err := state.spockSecretHasher.Write(getMarker)
   132  	if err != nil {
   133  		return nil, fmt.Errorf("get SPoCK failed: %w", err)
   134  	}
   135  
   136  	idBytes := id.Bytes()
   137  
   138  	// Note: encoding the register id length as part of spock hash to prevent
   139  	// string injection attacks.
   140  	err = binary.Write(
   141  		state.spockSecretHasher,
   142  		binary.LittleEndian,
   143  		int32(len(idBytes)))
   144  	if err != nil {
   145  		return nil, fmt.Errorf("get SPoCK failed: %w", err)
   146  	}
   147  
   148  	_, err = state.spockSecretHasher.Write(idBytes)
   149  	if err != nil {
   150  		return nil, fmt.Errorf("get SPoCK failed: %w", err)
   151  	}
   152  
   153  	return state.storageState.Get(id)
   154  }
   155  
   156  func (state *spockState) DropChanges() error {
   157  	if state.finalizedSpockSecret != nil {
   158  		return fmt.Errorf("cannot DropChanges on a finalized state")
   159  	}
   160  
   161  	_, err := state.spockSecretHasher.Write(dropChangesMarker)
   162  	if err != nil {
   163  		return fmt.Errorf("drop changes SPoCK failed: %w", err)
   164  	}
   165  
   166  	return state.storageState.DropChanges()
   167  }
   168  
   169  func (state *spockState) readSetSize() int {
   170  	return state.storageState.readSetSize()
   171  }
   172  
   173  func (state *spockState) interimReadSet(
   174  	accumulator map[flow.RegisterID]struct{},
   175  ) {
   176  	state.storageState.interimReadSet(accumulator)
   177  }