diff --git a/tvx/builders/builder.go b/tvx/builders/builder.go index 0991de597..c9c68d9c3 100644 --- a/tvx/builders/builder.go +++ b/tvx/builders/builder.go @@ -5,10 +5,9 @@ import ( "compress/gzip" "context" "encoding/json" - "fmt" "io" - "io/ioutil" "log" + "os" "github.com/filecoin-project/lotus/chain/state" "github.com/ipfs/go-cid" @@ -31,8 +30,8 @@ const ( func init() { // disable logs, as we need a clean stdout output. - log.SetOutput(ioutil.Discard) - log.SetFlags(0) + log.SetOutput(os.Stderr) + log.SetPrefix(">>> ") } // TODO use stage.Surgeon with non-proxying blockstore. @@ -131,7 +130,6 @@ func (b *Builder) applyMessage(am *ApplicableMessage) { Bytes: MustSerialize(am.Message), Epoch: &am.Epoch, }) - fmt.Println(am.Result.ExitCode) b.vector.Post.Receipts = append(b.vector.Post.Receipts, &schema.Receipt{ ExitCode: am.Result.ExitCode, ReturnValue: am.Result.Return, diff --git a/tvx/builders/generator.go b/tvx/builders/generator.go new file mode 100644 index 000000000..a3e91e33c --- /dev/null +++ b/tvx/builders/generator.go @@ -0,0 +1,175 @@ +package builders + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "log" + "os" + "path/filepath" + "regexp" + "sync" + + "github.com/filecoin-project/oni/tvx/schema" +) + +// Generator is a batch generator and organizer of test vectors. +// +// Test vector scripts are simple programs (main function). Test vector scripts +// can delegate to the Generator to handle the execution, reporting and capture +// of emitted test vectors into files. +// +// Generator supports the following CLI flags: +// +// -o +// directory where test vector JSON files will be saved; if omitted, +// vectors will be written to stdout. +// +// -f +// regex filter to select a subset of vectors to execute; matched against +// the vector's ID. +// +// Scripts can bundle test vectors into "groups". The generator will execute +// each group in parallel, and will write each vector in a file: +// /--.json +type Generator struct { + OutputPath string + Filter *regexp.Regexp + + wg sync.WaitGroup +} + +type MessageVectorGenItem struct { + Metadata *schema.Metadata + Func func(*Builder) +} + +func NewGenerator() *Generator { + // Consume CLI parameters. + var ( + outputDir = flag.String("o", "", "directory where test vector JSON files will be saved; if omitted, vectors will be written to stdout") + filter = flag.String("f", "", "regex filter to select a subset of vectors to execute; matched against the vector's ID") + ) + + flag.Parse() + + ret := new(Generator) + + // If output directory is provided, we ensure it exists, or create it. + // Else, we'll output to stdout. + if dir := *outputDir; dir != "" { + err := ensureDirectory(dir) + if err != nil { + log.Fatal(err) + } + ret.OutputPath = dir + } + + // If a filter has been provided, compile it into a regex. + if *filter != "" { + exp, err := regexp.Compile(*filter) + if err != nil { + log.Fatalf("supplied regex %s is invalid: %s", *filter, err) + } + ret.Filter = exp + } + + return ret +} + +func (g *Generator) Wait() { + g.wg.Wait() +} + +func (g *Generator) MessageVectorGroup(group string, vectors ...*MessageVectorGenItem) { + g.wg.Add(1) + go func() { + defer g.wg.Done() + + var wg sync.WaitGroup + for _, item := range vectors { + if id := item.Metadata.ID; g.Filter != nil && !g.Filter.MatchString(id) { + log.Printf("skipping %s", id) + continue + } + + var w io.Writer + if g.OutputPath == "" { + w = os.Stdout + } else { + file := filepath.Join(g.OutputPath, fmt.Sprintf("%s--%s.json", group, item.Metadata.ID)) + out, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + log.Printf("failed to write to file %s: %s", file, err) + return + } + w = out + } + + wg.Add(1) + go func(item *MessageVectorGenItem) { + g.generateOne(w, item, w != os.Stdout) + wg.Done() + }(item) + } + + wg.Wait() + }() +} + +func (g *Generator) generateOne(w io.Writer, b *MessageVectorGenItem, indent bool) { + log.Printf("generating test vector: %s", b.Metadata.ID) + + vector := MessageVector(b.Metadata) + + // TODO: currently if an assertion fails, we call os.Exit(1), which + // aborts all ongoing vector generations. The Asserter should + // call runtime.Goexit() instead so only that goroutine is + // cancelled. The assertion error must bubble up somehow. + b.Func(vector) + + buf := new(bytes.Buffer) + vector.Finish(buf) + + final := buf + if indent { + // reparse and reindent. + final = new(bytes.Buffer) + if err := json.Indent(final, buf.Bytes(), "", "\t"); err != nil { + log.Printf("failed to indent json: %s", err) + } + } + + n, err := w.Write(final.Bytes()) + if err != nil { + log.Printf("failed to write to output: %s", err) + return + } + + log.Printf("generated test vector: %s (size: %d bytes)", b.Metadata.ID, n) +} + +// ensureDirectory checks if the provided path is a directory. If yes, it +// returns nil. If the path doesn't exist, it creates the directory and +// returns nil. If the path is not a directory, or another error occurs, an +// error is returned. +func ensureDirectory(path string) error { + switch stat, err := os.Stat(path); { + case os.IsNotExist(err): + // create directory. + log.Printf("creating directory %s", path) + err := os.MkdirAll(path, 0700) + if err != nil { + return fmt.Errorf("failed to create directory %s: %s", path, err) + } + + case err == nil && !stat.IsDir(): + return fmt.Errorf("path %s exists, but it's not a directory", path) + + case err != nil: + return fmt.Errorf("failed to stat directory %s: %w", path, err) + } + return nil +} diff --git a/tvx/scripts/msg_application.go b/tvx/scripts/msg_application.go deleted file mode 100644 index b1f05aa29..000000000 --- a/tvx/scripts/msg_application.go +++ /dev/null @@ -1,247 +0,0 @@ -package main - -import ( - "os" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/specs-actors/actors/abi" - "github.com/filecoin-project/specs-actors/actors/abi/big" - "github.com/filecoin-project/specs-actors/actors/builtin/paych" - "github.com/filecoin-project/specs-actors/actors/crypto" - "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" - - . "github.com/filecoin-project/oni/tvx/builders" - "github.com/filecoin-project/oni/tvx/schema" -) - -var ( - unknown = MustNewIDAddr(10000000) - balance1T = abi.NewTokenAmount(1_000_000_000_000) - transferAmnt = abi.NewTokenAmount(10) -) - -func main() { - failCoverReceiptGasCost() - failCoverOnChainSizeGasCost() - failUnknownSender() - failInvalidActorNonce() - failInvalidReceiverMethod() - failInexistentReceiver() - failCoverTransferAccountCreationGasStepwise() - failActorExecutionAborted() -} - -func failCoverReceiptGasCost() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-receipt-gas", - Version: "v1", - Desc: "fail to cover gas cost for message receipt on chain", - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - alice := v.Actors.Account(address.SECP256K1, balance1T) - v.CommitPreconditions() - - v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(0), GasLimit(8)) - v.CommitApplies() - - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas)) - v.Finish(os.Stdout) -} - -func failCoverOnChainSizeGasCost() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-onchainsize-gas", - Version: "v1", - Desc: "not enough gas to pay message on-chain-size cost", - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(10)) - - alice := v.Actors.Account(address.SECP256K1, balance1T) - v.CommitPreconditions() - - v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(0), GasLimit(1)) - v.CommitApplies() - - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas)) - v.Finish(os.Stdout) -} - -func failUnknownSender() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-unknown-sender", - Version: "v1", - Desc: "fail due to lack of gas when sender is unknown", - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - alice := v.Actors.Account(address.SECP256K1, balance1T) - v.CommitPreconditions() - - v.Messages.Sugar().Transfer(unknown, alice.ID, Value(transferAmnt), Nonce(0)) - v.CommitApplies() - - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrSenderInvalid)) - v.Finish(os.Stdout) -} - -func failInvalidActorNonce() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-invalid-nonce", - Version: "v1", - Desc: "invalid actor nonce", - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - alice := v.Actors.Account(address.SECP256K1, balance1T) - v.CommitPreconditions() - - // invalid nonce from known account. - msg1 := v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(1)) - - // invalid nonce from an unknown account. - msg2 := v.Messages.Sugar().Transfer(unknown, alice.ID, Value(transferAmnt), Nonce(1)) - v.CommitApplies() - - v.Assert.Equal(msg1.Result.ExitCode, exitcode.SysErrSenderStateInvalid) - v.Assert.Equal(msg2.Result.ExitCode, exitcode.SysErrSenderInvalid) - - v.Finish(os.Stdout) -} - -func failInvalidReceiverMethod() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-invalid-receiver-method", - Version: "v1", - Desc: "invalid receiver method", - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - alice := v.Actors.Account(address.SECP256K1, balance1T) - v.CommitPreconditions() - - v.Messages.Typed(alice.ID, alice.ID, MarketComputeDataCommitment(nil), Nonce(0), Value(big.Zero())) - v.CommitApplies() - - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrInvalidMethod)) - - v.Finish(os.Stdout) -} - -func failInexistentReceiver() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-inexistent-receiver", - Version: "v1", - Desc: "inexistent receiver", - Comment: `Note that this test is not a valid message, since it is using -an unknown actor. However in the event that an invalid message isn't filtered by -block validation we need to ensure behaviour is consistent across VM implementations.`, - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - alice := v.Actors.Account(address.SECP256K1, balance1T) - v.CommitPreconditions() - - // Sending a message to non-existent ID address must produce an error. - unknownID := MustNewIDAddr(10000000) - v.Messages.Sugar().Transfer(alice.ID, unknownID, Value(transferAmnt), Nonce(0)) - - unknownActor := MustNewActorAddr("1234") - v.Messages.Sugar().Transfer(alice.ID, unknownActor, Value(transferAmnt), Nonce(1)) - v.CommitApplies() - - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrInvalidReceiver)) - v.Finish(os.Stdout) -} - -func failCoverTransferAccountCreationGasStepwise() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-transfer-accountcreation-gas", - Version: "v1", - Desc: "fail not enough gas to cover account actor creation on transfer", - } - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - var alice, bob, charlie AddressHandle - alice = v.Actors.Account(address.SECP256K1, balance1T) - bob.Robust, charlie.Robust = MustNewSECP256K1Addr("1"), MustNewSECP256K1Addr("2") - v.CommitPreconditions() - - var nonce uint64 - ref := v.Messages.Sugar().Transfer(alice.Robust, bob.Robust, Value(transferAmnt), Nonce(nonce)) - nonce++ - v.Messages.ApplyOne(ref) - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok)) - - // decrease the gas cost by `gasStep` for each apply and ensure `SysErrOutOfGas` is always returned. - trueGas := ref.Result.GasUsed - gasStep := trueGas / 100 - for tryGas := trueGas - gasStep; tryGas > 0; tryGas -= gasStep { - v.Messages.Sugar().Transfer(alice.Robust, charlie.Robust, Value(transferAmnt), Nonce(nonce), GasPrice(1), GasLimit(tryGas)) - nonce++ - } - v.CommitApplies() - - v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas), ref) - v.Finish(os.Stdout) -} - -func failActorExecutionAborted() { - metadata := &schema.Metadata{ - ID: "msg-apply-fail-actor-execution-illegal-arg", - Version: "v1", - Desc: "abort during actor execution due to illegal argument", - } - - // Set up sender and receiver accounts. - var sender, receiver AddressHandle - var paychAddr AddressHandle - - v := MessageVector(metadata) - v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - - v.Actors.AccountN(address.SECP256K1, balance1T, &sender, &receiver) - paychAddr = AddressHandle{ - ID: MustNewIDAddr(MustIDFromAddress(receiver.ID) + 1), - Robust: sender.NextActorAddress(0, 0), - } - v.CommitPreconditions() - - // Construct the payment channel. - createMsg := v.Messages.Sugar().CreatePaychActor(sender.Robust, receiver.Robust, Value(abi.NewTokenAmount(10_000))) - - // Update the payment channel. - updateMsg := v.Messages.Typed(sender.Robust, paychAddr.Robust, PaychUpdateChannelState(&paych.UpdateChannelStateParams{ - Sv: paych.SignedVoucher{ - ChannelAddr: paychAddr.Robust, - TimeLockMin: abi.ChainEpoch(10), - Lane: 123, - Nonce: 1, - Amount: big.NewInt(10), - Signature: &crypto.Signature{ - Type: crypto.SigTypeBLS, - Data: []byte("Grrr im an invalid signature, I cause panics in the payment channel actor"), - }, - }}), Nonce(1), Value(big.Zero())) - - v.CommitApplies() - - v.Assert.Equal(exitcode.Ok, createMsg.Result.ExitCode) - v.Assert.Equal(exitcode.ErrIllegalArgument, updateMsg.Result.ExitCode) - - v.Finish(os.Stdout) -} diff --git a/tvx/scripts/msg_application/actor_exec.go b/tvx/scripts/msg_application/actor_exec.go new file mode 100644 index 000000000..7bfa59fe2 --- /dev/null +++ b/tvx/scripts/msg_application/actor_exec.go @@ -0,0 +1,49 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/abi/big" + "github.com/filecoin-project/specs-actors/actors/builtin/paych" + "github.com/filecoin-project/specs-actors/actors/crypto" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +func failActorExecutionAborted(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + // Set up sender and receiver accounts. + var sender, receiver AddressHandle + var paychAddr AddressHandle + + v.Actors.AccountN(address.SECP256K1, balance1T, &sender, &receiver) + paychAddr = AddressHandle{ + ID: MustNewIDAddr(MustIDFromAddress(receiver.ID) + 1), + Robust: sender.NextActorAddress(0, 0), + } + v.CommitPreconditions() + + // Construct the payment channel. + createMsg := v.Messages.Sugar().CreatePaychActor(sender.Robust, receiver.Robust, Value(abi.NewTokenAmount(10_000))) + + // Update the payment channel. + updateMsg := v.Messages.Typed(sender.Robust, paychAddr.Robust, PaychUpdateChannelState(&paych.UpdateChannelStateParams{ + Sv: paych.SignedVoucher{ + ChannelAddr: paychAddr.Robust, + TimeLockMin: abi.ChainEpoch(10), + Lane: 123, + Nonce: 1, + Amount: big.NewInt(10), + Signature: &crypto.Signature{ + Type: crypto.SigTypeBLS, + Data: []byte("Grrr im an invalid signature, I cause panics in the payment channel actor"), + }, + }}), Nonce(1), Value(big.Zero())) + + v.CommitApplies() + + v.Assert.Equal(exitcode.Ok, createMsg.Result.ExitCode) + v.Assert.Equal(exitcode.ErrIllegalArgument, updateMsg.Result.ExitCode) +} diff --git a/tvx/scripts/msg_application/gas_cost.go b/tvx/scripts/msg_application/gas_cost.go new file mode 100644 index 000000000..3cddf35d5 --- /dev/null +++ b/tvx/scripts/msg_application/gas_cost.go @@ -0,0 +1,58 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +func failCoverReceiptGasCost(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + alice := v.Actors.Account(address.SECP256K1, balance1T) + v.CommitPreconditions() + + v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(0), GasLimit(8)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas)) +} + +func failCoverOnChainSizeGasCost(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(10)) + + alice := v.Actors.Account(address.SECP256K1, balance1T) + v.CommitPreconditions() + + v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(0), GasLimit(1)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas)) +} + +func failCoverTransferAccountCreationGasStepwise(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + var alice, bob, charlie AddressHandle + alice = v.Actors.Account(address.SECP256K1, balance1T) + bob.Robust, charlie.Robust = MustNewSECP256K1Addr("1"), MustNewSECP256K1Addr("2") + v.CommitPreconditions() + + var nonce uint64 + ref := v.Messages.Sugar().Transfer(alice.Robust, bob.Robust, Value(transferAmnt), Nonce(nonce)) + nonce++ + v.Messages.ApplyOne(ref) + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok)) + + // decrease the gas cost by `gasStep` for each apply and ensure `SysErrOutOfGas` is always returned. + trueGas := ref.Result.GasUsed + gasStep := trueGas / 100 + for tryGas := trueGas - gasStep; tryGas > 0; tryGas -= gasStep { + v.Messages.Sugar().Transfer(alice.Robust, charlie.Robust, Value(transferAmnt), Nonce(nonce), GasPrice(1), GasLimit(tryGas)) + nonce++ + } + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas), ref) +} diff --git a/tvx/scripts/msg_application/invalid_msgs.go b/tvx/scripts/msg_application/invalid_msgs.go new file mode 100644 index 000000000..544b41f72 --- /dev/null +++ b/tvx/scripts/msg_application/invalid_msgs.go @@ -0,0 +1,38 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi/big" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +func failInvalidActorNonce(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + alice := v.Actors.Account(address.SECP256K1, balance1T) + v.CommitPreconditions() + + // invalid nonce from known account. + msg1 := v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(1)) + + // invalid nonce from an unknown account. + msg2 := v.Messages.Sugar().Transfer(unknown, alice.ID, Value(transferAmnt), Nonce(1)) + v.CommitApplies() + + v.Assert.Equal(msg1.Result.ExitCode, exitcode.SysErrSenderStateInvalid) + v.Assert.Equal(msg2.Result.ExitCode, exitcode.SysErrSenderInvalid) +} + +func failInvalidReceiverMethod(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + alice := v.Actors.Account(address.SECP256K1, balance1T) + v.CommitPreconditions() + + v.Messages.Typed(alice.ID, alice.ID, MarketComputeDataCommitment(nil), Nonce(0), Value(big.Zero())) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrInvalidMethod)) +} diff --git a/tvx/scripts/msg_application/main.go b/tvx/scripts/msg_application/main.go new file mode 100644 index 000000000..359262e76 --- /dev/null +++ b/tvx/scripts/msg_application/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "github.com/filecoin-project/specs-actors/actors/abi" + + . "github.com/filecoin-project/oni/tvx/builders" + "github.com/filecoin-project/oni/tvx/schema" +) + +var ( + unknown = MustNewIDAddr(10000000) + balance1T = abi.NewTokenAmount(1_000_000_000_000) + transferAmnt = abi.NewTokenAmount(10) +) + +func main() { + g := NewGenerator() + + g.MessageVectorGroup("gas_cost", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-receipt-gas", + Version: "v1", + Desc: "fail to cover gas cost for message receipt on chain", + }, + Func: failCoverReceiptGasCost, + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-onchainsize-gas", + Version: "v1", + Desc: "not enough gas to pay message on-chain-size cost", + }, + Func: failCoverOnChainSizeGasCost, + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-transfer-accountcreation-gas", + Version: "v1", + Desc: "fail not enough gas to cover account actor creation on transfer", + }, + Func: failCoverTransferAccountCreationGasStepwise, + }) + + g.MessageVectorGroup("invalid_msgs", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-invalid-nonce", + Version: "v1", + Desc: "invalid actor nonce", + }, + Func: failInvalidActorNonce, + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-invalid-receiver-method", + Version: "v1", + Desc: "invalid receiver method", + }, + Func: failInvalidReceiverMethod, + }, + ) + + g.MessageVectorGroup("unknown_actors", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-unknown-sender", + Version: "v1", + Desc: "fail due to lack of gas when sender is unknown", + }, + Func: failUnknownSender, + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-unknown-receiver", + Version: "v1", + Desc: "inexistent receiver", + Comment: `Note that this test is not a valid message, since it is using +an unknown actor. However in the event that an invalid message isn't filtered by +block validation we need to ensure behaviour is consistent across VM implementations.`, + }, + Func: failUnknownReceiver, + }, + ) + + g.MessageVectorGroup("actor_exec", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "msg-apply-fail-actor-execution-illegal-arg", + Version: "v1", + Desc: "abort during actor execution due to illegal argument", + }, + Func: failActorExecutionAborted, + }, + ) + + g.Wait() +} diff --git a/tvx/scripts/msg_application/unknown_actors.go b/tvx/scripts/msg_application/unknown_actors.go new file mode 100644 index 000000000..f651e1b1c --- /dev/null +++ b/tvx/scripts/msg_application/unknown_actors.go @@ -0,0 +1,37 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +func failUnknownSender(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + alice := v.Actors.Account(address.SECP256K1, balance1T) + v.CommitPreconditions() + + v.Messages.Sugar().Transfer(unknown, alice.ID, Value(transferAmnt), Nonce(0)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrSenderInvalid)) +} + +func failUnknownReceiver(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) + + alice := v.Actors.Account(address.SECP256K1, balance1T) + v.CommitPreconditions() + + // Sending a message to non-existent ID address must produce an error. + unknownID := MustNewIDAddr(10000000) + v.Messages.Sugar().Transfer(alice.ID, unknownID, Value(transferAmnt), Nonce(0)) + + unknownActor := MustNewActorAddr("1234") + v.Messages.Sugar().Transfer(alice.ID, unknownActor, Value(transferAmnt), Nonce(1)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrInvalidReceiver)) +} diff --git a/tvx/scripts/paych/main.go b/tvx/scripts/paych/main.go new file mode 100644 index 000000000..039f852ec --- /dev/null +++ b/tvx/scripts/paych/main.go @@ -0,0 +1,14 @@ +package main + +import "github.com/filecoin-project/specs-actors/actors/abi" + +var ( + initialBal = abi.NewTokenAmount(200_000_000_000) + toSend = abi.NewTokenAmount(10_000) +) + +func main() { + happyPathCreate() + happyPathUpdate() + happyPathCollect() +} diff --git a/tvx/scripts/paych.go b/tvx/scripts/paych/ok.go similarity index 92% rename from tvx/scripts/paych.go rename to tvx/scripts/paych/ok.go index 3d809c33c..39c0c3f7d 100644 --- a/tvx/scripts/paych.go +++ b/tvx/scripts/paych/ok.go @@ -15,17 +15,6 @@ import ( "github.com/filecoin-project/oni/tvx/schema" ) -var ( - balance200B = abi.NewTokenAmount(200_000_000_000) - toSend = abi.NewTokenAmount(10_000) -) - -func main() { - happyPathCreate() - happyPathUpdate() - happyPathCollect() -} - func happyPathCreate() { metadata := &schema.Metadata{ID: "paych-create-ok", Version: "v1", Desc: "payment channel create"} @@ -34,7 +23,7 @@ func happyPathCreate() { // Set up sender and receiver accounts. var sender, receiver AddressHandle - v.Actors.AccountN(address.SECP256K1, balance200B, &sender, &receiver) + v.Actors.AccountN(address.SECP256K1, initialBal, &sender, &receiver) v.CommitPreconditions() // Add the constructor message. @@ -79,7 +68,7 @@ func happyPathUpdate() { v := MessageVector(metadata) v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPrice(1)) - v.Actors.AccountN(address.SECP256K1, balance200B, &sender, &receiver) + v.Actors.AccountN(address.SECP256K1, initialBal, &sender, &receiver) paychAddr = AddressHandle{ ID: MustNewIDAddr(MustIDFromAddress(receiver.ID) + 1), Robust: sender.NextActorAddress(0, 0), @@ -136,7 +125,7 @@ func happyPathCollect() { // Set up sender and receiver accounts. var sender, receiver AddressHandle var paychAddr AddressHandle - v.Actors.AccountN(address.SECP256K1, balance200B, &sender, &receiver) + v.Actors.AccountN(address.SECP256K1, initialBal, &sender, &receiver) paychAddr = AddressHandle{ ID: MustNewIDAddr(MustIDFromAddress(receiver.ID) + 1), Robust: sender.NextActorAddress(0, 0), @@ -175,7 +164,7 @@ func happyPathCollect() { // receiver_balance = initial_balance + paych_send - settle_paych_msg_gas - collect_paych_msg_gas gasUsed := big.Add(big.NewInt(settleMsg.Result.MessageReceipt.GasUsed), big.NewInt(collectMsg.Result.MessageReceipt.GasUsed)) - v.Assert.BalanceEq(receiver.Robust, big.Sub(big.Add(toSend, balance200B), gasUsed)) + v.Assert.BalanceEq(receiver.Robust, big.Sub(big.Add(toSend, initialBal), gasUsed)) // the paych actor should have been deleted after the collect v.Assert.ActorMissing(paychAddr.Robust)