github.com/koko1123/flow-go-1@v0.29.6/fvm/state/transaction_state.go (about) 1 package state 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/cadence/runtime/common" 7 8 "github.com/koko1123/flow-go-1/fvm/meter" 9 "github.com/koko1123/flow-go-1/model/flow" 10 ) 11 12 type nestedTransactionStackFrame struct { 13 state *State 14 15 // When nil, the subtransaction will have unrestricted access to the runtime 16 // environment. When non-nil, the subtransaction will only have access to 17 // the parts of the runtime environment necessary for importing/parsing the 18 // program, specifically, environment.ContractReader and 19 // environment.Programs. 20 parseRestriction *common.AddressLocation 21 } 22 23 // TransactionState provides active transaction states and facilitates common 24 // state management operations. 25 type TransactionState struct { 26 enforceLimits bool 27 28 // NOTE: The first frame is always the main transaction, and is not 29 // poppable during the course of the transaction. 30 nestedTransactions []nestedTransactionStackFrame 31 } 32 33 // Opaque identifier used for Restarting nested transactions 34 type NestedTransactionId struct { 35 state *State 36 } 37 38 func (id NestedTransactionId) StateForTestingOnly() *State { 39 return id.state 40 } 41 42 // NewTransactionState constructs a new state transaction which manages nested 43 // transactions. 44 func NewTransactionState( 45 startView View, 46 params StateParameters, 47 ) *TransactionState { 48 startState := NewState(startView, params) 49 return &TransactionState{ 50 enforceLimits: true, 51 nestedTransactions: []nestedTransactionStackFrame{ 52 nestedTransactionStackFrame{ 53 state: startState, 54 parseRestriction: nil, 55 }, 56 }, 57 } 58 } 59 60 func (s *TransactionState) current() nestedTransactionStackFrame { 61 return s.nestedTransactions[s.NumNestedTransactions()] 62 } 63 64 func (s *TransactionState) currentState() *State { 65 return s.current().state 66 } 67 68 // NumNestedTransactions returns the number of uncommitted nested transactions. 69 // Note that the main transaction is not considered a nested transaction. 70 func (s *TransactionState) NumNestedTransactions() int { 71 return len(s.nestedTransactions) - 1 72 } 73 74 // IsParseRestricted returns true if the current nested transaction is in 75 // parse resticted access mode. 76 func (s *TransactionState) IsParseRestricted() bool { 77 return s.current().parseRestriction != nil 78 } 79 80 func (s *TransactionState) MainTransactionId() NestedTransactionId { 81 return NestedTransactionId{ 82 state: s.nestedTransactions[0].state, 83 } 84 } 85 86 // IsCurrent returns true if the provide id refers to the current (nested) 87 // transaction. 88 func (s *TransactionState) IsCurrent(id NestedTransactionId) bool { 89 return s.currentState() == id.state 90 } 91 92 // BeginNestedTransaction creates a unrestricted nested transaction within the 93 // current unrestricted (nested) transaction. The meter parameters are 94 // inherited from the current transaction. This returns error if the current 95 // nested transaction is program restricted. 96 func (s *TransactionState) BeginNestedTransaction() ( 97 NestedTransactionId, 98 error, 99 ) { 100 if s.IsParseRestricted() { 101 return NestedTransactionId{}, fmt.Errorf( 102 "cannot begin a unrestricted nested transaction inside a " + 103 "program restricted nested transaction", 104 ) 105 } 106 107 child := s.currentState().NewChild() 108 s.push(child, nil) 109 110 return NestedTransactionId{ 111 state: child, 112 }, nil 113 } 114 115 // BeginNestedTransactionWithMeterParams creates a unrestricted nested 116 // transaction within the current unrestricted (nested) transaction, using the 117 // provided meter parameters. This returns error if the current nested 118 // transaction is program restricted. 119 func (s *TransactionState) BeginNestedTransactionWithMeterParams( 120 params meter.MeterParameters, 121 ) ( 122 NestedTransactionId, 123 error, 124 ) { 125 if s.IsParseRestricted() { 126 return NestedTransactionId{}, fmt.Errorf( 127 "cannot begin a unrestricted nested transaction inside a " + 128 "program restricted nested transaction", 129 ) 130 } 131 132 child := s.currentState().NewChildWithMeterParams(params) 133 s.push(child, nil) 134 135 return NestedTransactionId{ 136 state: child, 137 }, nil 138 } 139 140 // BeginParseRestrictedNestedTransaction creates a restricted nested 141 // transaction within the current (nested) transaction. The meter parameters 142 // are inherited from the current transaction. 143 func (s *TransactionState) BeginParseRestrictedNestedTransaction( 144 location common.AddressLocation, 145 ) ( 146 NestedTransactionId, 147 error, 148 ) { 149 child := s.currentState().NewChild() 150 s.push(child, &location) 151 152 return NestedTransactionId{ 153 state: child, 154 }, nil 155 } 156 157 func (s *TransactionState) push( 158 child *State, 159 location *common.AddressLocation, 160 ) { 161 s.nestedTransactions = append( 162 s.nestedTransactions, 163 nestedTransactionStackFrame{ 164 state: child, 165 parseRestriction: location, 166 }, 167 ) 168 } 169 170 func (s *TransactionState) pop(op string) (*State, error) { 171 if len(s.nestedTransactions) < 2 { 172 return nil, fmt.Errorf("cannot %s the main transaction", op) 173 } 174 175 child := s.current() 176 s.nestedTransactions = s.nestedTransactions[:len(s.nestedTransactions)-1] 177 178 return child.state, nil 179 } 180 181 func (s *TransactionState) mergeIntoParent() (*State, error) { 182 childState, err := s.pop("commit") 183 if err != nil { 184 return nil, err 185 } 186 187 childState.committed = true 188 189 err = s.current().state.MergeState(childState) 190 if err != nil { 191 return nil, err 192 } 193 194 return childState, nil 195 } 196 197 // Commit commits the changes in the current unrestricted nested transaction 198 // to the parent (nested) transaction. This returns error if the expectedId 199 // does not match the current nested transaction. This returns the committed 200 // state otherwise. 201 // 202 // Note: The returned committed state may be reused by another transaction via 203 // AttachAndCommit to update the transaction bookkeeping, but the caller must 204 // manually invalidate the state. USE WITH EXTREME CAUTION. 205 func (s *TransactionState) Commit( 206 expectedId NestedTransactionId, 207 ) ( 208 *State, 209 error, 210 ) { 211 if !s.IsCurrent(expectedId) { 212 return nil, fmt.Errorf( 213 "cannot commit unexpected nested transaction: id mismatch", 214 ) 215 } 216 217 if s.IsParseRestricted() { 218 // This is due to a programming error. 219 return nil, fmt.Errorf( 220 "cannot commit unexpected nested transaction: parse restricted", 221 ) 222 } 223 224 return s.mergeIntoParent() 225 } 226 227 // CommitParseRestricted commits the changes in the current restricted nested 228 // transaction to the parent (nested) transaction. This returns error if the 229 // specified location does not match the tracked location. This returns the 230 // committed state otherwise. 231 // 232 // Note: The returned committed state may be reused by another transaction via 233 // AttachAndCommit to update the transaction bookkeeping, but the caller must 234 // manually invalidate the state. USE WITH EXTREME CAUTION. 235 func (s *TransactionState) CommitParseRestricted( 236 location common.AddressLocation, 237 ) ( 238 *State, 239 error, 240 ) { 241 currentFrame := s.current() 242 if currentFrame.parseRestriction == nil || 243 *currentFrame.parseRestriction != location { 244 245 // This is due to a programming error. 246 return nil, fmt.Errorf( 247 "cannot commit unexpected nested transaction %v != %v", 248 currentFrame.parseRestriction, 249 location, 250 ) 251 } 252 253 return s.mergeIntoParent() 254 } 255 256 // Pause detaches the current nested transaction from the parent transaction, 257 // and returns the paused nested transaction state. The paused nested 258 // transaction may be resume via Resume. 259 // 260 // WARNING: Pause and Resume are intended for implementing continuation passing 261 // style behavior for the transaction executor, with the assumption that the 262 // states accessed prior to pausing remain valid after resumption. The paused 263 // nested transaction should not be reused across transactions. IT IS NOT 264 // SAFE TO PAUSE A NESTED TRANSACTION IN GENERAL SINCE THAT COULD LEAD TO 265 // PHANTOM READS. 266 func (s *TransactionState) Pause( 267 expectedId NestedTransactionId, 268 ) ( 269 *State, 270 error, 271 ) { 272 if !s.IsCurrent(expectedId) { 273 return nil, fmt.Errorf( 274 "cannot pause unexpected nested transaction: id mismatch", 275 ) 276 } 277 278 if s.IsParseRestricted() { 279 return nil, fmt.Errorf( 280 "cannot Pause parse restricted nested transaction") 281 } 282 283 return s.pop("pause") 284 } 285 286 // Resume attaches the paused nested transaction (state) to the current 287 // transaction. 288 func (s *TransactionState) Resume(pausedState *State) { 289 s.push(pausedState, nil) 290 } 291 292 // AttachAndCommit commits the changes in the cached nested transaction state 293 // to the current (nested) transaction. 294 func (s *TransactionState) AttachAndCommit(cachedState *State) error { 295 s.push(cachedState, nil) 296 _, err := s.mergeIntoParent() 297 return err 298 } 299 300 // RestartNestedTransaction merges all changes that belongs to the nested 301 // transaction about to be restart (for spock/meter bookkeeping), then 302 // wipes its view changes. 303 func (s *TransactionState) RestartNestedTransaction( 304 id NestedTransactionId, 305 ) error { 306 307 // NOTE: We need to verify the id is valid before any merge operation or 308 // else we would accidently merge everything into the main transaction. 309 found := false 310 for _, frame := range s.nestedTransactions { 311 if frame.state == id.state { 312 found = true 313 break 314 } 315 } 316 317 if !found { 318 return fmt.Errorf( 319 "cannot restart nested transaction: nested transaction not found") 320 } 321 322 for s.currentState() != id.state { 323 _, err := s.mergeIntoParent() 324 if err != nil { 325 return fmt.Errorf("cannot restart nested transaction: %w", err) 326 } 327 } 328 329 s.currentState().View().DropDelta() 330 return nil 331 } 332 333 func (s *TransactionState) Get( 334 owner string, 335 key string, 336 enforceLimit bool, 337 ) ( 338 flow.RegisterValue, 339 error, 340 ) { 341 return s.currentState().Get(owner, key, enforceLimit) 342 } 343 344 func (s *TransactionState) Set( 345 owner string, 346 key string, 347 value flow.RegisterValue, 348 enforceLimit bool, 349 ) error { 350 return s.currentState().Set(owner, key, value, enforceLimit) 351 } 352 353 func (s *TransactionState) UpdatedAddresses() []flow.Address { 354 return s.currentState().UpdatedAddresses() 355 } 356 357 func (s *TransactionState) MeterComputation( 358 kind common.ComputationKind, 359 intensity uint, 360 ) error { 361 return s.currentState().MeterComputation(kind, intensity) 362 } 363 364 func (s *TransactionState) MeterMemory( 365 kind common.MemoryKind, 366 intensity uint, 367 ) error { 368 return s.currentState().MeterMemory(kind, intensity) 369 } 370 371 func (s *TransactionState) ComputationIntensities() meter.MeteredComputationIntensities { 372 return s.currentState().ComputationIntensities() 373 } 374 375 func (s *TransactionState) TotalComputationLimit() uint { 376 return s.currentState().TotalComputationLimit() 377 } 378 379 func (s *TransactionState) TotalComputationUsed() uint64 { 380 return s.currentState().TotalComputationUsed() 381 } 382 383 func (s *TransactionState) MemoryIntensities() meter.MeteredMemoryIntensities { 384 return s.currentState().MemoryIntensities() 385 } 386 387 func (s *TransactionState) TotalMemoryEstimate() uint64 { 388 return s.currentState().TotalMemoryEstimate() 389 } 390 391 func (s *TransactionState) InteractionUsed() uint64 { 392 return s.currentState().InteractionUsed() 393 } 394 395 func (s *TransactionState) MeterEmittedEvent(byteSize uint64) error { 396 return s.currentState().MeterEmittedEvent(byteSize) 397 } 398 399 func (s *TransactionState) TotalEmittedEventBytes() uint64 { 400 return s.currentState().TotalEmittedEventBytes() 401 } 402 403 func (s *TransactionState) ViewForTestingOnly() View { 404 return s.currentState().View() 405 } 406 407 func (s *TransactionState) RegisterUpdates() ( 408 []flow.RegisterID, 409 []flow.RegisterValue, 410 ) { 411 return s.currentState().RegisterUpdates() 412 } 413 414 // EnableAllLimitEnforcements enables all the limits 415 func (s *TransactionState) EnableAllLimitEnforcements() { 416 s.enforceLimits = true 417 } 418 419 // DisableAllLimitEnforcements disables all the limits 420 func (s *TransactionState) DisableAllLimitEnforcements() { 421 s.enforceLimits = false 422 } 423 424 // RunWithAllLimitsDisabled runs f with limits disabled 425 func (s *TransactionState) RunWithAllLimitsDisabled(f func()) { 426 if f == nil { 427 return 428 } 429 current := s.enforceLimits 430 s.enforceLimits = false 431 f() 432 s.enforceLimits = current 433 } 434 435 // EnforceComputationLimits returns if the computation limits should be enforced 436 // or not. 437 func (s *TransactionState) EnforceComputationLimits() bool { 438 return s.enforceLimits 439 } 440 441 // EnforceInteractionLimits returns if the interaction limits should be enforced or not 442 func (s *TransactionState) EnforceLimits() bool { 443 return s.enforceLimits 444 }