lotus/tvx/messages_message_application.go
Anton Evangelatov 5353210814
generate test-vectors based on tests from chain-validation project (#181)
* Read a subset of filecoin state over the full node API

* wip

* import export wip

* extract actors from message

* generate car for any state

* library for providing a pruned statetree

* test vector schema draft + example 'message' class test vector.

* message => messages test vector class.

* fixup

* wip

* use lb.NewBlockstore with ID

* fixup lotus-soup, and generate

* fix deals

* magic params

* work on schema / export of test vector

* fixup

* wip deserialise state tree

* pass at building a test case from a message

* progress loading / serializing

* recreation of ipld nodes

* generation of vector creates json

* kick off tvx tool.

* wip

* wip

* retain init actor state.

* initial test with printed out state

* remove testing.T, but keep require and assert libraries

* wip refactor state tree plucking.

* simplified

* removed factories

* remove builder.Build ; remove interface - use concrete iface

* comment out validateState

* remove Validator

* remove TestDriverBuilder

* remove client

* remove box

* remove gen

* remove factories

* remove KeyManager interfafce

* moved stuff around

* remove ValidationConfig

* extract randomness

* extract config and key_manager

* extract statewrapper

* extract applier

* rename factories to various

* flatten chain-validation package

* initial marshal of test vector

* do not require schema package

* fixup

* run all messages tests

* better names

* run all messages tests

* remove Indent setting from JSON encoder for now

* refactor, and actually running successfully ;-)

* remove irrelevant files; rename extract-msg command.

* remove root CID from state_tree object in schema.

* add tvx/lotus package; adjust .gitignore.

* tidy up command flag management.

* add comment.

* remove validateState and trackState

* remove xerrors

* remove NewVM

* remove commented out RootCID sets

* enable more tests

* add all `message_application` tests

* delete all.json

* update Message struct

* fix message serialization

* support multiple messages

* gofmt

* remove custom Message and SignedMessage types

* update tests with gzip and adhere to new schema for compressed CAR

* improved iface for Marshal

* update Validation

* remove test-suites and utils

* better names for chain. methods

* go mod tidy

* remove top-level dummyT

Co-authored-by: Will Scott <will@cypherpunk.email>
Co-authored-by: Raúl Kripalani <raul@protocol.ai>
2020-08-05 19:40:09 +02:00

346 lines
9.9 KiB
Go

package main
import (
"encoding/json"
"os"
abi_spec "github.com/filecoin-project/specs-actors/actors/abi"
big_spec "github.com/filecoin-project/specs-actors/actors/abi/big"
paych_spec "github.com/filecoin-project/specs-actors/actors/builtin/paych"
crypto_spec "github.com/filecoin-project/specs-actors/actors/crypto"
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"
)
func MessageTest_MessageApplicationEdgecases() error {
var aliceBal = abi_spec.NewTokenAmount(1_000_000_000_000)
var transferAmnt = abi_spec.NewTokenAmount(10)
err := func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
alice, _ := td.NewAccountActor(drivers.SECP, aliceBal)
msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(0), chain.GasPrice(1), chain.GasLimit(8))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
td.ApplyFailure(
msg,
exitcode_spec.SysErrOutOfGas)
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("fail to cover gas cost for message receipt on chain")
if err != nil {
return err
}
err = func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
alice, _ := td.NewAccountActor(drivers.SECP, aliceBal)
// Expect Message application to fail due to lack of gas
td.ApplyFailure(
td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(0), chain.GasPrice(10), chain.GasLimit(1)),
exitcode_spec.SysErrOutOfGas)
// Expect Message application to fail due to lack of gas when sender is unknown
unknown := chain.MustNewIDAddr(10000000)
msg := td.MessageProducer.Transfer(unknown, alice, chain.Value(transferAmnt), chain.Nonce(0), chain.GasPrice(10), chain.GasLimit(1))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
td.ApplyFailure(
msg,
exitcode_spec.SysErrOutOfGas)
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("not enough gas to pay message on-chain-size cost")
if err != nil {
return err
}
err = func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
alice, _ := td.NewAccountActor(drivers.SECP, aliceBal)
aliceNonce := uint64(0)
aliceNonceF := func() uint64 {
defer func() { aliceNonce++ }()
return aliceNonce
}
newAccountA := chain.MustNewSECP256K1Addr("1")
msg := td.MessageProducer.Transfer(alice, newAccountA, chain.Value(transferAmnt), chain.Nonce(aliceNonceF()))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
// get the "true" gas cost of applying the message
result := td.ApplyOk(msg)
// decrease the gas cost by `gasStep` for each apply and ensure `SysErrOutOfGas` is always returned.
trueGas := int64(result.GasUsed())
gasStep := int64(trueGas / 100)
newAccountB := chain.MustNewSECP256K1Addr("2")
for tryGas := trueGas - gasStep; tryGas > 0; tryGas -= gasStep {
msg := td.MessageProducer.Transfer(alice, newAccountB, chain.Value(transferAmnt), chain.Nonce(aliceNonceF()), chain.GasPrice(1), chain.GasLimit(tryGas))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
td.ApplyFailure(
msg,
exitcode_spec.SysErrOutOfGas,
)
}
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("fail not enough gas to cover account actor creation")
if err != nil {
return err
}
err = func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
alice, _ := td.NewAccountActor(drivers.SECP, aliceBal)
msg := td.MessageProducer.Transfer(alice, alice, chain.Value(transferAmnt), chain.Nonce(1))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
// Expect Message application to fail due to callseqnum being invalid: 1 instead of 0
td.ApplyFailure(
msg,
exitcode_spec.SysErrSenderStateInvalid)
unknown := chain.MustNewIDAddr(10000000)
msg = td.MessageProducer.Transfer(unknown, alice, chain.Value(transferAmnt), chain.Nonce(1))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
// Expect message application to fail due to unknow actor when call seq num is also incorrect
td.ApplyFailure(
msg,
exitcode_spec.SysErrSenderInvalid)
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("invalid actor CallSeqNum")
if err != nil {
return err
}
err = func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
const pcTimeLock = abi_spec.ChainEpoch(10)
const pcLane = uint64(123)
const pcNonce = uint64(1)
var pcAmount = big_spec.NewInt(10)
var initialBal = abi_spec.NewTokenAmount(200_000_000_000)
var toSend = abi_spec.NewTokenAmount(10_000)
var pcSig = &crypto_spec.Signature{
Type: crypto_spec.SigTypeBLS,
Data: []byte("Grrr im an invalid signature, I cause panics in the payment channel actor"),
}
// will create and send on payment channel
sender, _ := td.NewAccountActor(drivers.SECP, initialBal)
// will be receiver on paych
receiver, receiverID := td.NewAccountActor(drivers.SECP, initialBal)
// the _expected_ address of the payment channel
paychAddr := chain.MustNewIDAddr(chain.MustIdFromAddress(receiverID) + 1)
createRet := td.ComputeInitActorExecReturn(sender, 0, 0, paychAddr)
msg := td.MessageProducer.CreatePaymentChannelActor(sender, receiver, chain.Value(toSend), chain.Nonce(0))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
td.ApplyExpect(
msg,
chain.MustSerialize(&createRet))
msg = td.MessageProducer.PaychUpdateChannelState(sender, paychAddr, &paych_spec.UpdateChannelStateParams{
Sv: paych_spec.SignedVoucher{
ChannelAddr: paychAddr,
TimeLockMin: pcTimeLock,
TimeLockMax: pcTimeLock,
SecretPreimage: nil,
Extra: nil,
Lane: pcLane,
Nonce: pcNonce,
Amount: pcAmount,
MinSettleHeight: 0,
Merges: nil,
Signature: pcSig, // construct with invalid signature
},
}, chain.Nonce(1), chain.Value(big_spec.Zero()))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
// message application fails due to invalid argument (signature).
td.ApplyFailure(
msg,
exitcode_spec.ErrIllegalArgument)
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("abort during actor execution")
if err != nil {
return err
}
err = func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
alice, _ := td.NewAccountActor(drivers.SECP, aliceBal)
msg := td.MessageProducer.MarketComputeDataCommitment(alice, alice, nil, chain.Nonce(0))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
// message application fails because ComputeDataCommitment isn't defined
// on the recipient actor
td.ApplyFailure(
msg,
exitcode_spec.SysErrInvalidMethod)
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("invalid method for receiver")
if err != nil {
return err
}
err = func(testname string) error {
td := drivers.NewTestDriver()
v := newEmptyMessageVector()
preroot := td.GetStateRoot()
alice, _ := td.NewAccountActor(drivers.SECP, aliceBal)
// Sending a message to non-existent ID address must produce an error.
unknownA := chain.MustNewIDAddr(10000000)
msg := td.MessageProducer.Transfer(alice, unknownA, chain.Value(transferAmnt), chain.Nonce(0))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
td.ApplyFailure(
msg,
exitcode_spec.SysErrInvalidReceiver)
// Sending a message to non-existing actor address must produce an error.
unknownB := chain.MustNewActorAddr("1234")
msg = td.MessageProducer.Transfer(alice, unknownB, chain.Value(transferAmnt), chain.Nonce(1))
v.ApplyMessages = append(v.ApplyMessages, chain.MustSerialize(msg))
td.ApplyFailure(
msg,
exitcode_spec.SysErrInvalidReceiver)
postroot := td.GetStateRoot()
v.CAR = td.MustMarshalGzippedCAR(preroot, postroot)
v.Pre.StateTree.RootCID = preroot
v.Post.StateTree.RootCID = postroot
// encode and output
enc := json.NewEncoder(os.Stdout)
if err := enc.Encode(&v); err != nil {
return err
}
return nil
}("receiver ID/Actor address does not exist")
if err != nil {
return err
}
return nil
// TODO more tests:
// - missing/mismatched params for receiver
// - various out-of-gas cases
}