Skip to content

Commit f6bcbd2

Browse files
committed
Simplify transaction preprocessing
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.
1 parent 5c83d67 commit f6bcbd2

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
@@ -68,8 +68,8 @@ type transactionExecutor struct {
6868

6969
errs *errors.ErrorsCollector
7070

71-
nestedTxnId state.NestedTransactionId
72-
pausedState *state.ExecutionState
71+
startedTransactionBodyExecution bool
72+
nestedTxnId state.NestedTransactionId
7373

7474
cadenceRuntime *reusableRuntime.ReusableCadenceRuntime
7575
txnBodyExecutor runtime.Executor
@@ -99,13 +99,14 @@ func newTransactionExecutor(
9999
TransactionVerifier: TransactionVerifier{
100100
VerificationConcurrency: 4,
101101
},
102-
ctx: ctx,
103-
proc: proc,
104-
txnState: txnState,
105-
span: span,
106-
env: env,
107-
errs: errors.NewErrorsCollector(),
108-
cadenceRuntime: env.BorrowCadenceRuntime(),
102+
ctx: ctx,
103+
proc: proc,
104+
txnState: txnState,
105+
span: span,
106+
env: env,
107+
errs: errors.NewErrorsCollector(),
108+
startedTransactionBodyExecution: false,
109+
cadenceRuntime: env.BorrowCadenceRuntime(),
109110
}
110111
}
111112

@@ -139,22 +140,53 @@ func (executor *transactionExecutor) handleError(
139140
}
140141

141142
func (executor *transactionExecutor) Preprocess() error {
143+
return executor.handleError(executor.preprocess(), "preprocess")
144+
}
145+
146+
func (executor *transactionExecutor) Execute() error {
147+
return executor.handleError(executor.execute(), "executing")
148+
}
149+
150+
func (executor *transactionExecutor) preprocess() error {
151+
if executor.AuthorizationChecksEnabled {
152+
err := executor.CheckAuthorization(
153+
executor.ctx.TracerSpan,
154+
executor.proc,
155+
executor.txnState,
156+
executor.AccountKeyWeightThreshold)
157+
if err != nil {
158+
executor.errs.Collect(err)
159+
return executor.errs.ErrorOrNil()
160+
}
161+
}
162+
163+
if executor.SequenceNumberCheckAndIncrementEnabled {
164+
err := executor.CheckAndIncrementSequenceNumber(
165+
executor.ctx.TracerSpan,
166+
executor.proc,
167+
executor.txnState)
168+
if err != nil {
169+
executor.errs.Collect(err)
170+
return executor.errs.ErrorOrNil()
171+
}
172+
}
173+
142174
if !executor.TransactionBodyExecutionEnabled {
143175
return nil
144176
}
145177

146-
err := executor.PreprocessTransactionBody()
147-
return executor.handleError(err, "preprocessing")
148-
}
178+
executor.errs.Collect(executor.preprocessTransactionBody())
179+
if executor.errs.CollectedFailure() {
180+
return executor.errs.ErrorOrNil()
181+
}
149182

150-
func (executor *transactionExecutor) Execute() error {
151-
return executor.handleError(executor.execute(), "executing")
183+
return nil
152184
}
153185

154-
// PreprocessTransactionBody preprocess parts of a transaction body that are
186+
// preprocessTransactionBody preprocess parts of a transaction body that are
155187
// infrequently modified and are expensive to compute. For now this includes
156188
// reading meter parameter overrides and parsing programs.
157-
func (executor *transactionExecutor) PreprocessTransactionBody() error {
189+
func (executor *transactionExecutor) preprocessTransactionBody() error {
158190
meterParams, err := getBodyMeterParameters(
159191
executor.ctx,
160192
executor.proc,
@@ -168,6 +200,7 @@ func (executor *transactionExecutor) PreprocessTransactionBody() error {
168200
if err != nil {
169201
return err
170202
}
203+
executor.startedTransactionBodyExecution = true
171204
executor.nestedTxnId = txnId
172205

173206
executor.txnBodyExecutor = executor.cadenceRuntime.NewTransactionExecutor(
@@ -181,93 +214,23 @@ func (executor *transactionExecutor) PreprocessTransactionBody() error {
181214
// by the transaction body.
182215
err = executor.txnBodyExecutor.Preprocess()
183216
if err != nil {
184-
executor.errs.Collect(
185-
fmt.Errorf(
186-
"transaction preprocess failed: %w",
187-
err))
188-
189-
// We shouldn't early exit on non-failure since we need to deduct fees.
190-
if executor.errs.CollectedFailure() {
191-
return executor.errs.ErrorOrNil()
192-
}
193-
194-
// NOTE: We need to restart the nested transaction in order to pause
195-
// for fees deduction.
196-
err = executor.txnState.RestartNestedTransaction(txnId)
197-
if err != nil {
198-
return err
199-
}
200-
}
201-
202-
// Pause the transaction body's nested transaction in order to interleave
203-
// auth and seq num checks.
204-
pausedState, err := executor.txnState.PauseNestedTransaction(txnId)
205-
if err != nil {
206-
return err
217+
return fmt.Errorf(
218+
"transaction preprocess failed: %w",
219+
err)
207220
}
208-
executor.pausedState = pausedState
209221

210222
return nil
211223
}
212224

213225
func (executor *transactionExecutor) execute() error {
214-
if executor.AuthorizationChecksEnabled {
215-
err := executor.CheckAuthorization(
216-
executor.ctx.TracerSpan,
217-
executor.proc,
218-
executor.txnState,
219-
executor.AccountKeyWeightThreshold)
220-
if err != nil {
221-
executor.errs.Collect(err)
222-
executor.errs.Collect(executor.abortPreprocessed())
223-
return executor.errs.ErrorOrNil()
224-
}
226+
if !executor.startedTransactionBodyExecution {
227+
return executor.errs.ErrorOrNil()
225228
}
226229

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

268233
func (executor *transactionExecutor) ExecuteTransactionBody() error {
269-
executor.txnState.ResumeNestedTransaction(executor.pausedState)
270-
271234
var invalidator derived.TransactionInvalidator
272235
if !executor.errs.CollectedError() {
273236

0 commit comments

Comments
 (0)