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 }