diff --git a/tvx/drivers/test_driver.go b/tvx/drivers/test_driver.go index 354f73053..4b6129e53 100644 --- a/tvx/drivers/test_driver.go +++ b/tvx/drivers/test_driver.go @@ -558,6 +558,22 @@ func (td *TestDriver) GetStateRoot() cid.Cid { return td.st.stateRoot } +func (td *TestDriver) UpdatePreStateRoot() { + td.Vector.Pre.StateTree.RootCID = td.st.stateRoot +} + +func (td *TestDriver) UpdatePostStateRoot() { + td.Vector.Post.StateTree.RootCID = td.st.stateRoot +} + +func (td *TestDriver) MustSerialize(w io.Writer) { + td.Vector.Post.StateTree.RootCID = td.st.stateRoot + + td.Vector.CAR = td.MustMarshalGzippedCAR(td.Vector.Pre.StateTree.RootCID, td.Vector.Post.StateTree.RootCID) + + fmt.Fprintln(w, string(td.Vector.MustMarshalJSON())) +} + func (td *TestDriver) MustMarshalGzippedCAR(roots ...cid.Cid) []byte { var b bytes.Buffer gw := gzip.NewWriter(&b) diff --git a/tvx/exec_lotus.go b/tvx/exec_lotus.go index 932d569ab..e2799b839 100644 --- a/tvx/exec_lotus.go +++ b/tvx/exec_lotus.go @@ -75,7 +75,7 @@ func runExecLotus(_ *cli.Context) error { } func executeTestVector(tv schema.TestVector) error { - fmt.Println("executing test vector") + fmt.Println("executing test vector:", tv.Meta.Desc) switch tv.Class { case "message": var ( diff --git a/tvx/lotus/driver.go b/tvx/lotus/driver.go index a97611f37..518376bbb 100644 --- a/tvx/lotus/driver.go +++ b/tvx/lotus/driver.go @@ -10,6 +10,7 @@ import ( "github.com/filecoin-project/lotus/lib/blockstore" "github.com/filecoin-project/sector-storage/ffiwrapper" "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/puppet" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" ) @@ -42,6 +43,13 @@ func (d *Driver) ExecuteMessage(msg *types.Message, preroot cid.Cid, bs blocksto if err != nil { return nil, cid.Undef, err } + // need to modify the VM invoker to add the puppet actor + chainValInvoker := vm.NewInvoker() + chainValInvoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{}) + lvm.SetInvoker(chainValInvoker) + if err != nil { + return nil, cid.Undef, err + } fmt.Println("applying message") ret, err := lvm.ApplyMessage(d.ctx, msg) diff --git a/tvx/suite_messages_create_actor.go b/tvx/suite_messages_create_actor.go index 3f41c2868..34ac6be44 100644 --- a/tvx/suite_messages_create_actor.go +++ b/tvx/suite_messages_create_actor.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "github.com/hashicorp/go-multierror" @@ -32,6 +31,7 @@ func suiteMessages(c *cli.Context) error { err = multierror.Append(MessageTest_Paych()) err = multierror.Append(MessageTest_ValueTransferSimple()) err = multierror.Append(MessageTest_ValueTransferAdvance()) + err = multierror.Append(MessageTest_NestedSends()) return err.ErrorOrNil() } @@ -96,7 +96,7 @@ func MessageTest_AccountActorCreation() error { existingAccountAddr, _ := td.NewAccountActor(tc.existingActorType, tc.existingActorBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(existingAccountAddr, tc.newActorAddr, chain.Value(tc.newActorInitBal), chain.Nonce(0)) result := td.ApplyFailure( @@ -110,14 +110,7 @@ func MessageTest_AccountActorCreation() error { td.AssertBalance(existingAccountAddr, big_spec.Sub(big_spec.Sub(tc.existingActorBal, result.Receipt.GasUsed.Big()), tc.newActorInitBal)) } - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }() @@ -146,7 +139,7 @@ func MessageTest_InitActorSequentialIDAddressCreate() error { firstInitRet := td.ComputeInitActorExecReturn(sender, 0, 0, firstPaychAddr) secondInitRet := td.ComputeInitActorExecReturn(sender, 1, 0, secondPaychAddr) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg1 := td.MessageProducer.CreatePaymentChannelActor(sender, receiver, chain.Value(toSend), chain.Nonce(0)) td.ApplyExpect( @@ -160,14 +153,7 @@ func MessageTest_InitActorSequentialIDAddressCreate() error { chain.MustSerialize(&secondInitRet), ) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil } diff --git a/tvx/suite_messages_message_application.go b/tvx/suite_messages_message_application.go index 6ad5f67b4..9a4fbffdf 100644 --- a/tvx/suite_messages_message_application.go +++ b/tvx/suite_messages_message_application.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" abi_spec "github.com/filecoin-project/specs-actors/actors/abi" @@ -24,7 +23,7 @@ func MessageTest_MessageApplicationEdgecases() error { alice, _ := td.NewAccountActor(drivers.SECP, aliceBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(0), chain.GasPrice(1), chain.GasLimit(8)) @@ -32,14 +31,7 @@ func MessageTest_MessageApplicationEdgecases() error { msg, exitcode_spec.SysErrOutOfGas) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("fail to cover gas cost for message receipt on chain") @@ -53,7 +45,8 @@ func MessageTest_MessageApplicationEdgecases() error { alice, _ := td.NewAccountActor(drivers.SECP, aliceBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() + msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(0), chain.GasPrice(10), chain.GasLimit(1)) // Expect Message application to fail due to lack of gas @@ -69,14 +62,7 @@ func MessageTest_MessageApplicationEdgecases() error { msg, exitcode_spec.SysErrOutOfGas) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("not enough gas to pay message on-chain-size cost") @@ -89,7 +75,7 @@ func MessageTest_MessageApplicationEdgecases() error { td.Vector.Meta.Desc = testname alice, _ := td.NewAccountActor(drivers.SECP, aliceBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() aliceNonce := uint64(0) aliceNonceF := func() uint64 { @@ -116,14 +102,7 @@ func MessageTest_MessageApplicationEdgecases() error { ) } - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("fail not enough gas to cover account actor creation") @@ -137,7 +116,7 @@ func MessageTest_MessageApplicationEdgecases() error { alice, _ := td.NewAccountActor(drivers.SECP, aliceBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(1)) @@ -154,14 +133,7 @@ func MessageTest_MessageApplicationEdgecases() error { msg, exitcode_spec.SysErrSenderInvalid) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("invalid actor nonce") @@ -193,7 +165,7 @@ func MessageTest_MessageApplicationEdgecases() error { paychAddr := chain.MustNewIDAddr(chain.MustIDFromAddress(receiverID) + 1) createRet := td.ComputeInitActorExecReturn(sender, 0, 0, paychAddr) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.CreatePaymentChannelActor(sender, receiver, chain.Value(toSend), chain.Nonce(0)) @@ -222,14 +194,7 @@ func MessageTest_MessageApplicationEdgecases() error { msg, exitcode_spec.ErrIllegalArgument) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("abort during actor execution") @@ -243,7 +208,7 @@ func MessageTest_MessageApplicationEdgecases() error { alice, _ := td.NewAccountActor(drivers.SECP, aliceBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.MarketComputeDataCommitment(alice, alice, nil, chain.Nonce(0)) @@ -253,14 +218,7 @@ func MessageTest_MessageApplicationEdgecases() error { msg, exitcode_spec.SysErrInvalidMethod) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("invalid method for receiver") @@ -275,7 +233,7 @@ func MessageTest_MessageApplicationEdgecases() error { alice, _ := td.NewAccountActor(drivers.SECP, aliceBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() // Sending a message to non-existent ID address must produce an error. unknownA := chain.MustNewIDAddr(10000000) @@ -293,14 +251,7 @@ func MessageTest_MessageApplicationEdgecases() error { msg, exitcode_spec.SysErrInvalidReceiver) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("receiver ID/Actor address does not exist") diff --git a/tvx/suite_messages_nested.go b/tvx/suite_messages_nested.go new file mode 100644 index 000000000..54436bee0 --- /dev/null +++ b/tvx/suite_messages_nested.go @@ -0,0 +1,509 @@ +package main + +import ( + "bytes" + "os" + + address "github.com/filecoin-project/go-address" + vtypes "github.com/filecoin-project/oni/tvx/chain/types" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/abi/big" + builtin "github.com/filecoin-project/specs-actors/actors/builtin" + init_ "github.com/filecoin-project/specs-actors/actors/builtin/init" + "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + "github.com/filecoin-project/specs-actors/actors/builtin/paych" + "github.com/filecoin-project/specs-actors/actors/builtin/reward" + "github.com/filecoin-project/specs-actors/actors/puppet" + "github.com/filecoin-project/specs-actors/actors/runtime" + exitcode_spec "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + typegen "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/oni/tvx/chain" + "github.com/filecoin-project/oni/tvx/drivers" +) + +var PuppetAddress address.Address + +func init() { + var err error + // the address before the burnt funds address + PuppetAddress, err = address.NewIDAddress(builtin.FirstNonSingletonActorId - 2) + if err != nil { + panic(err) + } +} + +// Tests exercising messages sent internally from one actor to another. +// These use a multisig actor with approvers=1 as a convenient staging ground for arbitrary internal messages. +func MessageTest_NestedSends() error { + var acctDefaultBalance = abi.NewTokenAmount(1_000_000_000_000) + var multisigBalance = abi.NewTokenAmount(1_000_000_000) + nonce := uint64(1) + + err := func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + // Multisig sends back to the creator. + amtSent := abi.NewTokenAmount(1) + result := stage.sendOk(stage.creator, amtSent, builtin.MethodSend, nil, nonce) + + td.AssertActor(stage.creator, big.Sub(big.Add(balanceBefore, amtSent), result.Receipt.GasUsed.Big()), nonce+1) + + td.MustSerialize(os.Stdout) + + return nil + }("ok basic") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + // Multisig sends to new address. + newAddr := td.Wallet().NewSECP256k1AccountAddress() + amtSent := abi.NewTokenAmount(1) + result := stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce) + + td.AssertBalance(stage.msAddr, big.Sub(multisigBalance, amtSent)) + td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) + td.AssertBalance(newAddr, amtSent) + + td.MustSerialize(os.Stdout) + + return nil + }("ok to new actor") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + // Multisig sends to new address and invokes pubkey method at the same time. + newAddr := td.Wallet().NewSECP256k1AccountAddress() + amtSent := abi.NewTokenAmount(1) + result := stage.sendOk(newAddr, amtSent, builtin.MethodsAccount.PubkeyAddress, nil, nonce) + // TODO: use an explicit Approve() and check the return value is the correct pubkey address + // when the multisig Approve() method plumbs through the inner exit code and value. + // https://github.com/filecoin-project/specs-actors/issues/113 + //expected := bytes.Buffer{} + //require.NoError(t, newAddr.MarshalCBOR(&expected)) + //assert.Equal(t, expected.Bytes(), result.Receipt.ReturnValue) + + td.AssertBalance(stage.msAddr, big.Sub(multisigBalance, amtSent)) + td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) + td.AssertBalance(newAddr, amtSent) + + td.MustSerialize(os.Stdout) + + return nil + }("ok to new actor with invoke") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + _, anotherId := td.NewAccountActor(drivers.SECP, big.Zero()) + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + // Multisig sends to itself. + params := multisig.AddSignerParams{ + Signer: anotherId, + Increase: false, + } + result := stage.sendOk(stage.msAddr, big.Zero(), builtin.MethodsMultisig.AddSigner, ¶ms, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) + assert.Equal(drivers.T, big.Sub(balanceBefore, result.Receipt.GasUsed.Big()), td.GetBalance(stage.creator)) + var st multisig.State + td.GetActorState(stage.msAddr, &st) + assert.Equal(drivers.T, []address.Address{stage.creator, anotherId}, st.Signers) + + td.MustSerialize(os.Stdout) + + return nil + }("ok recursive") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + + newAddr := td.Wallet().NewSECP256k1AccountAddress() + amtSent := abi.NewTokenAmount(1) + // So long as the parameters are not actually used by the method, a message can carry arbitrary bytes. + params := typegen.Deferred{Raw: []byte{1, 2, 3, 4}} + stage.sendOk(newAddr, amtSent, builtin.MethodSend, ¶ms, nonce) + + td.AssertBalance(stage.msAddr, big.Sub(multisigBalance, amtSent)) + td.AssertBalance(newAddr, amtSent) + + td.MustSerialize(os.Stdout) + + return nil + }("ok non-CBOR params with transfer") + if err != nil { + return err + } + + // + // TODO: Tests to exercise invalid "syntax" of the inner message. + // These would fail message syntax validation if the message were top-level. + // + // Some of these require handcrafting the proposal params serialization. + // - malformed address: zero-length, one-length, too-short pubkeys, invalid UVarints, ... + // - negative method num + // + // Unfortunately the multisig actor can't be used to trigger a negative-value internal transfer because + // it checks just before sending. + // We need a custom actor for staging whackier messages. + + // + // The following tests exercise invalid semantics of the inner message + // + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + + newAddr := chain.MustNewIDAddr(1234) + amtSent := abi.NewTokenAmount(1) + stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + _, err := td.State().Actor(newAddr) + assert.Error(drivers.T, err) + + td.MustSerialize(os.Stdout) + + return nil + }("fail nonexistent ID address") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + + newAddr := chain.MustNewActorAddr("1234") + amtSent := abi.NewTokenAmount(1) + stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + _, err := td.State().Actor(newAddr) + assert.Error(drivers.T, err) + + td.MustSerialize(os.Stdout) + + return nil + }("fail nonexistent actor address") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + + newAddr := td.Wallet().NewSECP256k1AccountAddress() + amtSent := abi.NewTokenAmount(1) + stage.sendOk(newAddr, amtSent, abi.MethodNum(99), nil, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + td.AssertNoActor(newAddr) + + td.MustSerialize(os.Stdout) + + return nil + }("fail invalid methodnum new actor") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + amtSent := abi.NewTokenAmount(1) + result := stage.sendOk(stage.creator, amtSent, abi.MethodNum(99), nil, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) // Pay gas, don't receive funds. + + td.MustSerialize(os.Stdout) + + return nil + }("fail invalid methodnum for actor") + if err != nil { + return err + } + + // The multisig actor checks before attempting to transfer more than its balance, so we can't exercise that + // the VM also checks this. Need a custome actor to exercise this. + //t.Run("fail insufficient funds", func(t *testing.T) { + // td := builder.Build(t) + // defer td.Complete() + // + // stage := prepareStage(td, acctDefaultBalance, multisigBalance) + // balanceBefore := td.GetBalance(stage.creator) + // + // // Attempt to transfer from the multisig more than the balance it has. + // // The proposal to do should succeed, but the inner message fail. + // amtSent := big.Add(multisigBalance, abi.NewTokenAmount(1)) + // result := stage.send(stage.creator, amtSent, builtin.MethodSend, nil, nonce) + // assert.Equal(t, exitcode_spec.Ok, result.Receipt.ExitCode) + // + // td.AssertBalance(stage.msAddr, multisigBalance) // No change. + // td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) // Pay gas, don't receive funds. + //}) + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + params := adt.Empty // Missing params required by AddSigner + amtSent := abi.NewTokenAmount(1) + result := stage.sendOk(stage.msAddr, amtSent, builtin.MethodsMultisig.AddSigner, params, nonce) + + td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + assert.Equal(drivers.T, 1, len(stage.state().Signers)) // No new signers + + td.MustSerialize(os.Stdout) + + return nil + }("fail missing params") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + balanceBefore := td.GetBalance(stage.creator) + + // Wrong params for AddSigner + params := multisig.ProposeParams{ + To: stage.creator, + Value: big.Zero(), + Method: builtin.MethodSend, + Params: nil, + } + amtSent := abi.NewTokenAmount(1) + result := stage.sendOk(stage.msAddr, amtSent, builtin.MethodsMultisig.AddSigner, ¶ms, nonce) + + td.AssertBalance(stage.creator, big.Sub(balanceBefore, result.Receipt.GasUsed.Big())) + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + assert.Equal(drivers.T, 1, len(stage.state().Signers)) // No new signers + + td.MustSerialize(os.Stdout) + + return nil + }("fail mismatched params") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + prevHead := td.GetHead(builtin.RewardActorAddr) + + // AwardBlockReward will abort unless invoked by the system actor + params := reward.AwardBlockRewardParams{ + Miner: stage.creator, + Penalty: big.Zero(), + GasReward: big.Zero(), + } + amtSent := abi.NewTokenAmount(1) + stage.sendOk(builtin.RewardActorAddr, amtSent, builtin.MethodsReward.AwardBlockReward, ¶ms, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + td.AssertHead(builtin.RewardActorAddr, prevHead) + + td.MustSerialize(os.Stdout) + + return nil + }("fail inner abort") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + stage := prepareStage(td, acctDefaultBalance, multisigBalance) + prevHead := td.GetHead(builtin.InitActorAddr) + + // Illegal paych constructor params (addresses are not accounts) + ctorParams := paych.ConstructorParams{ + From: builtin.SystemActorAddr, + To: builtin.SystemActorAddr, + } + execParams := init_.ExecParams{ + CodeCID: builtin.PaymentChannelActorCodeID, + ConstructorParams: chain.MustSerialize(&ctorParams), + } + + amtSent := abi.NewTokenAmount(1) + stage.sendOk(builtin.InitActorAddr, amtSent, builtin.MethodsInit.Exec, &execParams, nonce) + + td.AssertBalance(stage.msAddr, multisigBalance) // No change. + td.AssertHead(builtin.InitActorAddr, prevHead) // Init state unchanged. + + td.MustSerialize(os.Stdout) + + return nil + }("fail aborted exec") + if err != nil { + return err + } + + err = func(testname string) error { + td := drivers.NewTestDriver() + td.Vector.Meta.Desc = testname + + // puppet actor has zero funds + puppetBalance := big.Zero() + + _, _, err := td.StateDriver.State().CreateActor(puppet.PuppetActorCodeID, PuppetAddress, puppetBalance, &puppet.State{}) + require.NoError(drivers.T, err) + + alice, _ := td.NewAccountActor(drivers.SECP, acctDefaultBalance) + bob, _ := td.NewAccountActor(drivers.SECP, big.Zero()) + + preroot := td.GetStateRoot() + td.Vector.Pre.StateTree.RootCID = preroot + + // alice tells the puppet actor to send funds to bob, the puppet actor has 0 balance so the inner send will fail, + // and alice will pay the gas cost. + amtSent := abi.NewTokenAmount(1) + result := td.ApplyMessage(td.MessageProducer.PuppetSend(alice, PuppetAddress, &puppet.SendParams{ + To: bob, + Value: amtSent, + Method: builtin.MethodSend, + Params: nil, + })) + + // the outer message should be applied successfully + assert.Equal(drivers.T, exitcode_spec.Ok, result.Receipt.ExitCode) + + var puppetRet puppet.SendReturn + chain.MustDeserialize(result.Receipt.ReturnValue, &puppetRet) + + // the inner message should fail + assert.Equal(drivers.T, exitcode_spec.SysErrInsufficientFunds, puppetRet.Code) + + // alice should be charged for the gas cost and bob should have not received any funds. + td.AssertBalance(alice, big.Sub(acctDefaultBalance, result.GasUsed().Big())) + td.AssertBalance(bob, big.Zero()) + + td.MustSerialize(os.Stdout) + + return nil + }("fail insufficient funds for transfer in inner send") + if err != nil { + return err + } + + return nil + + // TODO more tests: + // fail send running out of gas on inner method + // fail send when target method on multisig (recursive) aborts +} + +// Wraps a multisig actor as a stage for nested sends. +type msStage struct { + driver *drivers.TestDriver + creator address.Address // Address of the creator and sole signer of the multisig. + msAddr address.Address // Address of the multisig actor from which nested messages are sent. +} + +// Creates a multisig actor with its creator as sole approver. +func prepareStage(td *drivers.TestDriver, creatorBalance, msBalance abi.TokenAmount) *msStage { + _, creatorId := td.NewAccountActor(drivers.SECP, creatorBalance) + + msg := td.MessageProducer.CreateMultisigActor(creatorId, []address.Address{creatorId}, 0, 1, chain.Value(msBalance), chain.Nonce(0)) + + td.UpdatePreStateRoot() + + result := td.ApplyMessage(msg) + require.Equal(drivers.T, exitcode_spec.Ok, result.Receipt.ExitCode) + + var ret init_.ExecReturn + err := ret.UnmarshalCBOR(bytes.NewReader(result.Receipt.ReturnValue)) + require.NoError(drivers.T, err) + + return &msStage{ + driver: td, + creator: creatorId, + msAddr: ret.IDAddress, + } +} + +func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.MethodNum, params runtime.CBORMarshaler, approverNonce uint64) vtypes.ApplyMessageResult { + buf := bytes.Buffer{} + if params != nil { + err := params.MarshalCBOR(&buf) + require.NoError(drivers.T, err) + } + pparams := multisig.ProposeParams{ + To: to, + Value: value, + Method: method, + Params: buf.Bytes(), + } + msg := s.driver.MessageProducer.MultisigPropose(s.creator, s.msAddr, &pparams, chain.Nonce(approverNonce)) + result := s.driver.ApplyMessage(msg) + require.Equal(drivers.T, exitcode_spec.Ok, result.Receipt.ExitCode) + return result +} + +func (s *msStage) state() *multisig.State { + var msState multisig.State + s.driver.GetActorState(s.msAddr, &msState) + return &msState +} diff --git a/tvx/suite_messages_paych.go b/tvx/suite_messages_paych.go index 0ffb2a194..a3c11c85e 100644 --- a/tvx/suite_messages_paych.go +++ b/tvx/suite_messages_paych.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" abi_spec "github.com/filecoin-project/specs-actors/actors/abi" @@ -28,7 +27,7 @@ func MessageTest_Paych() error { // will be receiver on paych receiver, receiverID := td.NewAccountActor(drivers.SECP, initialBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() // the _expected_ address of the payment channel paychAddr := chain.MustNewIDAddr(chain.MustIDFromAddress(receiverID) + 1) @@ -47,14 +46,7 @@ func MessageTest_Paych() error { assert.Equal(drivers.T, receiverID, pcState.To) td.AssertBalance(paychAddr, toSend) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("happy path constructor") @@ -82,7 +74,7 @@ func MessageTest_Paych() error { // will be receiver on paych receiver, receiverID := td.NewAccountActor(drivers.SECP, initialBal) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() // the _expected_ address of the payment channel paychAddr := chain.MustNewIDAddr(chain.MustIDFromAddress(receiverID) + 1) @@ -118,14 +110,7 @@ func MessageTest_Paych() error { assert.Equal(drivers.T, pcNonce, ls.Nonce) assert.Equal(drivers.T, pcLane, ls.ID) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("happy path update") @@ -143,7 +128,7 @@ func MessageTest_Paych() error { paychAddr := chain.MustNewIDAddr(chain.MustIDFromAddress(receiverID) + 1) initRet := td.ComputeInitActorExecReturn(sender, 0, 0, paychAddr) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.CreatePaymentChannelActor(sender, receiver, chain.Value(toSend), chain.Nonce(0)) td.ApplyExpect( @@ -188,14 +173,7 @@ func MessageTest_Paych() error { // the paych actor should have been deleted after the collect td.AssertNoActor(paychAddr) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("happy path collect") diff --git a/tvx/suite_messages_transfer.go b/tvx/suite_messages_transfer.go index 7f0975a88..1c5a5d1da 100644 --- a/tvx/suite_messages_transfer.go +++ b/tvx/suite_messages_transfer.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" address "github.com/filecoin-project/go-address" @@ -109,7 +108,7 @@ func MessageTest_ValueTransferSimple() error { require.NoError(drivers.T, err) require.Equal(drivers.T, tc.senderBal.String(), sendAct.Balance().String()) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(tc.sender, tc.receiver, chain.Value(tc.transferAmnt), chain.Nonce(0)) @@ -131,14 +130,7 @@ func MessageTest_ValueTransferSimple() error { } } - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }(tc.desc) @@ -160,7 +152,7 @@ func MessageTest_ValueTransferAdvance() error { alice, _ := td.NewAccountActor(drivers.SECP, aliceInitialBalance) transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(0)) result := td.ApplyOk(msg) @@ -168,14 +160,7 @@ func MessageTest_ValueTransferAdvance() error { // since this is a self transfer expect alice's balance to only decrease by the gasUsed td.AssertBalance(alice, big_spec.Sub(aliceInitialBalance, result.Receipt.GasUsed.Big())) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("self transfer secp to secp") @@ -190,7 +175,7 @@ func MessageTest_ValueTransferAdvance() error { alice, aliceId := td.NewAccountActor(drivers.SECP, aliceInitialBalance) transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(alice, aliceId, chain.Value(transferAmnt), chain.Nonce(0)) @@ -199,14 +184,7 @@ func MessageTest_ValueTransferAdvance() error { // since this is a self transfer expect alice's balance to only decrease by the gasUsed td.AssertBalance(alice, big_spec.Sub(aliceInitialBalance, result.Receipt.GasUsed.Big())) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("self transfer secp to id address") @@ -221,7 +199,7 @@ func MessageTest_ValueTransferAdvance() error { alice, aliceId := td.NewAccountActor(drivers.SECP, aliceInitialBalance) transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(aliceId, alice, chain.Value(transferAmnt), chain.Nonce(0)) @@ -230,14 +208,7 @@ func MessageTest_ValueTransferAdvance() error { // since this is a self transfer expect alice's balance to only decrease by the gasUsed td.AssertBalance(alice, big_spec.Sub(aliceInitialBalance, result.Receipt.GasUsed.Big())) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("self transfer id to secp address") @@ -252,7 +223,7 @@ func MessageTest_ValueTransferAdvance() error { alice, aliceId := td.NewAccountActor(drivers.SECP, aliceInitialBalance) transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(aliceId, aliceId, chain.Value(transferAmnt), chain.Nonce(0)) @@ -261,14 +232,7 @@ func MessageTest_ValueTransferAdvance() error { // since this is a self transfer expect alice's balance to only decrease by the gasUsed td.AssertBalance(alice, big_spec.Sub(aliceInitialBalance, result.Receipt.GasUsed.Big())) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("self transfer id to id address") @@ -284,7 +248,7 @@ func MessageTest_ValueTransferAdvance() error { receiver := td.Wallet().NewSECP256k1AccountAddress() transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(alice, receiver, chain.Value(transferAmnt), chain.Nonce(0)) @@ -292,14 +256,7 @@ func MessageTest_ValueTransferAdvance() error { td.AssertBalance(alice, big_spec.Sub(big_spec.Sub(aliceInitialBalance, result.Receipt.GasUsed.Big()), transferAmnt)) td.AssertBalance(receiver, transferAmnt) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("ok transfer from known address to new account") @@ -315,7 +272,7 @@ func MessageTest_ValueTransferAdvance() error { unknown := td.Wallet().NewSECP256k1AccountAddress() transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(unknown, alice, chain.Value(transferAmnt), chain.Nonce(0)) @@ -324,14 +281,7 @@ func MessageTest_ValueTransferAdvance() error { exitcode_spec.SysErrSenderInvalid) td.AssertBalance(alice, aliceInitialBalance) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("fail to transfer from unknown account to known address") @@ -347,7 +297,7 @@ func MessageTest_ValueTransferAdvance() error { receiver := td.Wallet().NewSECP256k1AccountAddress() transferAmnt := abi_spec.NewTokenAmount(10) - preroot := td.GetStateRoot() + td.UpdatePreStateRoot() msg := td.MessageProducer.Transfer(sender, receiver, chain.Value(transferAmnt), chain.Nonce(0)) @@ -357,14 +307,7 @@ func MessageTest_ValueTransferAdvance() error { td.AssertNoActor(sender) td.AssertNoActor(receiver) - postroot := td.GetStateRoot() - - td.Vector.CAR = td.MustMarshalGzippedCAR(preroot, postroot) - td.Vector.Pre.StateTree.RootCID = preroot - td.Vector.Post.StateTree.RootCID = postroot - - // encode and output - fmt.Fprintln(os.Stdout, string(td.Vector.MustMarshalJSON())) + td.MustSerialize(os.Stdout) return nil }("fail to transfer from unknown address to unknown address")