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  }