diff --git a/access/validator.go b/access/validator.go index f0dec8071d0..00d902d0c10 100644 --- a/access/validator.go +++ b/access/validator.go @@ -320,11 +320,20 @@ func (v *TransactionValidator) checkExpiry(tx *flow.TransactionBody) error { return nil } -func (v *TransactionValidator) checkCanBeParsed(tx *flow.TransactionBody) error { +func (v *TransactionValidator) checkCanBeParsed(tx *flow.TransactionBody) (err error) { + defer func() { + if r := recover(); r != nil { + if panicErr, ok := r.(error); ok { + err = InvalidScriptError{ParserErr: panicErr} + } else { + err = InvalidScriptError{ParserErr: fmt.Errorf("non-error-typed panic: %v", r)} + } + } + }() if v.options.CheckScriptsParse { - _, err := parser.ParseProgram(nil, tx.Script, parser.Config{}) - if err != nil { - return InvalidScriptError{ParserErr: err} + _, parseErr := parser.ParseProgram(nil, tx.Script, parser.Config{}) + if parseErr != nil { + return InvalidScriptError{ParserErr: parseErr} } } diff --git a/engine/collection/ingest/engine_test.go b/engine/collection/ingest/engine_test.go index 97eab113c97..f96cebc7908 100644 --- a/engine/collection/ingest/engine_test.go +++ b/engine/collection/ingest/engine_test.go @@ -172,6 +172,25 @@ func (suite *Suite) TestInvalidTransaction() { suite.Assert().True(errors.As(err, &access.InvalidScriptError{})) }) + // In some cases the Cadence parser will panic rather than return an error. + // If this happens, we should recover from the panic and return an InvalidScriptError. + // See: https://github.com/onflow/cadence/issues/3428, https://github.com/dapperlabs/flow-go/issues/6964 + suite.Run("transaction script exceeds parse token limit (Cadence parser panic should be caught)", func() { + const tokenLimit = 1 << 19 + script := "{};" + for len(script) < tokenLimit { + script += script + } + + tx := unittest.TransactionBodyFixture() + tx.ReferenceBlockID = suite.root.ID() + tx.Script = []byte("transaction { execute {" + script + "}}") + + err := suite.engine.ProcessTransaction(&tx) + suite.Assert().Error(err) + suite.Assert().True(errors.As(err, &access.InvalidScriptError{})) + }) + suite.Run("invalid signature format", func() { signer := flow.Testnet.Chain().ServiceAddress() keyIndex := uint32(0)