github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/storage/state/spock_state.go (about)

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