From 56e0fd078a4efb47e54d9f8b2019360286d20c09 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Thu, 10 Sep 2020 17:49:02 +0100 Subject: [PATCH] test: actor abort (#118) --- gen/builders/builder_message.go | 25 +++++---- gen/builders/messages.go | 4 ++ gen/builders/predicates.go | 10 ++++ gen/builders/state_tracker.go | 10 +++- gen/suites/vm_violations/actor_abort.go | 53 +++++++++++++++++++ gen/suites/vm_violations/main.go | 69 +++++++++++++++++++++++++ go.mod | 4 +- go.sum | 2 + schema.json | 9 +++- 9 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 gen/suites/vm_violations/actor_abort.go diff --git a/gen/builders/builder_message.go b/gen/builders/builder_message.go index 7689b50d..766155d9 100644 --- a/gen/builders/builder_message.go +++ b/gen/builders/builder_message.go @@ -103,7 +103,7 @@ func (b *MessageVectorBuilder) CommitApplies() { for _, am := range b.Messages.All() { // apply all messages that are pending application. - if am.Result == nil { + if !am.Applied { b.StateTracker.ApplyMessage(am) } @@ -112,11 +112,17 @@ func (b *MessageVectorBuilder) CommitApplies() { Bytes: MustSerialize(am.Message), Epoch: &epoch, }) - b.vector.Post.Receipts = append(b.vector.Post.Receipts, &schema.Receipt{ - ExitCode: int64(am.Result.ExitCode), - ReturnValue: am.Result.Return, - GasUsed: am.Result.GasUsed, - }) + + // am.Result may still be nil if the message failed to be applied + if am.Result != nil { + b.vector.Post.Receipts = append(b.vector.Post.Receipts, &schema.Receipt{ + ExitCode: int64(am.Result.ExitCode), + ReturnValue: am.Result.Return, + GasUsed: am.Result.GasUsed, + }) + } else { + b.vector.Post.Receipts = append(b.vector.Post.Receipts, nil) + } } // update the internal state. @@ -124,7 +130,6 @@ func (b *MessageVectorBuilder) CommitApplies() { b.vector.Post.StateTree = &schema.StateTree{RootCID: b.PostRoot} b.Stage = StageChecks b.Assert.enterStage(StageChecks) - } // Finish signals to the builder that the checks stage is complete and that the @@ -146,8 +151,10 @@ func (b *MessageVectorBuilder) Finish(w io.Writer) { msgs := b.Messages.All() traces := make([]types.ExecutionTrace, 0, len(msgs)) - for _, msgs := range msgs { - traces = append(traces, msgs.Result.ExecutionTrace) + for _, msg := range msgs { + if msg.Result != nil { + traces = append(traces, msg.Result.ExecutionTrace) + } } b.vector.Diagnostics = EncodeTraces(traces) diff --git a/gen/builders/messages.go b/gen/builders/messages.go index 4e6cb502..8c13d94d 100644 --- a/gen/builders/messages.go +++ b/gen/builders/messages.go @@ -38,6 +38,10 @@ type ApplicableMessage struct { Epoch abi.ChainEpoch Message *types.Message Result *vm.ApplyRet + // Applied is true if this message has already been applied. Note it's + // not safe to rely on non-nil Result as indication of application + // since applied messages may fail without a result. + Applied bool } func (m *Messages) Sugar() *sugarMsg { diff --git a/gen/builders/predicates.go b/gen/builders/predicates.go index 506465a5..ff56606c 100644 --- a/gen/builders/predicates.go +++ b/gen/builders/predicates.go @@ -48,6 +48,16 @@ func MessageReturns(expect cbg.CBORMarshaler) ApplyRetPredicate { } } +// Nil returns an ApplyRetPredicate that passes if the message receipt is nil. +func Nil() ApplyRetPredicate { + return func(ret *vm.ApplyRet) error { + if ret != nil { + return fmt.Errorf("message receipt was not nil: %+v", ret) + } + return nil + } +} + // BalanceUpdated returns a ActorPredicate that checks whether the balance // of the actor has been deducted the gas cost and the outgoing value transfers, // and has been increased by the offset (or decreased, if the argument is negative). diff --git a/gen/builders/state_tracker.go b/gen/builders/state_tracker.go index 971576e9..e0af6da3 100644 --- a/gen/builders/state_tracker.go +++ b/gen/builders/state_tracker.go @@ -86,10 +86,16 @@ func (st *StateTracker) Flush() cid.Cid { // root, refreshes the state tree, and updates the underlying vector with the // message and its receipt. func (st *StateTracker) ApplyMessage(am *ApplicableMessage) { + var postRoot cid.Cid var err error - am.Result, st.CurrRoot, err = st.Driver.ExecuteMessage(st.Stores.Blockstore, st.CurrRoot, am.Epoch, am.Message) - st.bc.Assert.NoError(err) + am.Applied = true + am.Result, postRoot, err = st.Driver.ExecuteMessage(st.Stores.Blockstore, st.CurrRoot, am.Epoch, am.Message) + if err != nil { + return + } + + st.CurrRoot = postRoot // replace the state tree. st.StateTree, err = state.LoadStateTree(st.Stores.CBORStore, st.CurrRoot) st.bc.Assert.NoError(err) diff --git a/gen/suites/vm_violations/actor_abort.go b/gen/suites/vm_violations/actor_abort.go new file mode 100644 index 00000000..573049c1 --- /dev/null +++ b/gen/suites/vm_violations/actor_abort.go @@ -0,0 +1,53 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/lotus/conformance/chaos" + + . "github.com/filecoin-project/test-vectors/gen/builders" +) + +func actorAbort(abortCode exitcode.ExitCode, msg string, expectedCode exitcode.ExitCode) func(*MessageVectorBuilder) { + return func(v *MessageVectorBuilder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200)) + + sender := v.Actors.Account(address.SECP256K1, abi.NewTokenAmount(1_000_000_000_000)) + v.CommitPreconditions() + + v.Messages.Raw( + sender.ID, + chaos.Address, + chaos.MethodAbort, + MustSerialize(&chaos.AbortArgs{Code: abortCode, Message: msg}), + Value(big.Zero()), + Nonce(0), + ) + v.CommitApplies() + + v.Assert.LastMessageResultSatisfies(ExitCode(expectedCode)) + } +} + +func actorPanic(msg string) func(*MessageVectorBuilder) { + return func(v *MessageVectorBuilder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200)) + + sender := v.Actors.Account(address.SECP256K1, abi.NewTokenAmount(1_000_000_000_000)) + v.CommitPreconditions() + + v.Messages.Raw( + sender.ID, + chaos.Address, + chaos.MethodAbort, + MustSerialize(&chaos.AbortArgs{NoCode: true, Message: msg}), + Value(big.Zero()), + Nonce(0), + ) + v.CommitApplies() + + v.Assert.LastMessageResultSatisfies(Nil()) + } +} diff --git a/gen/suites/vm_violations/main.go b/gen/suites/vm_violations/main.go index 192a2e51..e4d7e39b 100644 --- a/gen/suites/vm_violations/main.go +++ b/gen/suites/vm_violations/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/specs-actors/actors/builtin" @@ -228,4 +229,72 @@ func main() { MessageFunc: mutateState(valPfx+"after-transaction", chaos.MutateAfterTransaction, exitcode.SysErrorIllegalActor), }, ) + + actorAbortVectors := []*VectorDef{ + { + Metadata: &Metadata{ + ID: "custom-exit-code", + Version: "v1", + Desc: "actors can abort with custom exit codes", + }, + Selector: map[string]string{"chaos_actor": "true"}, + MessageFunc: actorAbort(exitcode.FirstActorSpecificExitCode, "custom exit code abort", exitcode.FirstActorSpecificExitCode), + }, + { + Metadata: &Metadata{ + ID: "negative-exit-code", + Version: "v1", + Desc: "actors should not abort with negative exit codes", + }, + Selector: map[string]string{"chaos_actor": "true"}, + Mode: ModeLenientAssertions, + Hints: []string{schema.HintIncorrect, schema.HintNegate}, + MessageFunc: actorAbort(-1, "negative exit code abort", exitcode.SysErrorIllegalActor), + }, + { + Metadata: &Metadata{ + ID: "no-exit-code", + Version: "v1", + Desc: "actor failure, a panic with no associated exit code", + }, + Selector: map[string]string{"chaos_actor": "true"}, + Mode: ModeLenientAssertions, + Hints: []string{schema.HintIncorrect, schema.HintNegate}, + MessageFunc: actorPanic("no exit code abort"), + }, + } + + sysExitCodes := []exitcode.ExitCode{ + exitcode.SysErrSenderInvalid, + exitcode.SysErrSenderStateInvalid, + exitcode.SysErrInvalidMethod, + exitcode.SysErrReserved1, + exitcode.SysErrInvalidReceiver, + exitcode.SysErrInsufficientFunds, + exitcode.SysErrOutOfGas, + exitcode.SysErrForbidden, + exitcode.SysErrorIllegalActor, + exitcode.SysErrorIllegalArgument, + exitcode.SysErrReserved2, + exitcode.SysErrReserved3, + exitcode.SysErrReserved4, + exitcode.SysErrReserved5, + exitcode.SysErrReserved6, + } + + for _, xc := range sysExitCodes { + actorAbortVectors = append(actorAbortVectors, &VectorDef{ + Metadata: &Metadata{ + ID: fmt.Sprintf("system-exit-code-%d", xc), + Version: "v1", + Desc: fmt.Sprintf("actors should not abort with %s", xc), + }, + Selector: map[string]string{"chaos_actor": "true"}, + Mode: ModeLenientAssertions, + Hints: []string{schema.HintIncorrect, schema.HintNegate}, + MessageFunc: actorAbort(xc, fmt.Sprintf("%s abort", xc), exitcode.SysErrorIllegalActor), + }) + } + + g.Group("actor_abort", actorAbortVectors...) } diff --git a/go.mod b/go.mod index 4bc6e52a..2b8a3572 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,9 @@ require ( github.com/filecoin-project/go-bitfield v0.2.0 github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 github.com/filecoin-project/go-state-types v0.0.0-20200905071437-95828685f9df - github.com/filecoin-project/lotus v0.6.2-0.20200908205025-63cdbef2197c + github.com/filecoin-project/lotus v0.6.2-0.20200909104030-159be5b543fd github.com/filecoin-project/specs-actors v0.9.7 - github.com/filecoin-project/test-vectors/schema v0.0.0-00010101000000-000000000000 + github.com/filecoin-project/test-vectors/schema v0.0.1 github.com/ipfs/go-block-format v0.0.2 github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834 github.com/ipfs/go-cid v0.0.7 diff --git a/go.sum b/go.sum index 0c6a728b..623705e9 100644 --- a/go.sum +++ b/go.sum @@ -261,6 +261,8 @@ github.com/filecoin-project/lotus v0.4.3-0.20200819133134-a21234cd54d5/go.mod h1 github.com/filecoin-project/lotus v0.4.3-0.20200820203717-d1718369a182/go.mod h1:biFZPQ/YyQGfkHUmHMiaNf2hnD6zm1+OAXPQYQ61Zkg= github.com/filecoin-project/lotus v0.6.2-0.20200908205025-63cdbef2197c h1:S5wcQLt8tFbh5q/ajPCawRgFPQm3CJ9LAy9/dwlNxtk= github.com/filecoin-project/lotus v0.6.2-0.20200908205025-63cdbef2197c/go.mod h1:zK3h9oTAN5QSfcKq8JYmGPq7oIYGfwMQ9UKD5/yp5GU= +github.com/filecoin-project/lotus v0.6.2-0.20200909104030-159be5b543fd h1:6Reoqcam6sUmoeyUauwTHgnhYlzf7QAhXciIE1p//3s= +github.com/filecoin-project/lotus v0.6.2-0.20200909104030-159be5b543fd/go.mod h1:8SUeiDq172ZpVm4bLYfaulKLsYlYkl9ZI/IDxacP8M8= github.com/filecoin-project/sector-storage v0.0.0-20200712023225-1d67dcfa3c15/go.mod h1:salgVdX7qeXFo/xaiEQE29J4pPkjn71T0kt0n+VDBzo= github.com/filecoin-project/sector-storage v0.0.0-20200730050024-3ee28c3b6d9a/go.mod h1:oOawOl9Yk+qeytLzzIryjI8iRbqo+qzS6EEeElP4PWA= github.com/filecoin-project/sector-storage v0.0.0-20200810171746-eac70842d8e0/go.mod h1:oOawOl9Yk+qeytLzzIryjI8iRbqo+qzS6EEeElP4PWA= diff --git a/schema.json b/schema.json index 07ef5753..cdccac2b 100644 --- a/schema.json +++ b/schema.json @@ -148,7 +148,14 @@ "type": "array", "additionalItems": false, "items": { - "$ref": "#/definitions/receipt" + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/definitions/receipt" + } + ] } }, "receipts_roots": {