diff --git a/.circleci/config.yml b/.circleci/config.yml index db5bfd8b9..17e0c1951 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,6 +64,9 @@ jobs: - run: name: "run messages test vector suite: actor_creation" command: pushd tvx/scripts/actor_creation && go build . && ./actor_creation | ../../tvx exec-lotus + - run: + name: "run messages test vector suite: transfer" + command: pushd tvx/scripts/transfer && go build . && ./transfer | ../../tvx exec-lotus soup-build-linux: executor: linux steps: diff --git a/tvx/_suite_messages_transfer.go b/tvx/_suite_messages_transfer.go deleted file mode 100644 index 1c5a5d1da..000000000 --- a/tvx/_suite_messages_transfer.go +++ /dev/null @@ -1,319 +0,0 @@ -package main - -import ( - "os" - - address "github.com/filecoin-project/go-address" - abi_spec "github.com/filecoin-project/specs-actors/actors/abi" - big_spec "github.com/filecoin-project/specs-actors/actors/abi/big" - account_spec "github.com/filecoin-project/specs-actors/actors/builtin/account" - - require "github.com/stretchr/testify/require" - - builtin_spec "github.com/filecoin-project/specs-actors/actors/builtin" - exitcode_spec "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" - - "github.com/filecoin-project/oni/tvx/chain" - "github.com/filecoin-project/oni/tvx/drivers" -) - -type valueTransferTestCases struct { - desc string - - sender address.Address - senderBal big_spec.Int - - transferAmnt big_spec.Int - - receiver address.Address - receiverBal big_spec.Int - - code exitcode_spec.ExitCode -} - -func MessageTest_ValueTransferSimple() error { - alice := chain.MustNewSECP256K1Addr("1") - bob := chain.MustNewSECP256K1Addr("2") - const gasLimit = 1_000_000_000 - - testCases := []valueTransferTestCases{ - { - desc: "successfully transfer funds from sender to receiver", - - sender: alice, - senderBal: big_spec.NewInt(10 * gasLimit), - - transferAmnt: big_spec.NewInt(50), - - receiver: bob, - receiverBal: big_spec.Zero(), - - code: exitcode_spec.Ok, - }, - { - desc: "successfully transfer zero funds from sender to receiver", - - sender: alice, - senderBal: big_spec.NewInt(10 * gasLimit), - - transferAmnt: big_spec.NewInt(0), - - receiver: bob, - receiverBal: big_spec.Zero(), - - code: exitcode_spec.Ok, - }, - { - desc: "fail to transfer more funds than sender balance > 0", - - sender: alice, - senderBal: big_spec.NewInt(10 * gasLimit), - - transferAmnt: big_spec.NewInt(10*gasLimit - gasLimit + 1), - - receiver: bob, - receiverBal: big_spec.Zero(), - - code: exitcode_spec.SysErrInsufficientFunds, - }, - { - desc: "fail to transfer more funds than sender has when sender balance == zero", - - sender: alice, - senderBal: big_spec.NewInt(gasLimit), - - transferAmnt: big_spec.NewInt(1), - - receiver: bob, - receiverBal: big_spec.Zero(), - - code: exitcode_spec.SysErrInsufficientFunds, - }, - } - - for _, tc := range testCases { - err := func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - // Create the to and from actors with balance in the state tree - _, _, err := td.State().CreateActor(builtin_spec.AccountActorCodeID, tc.sender, tc.senderBal, &account_spec.State{Address: tc.sender}) - require.NoError(drivers.T, err) - if tc.sender.String() != tc.receiver.String() { - _, _, err := td.State().CreateActor(builtin_spec.AccountActorCodeID, tc.receiver, tc.receiverBal, &account_spec.State{Address: tc.receiver}) - require.NoError(drivers.T, err) - } - - sendAct, err := td.State().Actor(tc.sender) - require.NoError(drivers.T, err) - require.Equal(drivers.T, tc.senderBal.String(), sendAct.Balance().String()) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(tc.sender, tc.receiver, chain.Value(tc.transferAmnt), chain.Nonce(0)) - - result := td.ApplyFailure( - msg, - tc.code, - ) - - // create a message to transfer funds from `to` to `from` for amount `transferAmnt` and apply it to the state tree - // assert the actor balances changed as expected, the receiver balance should not change if transfer fails - if tc.code.IsSuccess() { - td.AssertBalance(tc.sender, big_spec.Sub(big_spec.Sub(tc.senderBal, tc.transferAmnt), result.Receipt.GasUsed.Big())) - td.AssertBalance(tc.receiver, tc.transferAmnt) - } else { - if tc.code == exitcode_spec.SysErrInsufficientFunds { - td.AssertBalance(tc.sender, big_spec.Sub(tc.senderBal, result.Receipt.GasUsed.Big())) - } else { - td.AssertBalance(tc.sender, tc.senderBal) - } - } - - td.MustSerialize(os.Stdout) - - return nil - }(tc.desc) - if err != nil { - return err - } - } - - return nil -} - -func MessageTest_ValueTransferAdvance() error { - var aliceInitialBalance = abi_spec.NewTokenAmount(10_000_000_000) - - err := func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - alice, _ := td.NewAccountActor(drivers.SECP, aliceInitialBalance) - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(0)) - result := td.ApplyOk(msg) - - // 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())) - - td.MustSerialize(os.Stdout) - - return nil - }("self transfer secp to secp") - if err != nil { - return err - } - - err = func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - alice, aliceId := td.NewAccountActor(drivers.SECP, aliceInitialBalance) - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(alice, aliceId, chain.Value(transferAmnt), chain.Nonce(0)) - - result := td.ApplyOk(msg) - - // 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())) - - td.MustSerialize(os.Stdout) - - return nil - }("self transfer secp to id address") - if err != nil { - return err - } - - err = func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - alice, aliceId := td.NewAccountActor(drivers.SECP, aliceInitialBalance) - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(aliceId, alice, chain.Value(transferAmnt), chain.Nonce(0)) - - result := td.ApplyOk(msg) - - // 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())) - - td.MustSerialize(os.Stdout) - - return nil - }("self transfer id to secp address") - if err != nil { - return err - } - - err = func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - alice, aliceId := td.NewAccountActor(drivers.SECP, aliceInitialBalance) - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(aliceId, aliceId, chain.Value(transferAmnt), chain.Nonce(0)) - - result := td.ApplyOk(msg) - - // 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())) - - td.MustSerialize(os.Stdout) - - return nil - }("self transfer id to id address") - if err != nil { - return err - } - - err = func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - alice, _ := td.NewAccountActor(drivers.SECP, aliceInitialBalance) - receiver := td.Wallet().NewSECP256k1AccountAddress() - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(alice, receiver, chain.Value(transferAmnt), chain.Nonce(0)) - - result := td.ApplyOk(msg) - td.AssertBalance(alice, big_spec.Sub(big_spec.Sub(aliceInitialBalance, result.Receipt.GasUsed.Big()), transferAmnt)) - td.AssertBalance(receiver, transferAmnt) - - td.MustSerialize(os.Stdout) - - return nil - }("ok transfer from known address to new account") - if err != nil { - return err - } - - err = func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - alice, _ := td.NewAccountActor(drivers.SECP, aliceInitialBalance) - unknown := td.Wallet().NewSECP256k1AccountAddress() - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(unknown, alice, chain.Value(transferAmnt), chain.Nonce(0)) - - td.ApplyFailure( - msg, - exitcode_spec.SysErrSenderInvalid) - td.AssertBalance(alice, aliceInitialBalance) - - td.MustSerialize(os.Stdout) - - return nil - }("fail to transfer from unknown account to known address") - if err != nil { - return err - } - - err = func(testname string) error { - td := drivers.NewTestDriver() - td.Vector.Meta.Desc = testname - - sender := td.Wallet().NewSECP256k1AccountAddress() - receiver := td.Wallet().NewSECP256k1AccountAddress() - transferAmnt := abi_spec.NewTokenAmount(10) - - td.UpdatePreStateRoot() - - msg := td.MessageProducer.Transfer(sender, receiver, chain.Value(transferAmnt), chain.Nonce(0)) - - td.ApplyFailure( - msg, - exitcode_spec.SysErrSenderInvalid) - td.AssertNoActor(sender) - td.AssertNoActor(receiver) - - td.MustSerialize(os.Stdout) - - return nil - }("fail to transfer from unknown address to unknown address") - if err != nil { - return err - } - - return nil -} diff --git a/tvx/builders/address.go b/tvx/builders/address.go index 51ddafeda..e4deb3f2b 100644 --- a/tvx/builders/address.go +++ b/tvx/builders/address.go @@ -15,6 +15,14 @@ type AddressHandle struct { ID, Robust address.Address } +func (ah AddressHandle) IDAddr() address.Address { + return ah.ID +} + +func (ah AddressHandle) RobustAddr() address.Address { + return ah.Robust +} + func (ah AddressHandle) String() string { return fmt.Sprintf("AddressHandle[ID: %s, Robust: %s]", ah.ID, ah.Robust) } diff --git a/tvx/scripts/transfer/basic.go b/tvx/scripts/transfer/basic.go new file mode 100644 index 000000000..e38728a0b --- /dev/null +++ b/tvx/scripts/transfer/basic.go @@ -0,0 +1,43 @@ +package main + +import ( + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi/big" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +type basicTransferParams struct { + senderType address.Protocol + senderBal abi.TokenAmount + receiverType address.Protocol + amount abi.TokenAmount + exitCode exitcode.ExitCode +} + +func basicTransfer(params basicTransferParams) func(v *Builder) { + return func(v *Builder) { + v.Messages.SetDefaults(GasLimit(gasLimit), GasPremium(1), GasFeeCap(gasFeeCap)) + + // Set up sender and receiver accounts. + var sender, receiver AddressHandle + sender = v.Actors.Account(params.senderType, params.senderBal) + receiver = v.Actors.Account(params.receiverType, big.Zero()) + v.CommitPreconditions() + + // Perform the transfer. + v.Messages.Sugar().Transfer(sender.ID, receiver.ID, Value(params.amount), Nonce(0)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(params.exitCode)) + v.Assert.EveryMessageSenderSatisfies(BalanceUpdated(big.Zero())) + + if params.exitCode.IsSuccess() { + v.Assert.EveryMessageSenderSatisfies(NonceUpdated()) + v.Assert.BalanceEq(receiver.ID, params.amount) + } + } +} diff --git a/tvx/scripts/transfer/main.go b/tvx/scripts/transfer/main.go new file mode 100644 index 000000000..acaab53d9 --- /dev/null +++ b/tvx/scripts/transfer/main.go @@ -0,0 +1,165 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" + "github.com/filecoin-project/oni/tvx/schema" +) + +const ( + gasLimit = 1_000_000_000 + gasFeeCap = 200 +) + +func main() { + g := NewGenerator() + defer g.Wait() + + g.MessageVectorGroup("basic", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "ok", + Version: "v1", + Desc: "successfully transfer funds from sender to receiver", + }, + Func: basicTransfer(basicTransferParams{ + senderType: address.SECP256K1, + senderBal: abi.NewTokenAmount(10 * gasLimit * gasFeeCap), + receiverType: address.SECP256K1, + amount: abi.NewTokenAmount(50), + exitCode: exitcode.Ok, + }), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "ok-zero", + Version: "v1", + Desc: "successfully transfer zero funds from sender to receiver", + }, + Func: basicTransfer(basicTransferParams{ + senderType: address.SECP256K1, + senderBal: abi.NewTokenAmount(10 * gasFeeCap * gasLimit), + receiverType: address.SECP256K1, + amount: abi.NewTokenAmount(0), + exitCode: exitcode.Ok, + }), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "fail-exceed-balance", + Version: "v1", + Desc: "fail to transfer more funds than sender balance > 0", + }, + Func: basicTransfer(basicTransferParams{ + senderType: address.SECP256K1, + senderBal: abi.NewTokenAmount(10 * gasFeeCap * gasLimit), + receiverType: address.SECP256K1, + amount: abi.NewTokenAmount(10*gasFeeCap*gasLimit - gasFeeCap*gasLimit + 1), + exitCode: exitcode.SysErrInsufficientFunds, + }), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "fail-balance-equal-gas", + Version: "v1", + Desc: "fail to transfer more funds than sender has when sender balance matches gas limit", + }, + Func: basicTransfer(basicTransferParams{ + senderType: address.SECP256K1, + senderBal: abi.NewTokenAmount(gasFeeCap * gasLimit), + receiverType: address.SECP256K1, + amount: abi.NewTokenAmount(1), + exitCode: exitcode.SysErrInsufficientFunds, + }), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "fail-balance-under-gaslimit", + Version: "v1", + Desc: "fail to transfer when sender balance under gas limit", + }, + Func: basicTransfer(basicTransferParams{ + senderType: address.SECP256K1, + senderBal: abi.NewTokenAmount(gasFeeCap*gasLimit - 1), + receiverType: address.SECP256K1, + amount: abi.NewTokenAmount(0), + exitCode: exitcode.SysErrSenderStateInvalid, + }), + }, + ) + + g.MessageVectorGroup("self_transfer", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "secp-to-secp-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.RobustAddr, AddressHandle.RobustAddr), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "secp-to-id-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.RobustAddr, AddressHandle.IDAddr), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "id-to-secp-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.IDAddr, AddressHandle.RobustAddr), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "id-to-id-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.IDAddr, AddressHandle.IDAddr), + }, + ) + + g.MessageVectorGroup("unknown_accounts", + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "fail-unknown-sender-known-receiver", + Version: "v1", + Desc: "fail to transfer from unknown account to known address", + }, + Func: failTransferUnknownSenderKnownReceiver, + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "fail-unknown-sender-unknown-receiver", + Version: "v1", + Desc: "fail to transfer from unknown address to unknown address", + }, + Func: failTransferUnknownSenderUnknownReceiver, + }, + + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "secp-to-id-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.RobustAddr, AddressHandle.IDAddr), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "id-to-secp-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.IDAddr, AddressHandle.RobustAddr), + }, + &MessageVectorGenItem{ + Metadata: &schema.Metadata{ + ID: "id-to-id-addresses", + Version: "v1", + }, + Func: selfTransfer(AddressHandle.IDAddr, AddressHandle.IDAddr), + }, + ) +} diff --git a/tvx/scripts/transfer/self_transfer.go b/tvx/scripts/transfer/self_transfer.go new file mode 100644 index 000000000..e6e158eee --- /dev/null +++ b/tvx/scripts/transfer/self_transfer.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +func selfTransfer(from, to func(h AddressHandle) address.Address) func(v *Builder) { + return func(v *Builder) { + initial := abi.NewTokenAmount(1_000_000_000_000) + transfer := abi.NewTokenAmount(10) + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200)) + + // Set up sender account. + account := v.Actors.Account(address.SECP256K1, initial) + v.CommitPreconditions() + + // Perform the transfer. + msg := v.Messages.Sugar().Transfer(from(account), to(account), Value(transfer), Nonce(0)) + v.CommitApplies() + + v.Assert.Equal(exitcode.Ok, msg.Result.ExitCode) + + // the transfer balance comes back to us. + v.Assert.EveryMessageSenderSatisfies(BalanceUpdated(transfer)) + } +} diff --git a/tvx/scripts/transfer/unknown.go b/tvx/scripts/transfer/unknown.go new file mode 100644 index 000000000..f64e9c5bc --- /dev/null +++ b/tvx/scripts/transfer/unknown.go @@ -0,0 +1,52 @@ +package main + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" + + . "github.com/filecoin-project/oni/tvx/builders" +) + +var ( + initial = abi.NewTokenAmount(1_000_000_000_000) + transfer = Value(abi.NewTokenAmount(10)) +) + +func failTransferUnknownSenderKnownReceiver(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200)) + + // Set up receiver account. + receiver := v.Actors.Account(address.SECP256K1, initial) + v.CommitPreconditions() + + // create a new random sender. + sender := v.Wallet.NewSECP256k1Account() + + // perform the transfer. + v.Messages.Sugar().Transfer(sender, receiver.Robust, transfer, Nonce(0)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrSenderInvalid)) + v.Assert.ActorMissing(sender) + v.Assert.ActorExists(receiver.Robust) + v.Assert.BalanceEq(receiver.Robust, initial) +} + +func failTransferUnknownSenderUnknownReceiver(v *Builder) { + v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200)) + + // no accounts in the system. + v.CommitPreconditions() + + // create new random senders and resceivers. + sender, receiver := v.Wallet.NewSECP256k1Account(), v.Wallet.NewSECP256k1Account() + + // perform the transfer. + v.Messages.Sugar().Transfer(sender, receiver, transfer, Nonce(0)) + v.CommitApplies() + + v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrSenderInvalid)) + v.Assert.ActorMissing(sender) + v.Assert.ActorMissing(receiver) +}