5353210814
* 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>
346 lines
9.9 KiB
Go
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
|
|
}
|