Skip to content

Commit 55f5744

Browse files
Merge #4200
4200: Simplify transaction preprocessing r=pattyshack a=pattyshack Pause/Resume nested txn was a premature optimization. Removing pause/resume, and reordering execution back to normal ordering simplify interim read set computation, and removes unnecessary assumptions. Co-authored-by: Patrick Lee <[email protected]>
2 parents 1306e52 + f6bcbd2 commit 55f5744

File tree

3 files changed

+55
-181
lines changed

3 files changed

+55
-181
lines changed

fvm/state/transaction_state.go

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -128,27 +128,6 @@ type NestedTransaction interface {
128128
error,
129129
)
130130

131-
// PauseNestedTransaction detaches the current nested transaction from the
132-
// parent transaction, and returns the paused nested transaction state.
133-
// The paused nested transaction may be resume via Resume.
134-
//
135-
// WARNING: Pause and Resume are intended for implementing continuation
136-
// passing style behavior for the transaction executor, with the assumption
137-
// that the states accessed prior to pausing remain valid after resumption.
138-
// The paused nested transaction should not be reused across transactions.
139-
// IT IS NOT SAFE TO PAUSE A NESTED TRANSACTION IN GENERAL SINCE THAT
140-
// COULD LEAD TO PHANTOM READS.
141-
PauseNestedTransaction(
142-
expectedId NestedTransactionId,
143-
) (
144-
*ExecutionState,
145-
error,
146-
)
147-
148-
// ResumeNestedTransaction attaches the paused nested transaction (state)
149-
// to the current transaction.
150-
ResumeNestedTransaction(pausedState *ExecutionState)
151-
152131
// AttachAndCommitNestedTransaction commits the changes from the cached
153132
// nested transaction execution snapshot to the current (nested)
154133
// transaction.
@@ -373,30 +352,6 @@ func (txnState *transactionState) CommitParseRestrictedNestedTransaction(
373352
return txnState.mergeIntoParent()
374353
}
375354

376-
func (txnState *transactionState) PauseNestedTransaction(
377-
expectedId NestedTransactionId,
378-
) (
379-
*ExecutionState,
380-
error,
381-
) {
382-
if !txnState.IsCurrent(expectedId) {
383-
return nil, fmt.Errorf(
384-
"cannot pause unexpected nested transaction: id mismatch",
385-
)
386-
}
387-
388-
if txnState.IsParseRestricted() {
389-
return nil, fmt.Errorf(
390-
"cannot Pause parse restricted nested transaction")
391-
}
392-
393-
return txnState.pop("pause")
394-
}
395-
396-
func (txnState *transactionState) ResumeNestedTransaction(pausedState *ExecutionState) {
397-
txnState.push(pausedState, nil)
398-
}
399-
400355
func (txnState *transactionState) AttachAndCommitNestedTransaction(
401356
cachedSnapshot *ExecutionSnapshot,
402357
) error {

fvm/state/transaction_state_test.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -480,50 +480,6 @@ func TestParseRestrictedCannotCommitLocationMismatch(t *testing.T) {
480480
require.True(t, txn.IsCurrent(id))
481481
}
482482

483-
func TestPauseAndResume(t *testing.T) {
484-
txn := newTestTransactionState()
485-
486-
key1 := flow.NewRegisterID("addr", "key")
487-
key2 := flow.NewRegisterID("addr2", "key2")
488-
489-
val, err := txn.Get(key1)
490-
require.NoError(t, err)
491-
require.Nil(t, val)
492-
493-
id1, err := txn.BeginNestedTransaction()
494-
require.NoError(t, err)
495-
496-
err = txn.Set(key1, createByteArray(2))
497-
require.NoError(t, err)
498-
499-
val, err = txn.Get(key1)
500-
require.NoError(t, err)
501-
require.NotNil(t, val)
502-
503-
pausedState, err := txn.PauseNestedTransaction(id1)
504-
require.NoError(t, err)
505-
506-
val, err = txn.Get(key1)
507-
require.NoError(t, err)
508-
require.Nil(t, val)
509-
510-
txn.ResumeNestedTransaction(pausedState)
511-
512-
val, err = txn.Get(key1)
513-
require.NoError(t, err)
514-
require.NotNil(t, val)
515-
516-
err = txn.Set(key2, createByteArray(2))
517-
require.NoError(t, err)
518-
519-
_, err = txn.CommitNestedTransaction(id1)
520-
require.NoError(t, err)
521-
522-
val, err = txn.Get(key2)
523-
require.NoError(t, err)
524-
require.NotNil(t, val)
525-
}
526-
527483
func TestFinalizeMainTransactionFailWithUnexpectedNestedTransactions(
528484
t *testing.T,
529485
) {

fvm/transactionInvoker.go

Lines changed: 55 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ type transactionExecutor struct {
6060

6161
errs *errors.ErrorsCollector
6262

63-
nestedTxnId state.NestedTransactionId
64-
pausedState *state.ExecutionState
63+
startedTransactionBodyExecution bool
64+
nestedTxnId state.NestedTransactionId
6565

6666
cadenceRuntime *reusableRuntime.ReusableCadenceRuntime
6767
txnBodyExecutor runtime.Executor
@@ -91,13 +91,14 @@ func newTransactionExecutor(
9191
TransactionVerifier: TransactionVerifier{
9292
VerificationConcurrency: 4,
9393
},
94-
ctx: ctx,
95-
proc: proc,
96-
txnState: txnState,
97-
span: span,
98-
env: env,
99-
errs: errors.NewErrorsCollector(),
100-
cadenceRuntime: env.BorrowCadenceRuntime(),
94+
ctx: ctx,
95+
proc: proc,
96+
txnState: txnState,
97+
span: span,
98+
env: env,
99+
errs: errors.NewErrorsCollector(),
100+
startedTransactionBodyExecution: false,
101+
cadenceRuntime: env.BorrowCadenceRuntime(),
101102
}
102103
}
103104

@@ -131,22 +132,53 @@ func (executor *transactionExecutor) handleError(
131132
}
132133

133134
func (executor *transactionExecutor) Preprocess() error {
135+
return executor.handleError(executor.preprocess(), "preprocess")
136+
}
137+
138+
func (executor *transactionExecutor) Execute() error {
139+
return executor.handleError(executor.execute(), "executing")
140+
}
141+
142+
func (executor *transactionExecutor) preprocess() error {
143+
if executor.AuthorizationChecksEnabled {
144+
err := executor.CheckAuthorization(
145+
executor.ctx.TracerSpan,
146+
executor.proc,
147+
executor.txnState,
148+
executor.AccountKeyWeightThreshold)
149+
if err != nil {
150+
executor.errs.Collect(err)
151+
return executor.errs.ErrorOrNil()
152+
}
153+
}
154+
155+
if executor.SequenceNumberCheckAndIncrementEnabled {
156+
err := executor.CheckAndIncrementSequenceNumber(
157+
executor.ctx.TracerSpan,
158+
executor.proc,
159+
executor.txnState)
160+
if err != nil {
161+
executor.errs.Collect(err)
162+
return executor.errs.ErrorOrNil()
163+
}
164+
}
165+
134166
if !executor.TransactionBodyExecutionEnabled {
135167
return nil
136168
}
137169

138-
err := executor.PreprocessTransactionBody()
139-
return executor.handleError(err, "preprocessing")
140-
}
170+
executor.errs.Collect(executor.preprocessTransactionBody())
171+
if executor.errs.CollectedFailure() {
172+
return executor.errs.ErrorOrNil()
173+
}
141174

142-
func (executor *transactionExecutor) Execute() error {
143-
return executor.handleError(executor.execute(), "executing")
175+
return nil
144176
}
145177

146-
// PreprocessTransactionBody preprocess parts of a transaction body that are
178+
// preprocessTransactionBody preprocess parts of a transaction body that are
147179
// infrequently modified and are expensive to compute. For now this includes
148180
// reading meter parameter overrides and parsing programs.
149-
func (executor *transactionExecutor) PreprocessTransactionBody() error {
181+
func (executor *transactionExecutor) preprocessTransactionBody() error {
150182
meterParams, err := getBodyMeterParameters(
151183
executor.ctx,
152184
executor.proc,
@@ -160,6 +192,7 @@ func (executor *transactionExecutor) PreprocessTransactionBody() error {
160192
if err != nil {
161193
return err
162194
}
195+
executor.startedTransactionBodyExecution = true
163196
executor.nestedTxnId = txnId
164197

165198
executor.txnBodyExecutor = executor.cadenceRuntime.NewTransactionExecutor(
@@ -173,93 +206,23 @@ func (executor *transactionExecutor) PreprocessTransactionBody() error {
173206
// by the transaction body.
174207
err = executor.txnBodyExecutor.Preprocess()
175208
if err != nil {
176-
executor.errs.Collect(
177-
fmt.Errorf(
178-
"transaction preprocess failed: %w",
179-
err))
180-
181-
// We shouldn't early exit on non-failure since we need to deduct fees.
182-
if executor.errs.CollectedFailure() {
183-
return executor.errs.ErrorOrNil()
184-
}
185-
186-
// NOTE: We need to restart the nested transaction in order to pause
187-
// for fees deduction.
188-
err = executor.txnState.RestartNestedTransaction(txnId)
189-
if err != nil {
190-
return err
191-
}
192-
}
193-
194-
// Pause the transaction body's nested transaction in order to interleave
195-
// auth and seq num checks.
196-
pausedState, err := executor.txnState.PauseNestedTransaction(txnId)
197-
if err != nil {
198-
return err
209+
return fmt.Errorf(
210+
"transaction preprocess failed: %w",
211+
err)
199212
}
200-
executor.pausedState = pausedState
201213

202214
return nil
203215
}
204216

205217
func (executor *transactionExecutor) execute() error {
206-
if executor.AuthorizationChecksEnabled {
207-
err := executor.CheckAuthorization(
208-
executor.ctx.TracerSpan,
209-
executor.proc,
210-
executor.txnState,
211-
executor.AccountKeyWeightThreshold)
212-
if err != nil {
213-
executor.errs.Collect(err)
214-
executor.errs.Collect(executor.abortPreprocessed())
215-
return executor.errs.ErrorOrNil()
216-
}
218+
if !executor.startedTransactionBodyExecution {
219+
return executor.errs.ErrorOrNil()
217220
}
218221

219-
if executor.SequenceNumberCheckAndIncrementEnabled {
220-
err := executor.CheckAndIncrementSequenceNumber(
221-
executor.ctx.TracerSpan,
222-
executor.proc,
223-
executor.txnState)
224-
if err != nil {
225-
executor.errs.Collect(err)
226-
executor.errs.Collect(executor.abortPreprocessed())
227-
return executor.errs.ErrorOrNil()
228-
}
229-
}
230-
231-
if executor.TransactionBodyExecutionEnabled {
232-
err := executor.ExecuteTransactionBody()
233-
if err != nil {
234-
return err
235-
}
236-
}
237-
238-
return nil
239-
}
240-
241-
func (executor *transactionExecutor) abortPreprocessed() error {
242-
if !executor.TransactionBodyExecutionEnabled {
243-
return nil
244-
}
245-
246-
executor.txnState.ResumeNestedTransaction(executor.pausedState)
247-
248-
// There shouldn't be any update, but drop all updates just in case.
249-
err := executor.txnState.RestartNestedTransaction(executor.nestedTxnId)
250-
if err != nil {
251-
return err
252-
}
253-
254-
// We need to commit the aborted state unconditionally to include
255-
// the touched registers in the execution receipt.
256-
_, err = executor.txnState.CommitNestedTransaction(executor.nestedTxnId)
257-
return err
222+
return executor.ExecuteTransactionBody()
258223
}
259224

260225
func (executor *transactionExecutor) ExecuteTransactionBody() error {
261-
executor.txnState.ResumeNestedTransaction(executor.pausedState)
262-
263226
var invalidator derived.TransactionInvalidator
264227
if !executor.errs.CollectedError() {
265228

0 commit comments

Comments
 (0)