transfer builders and scripts to filecoin-project/test-vectors. (#242)

This commit is contained in:
Raúl Kripalani 2020-08-16 20:36:49 +01:00 committed by GitHub
parent 6164d16f19
commit 497bda21b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 0 additions and 3741 deletions

View File

@ -52,21 +52,6 @@ jobs:
- run:
name: "build tvx"
command: pushd tvx && go build .
- run:
name: "run messages test vector suite: msg_application"
command: pushd tvx/scripts/msg_application && go build . && ./msg_application | ../../tvx exec-lotus
- run:
name: "run messages test vector suite: nested send"
command: pushd tvx/scripts/nested && go build . && ./nested | ../../tvx exec-lotus
- run:
name: "run messages test vector suite: paych"
command: pushd tvx/scripts/paych && go build . && ./paych | ../../tvx exec-lotus
- 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:

View File

@ -1,261 +0,0 @@
package builders
import (
"context"
"log"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"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"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
)
type registeredActor struct {
handle AddressHandle
initial abi.TokenAmount
}
// Actors is an object that manages actors in the test vector.
type Actors struct {
// registered stores registered actors and their initial balances.
registered []registeredActor
b *Builder
}
func newActors(b *Builder) *Actors {
return &Actors{b: b}
}
// Count returns the number of actors registered during preconditions.
func (a *Actors) Count() int {
return len(a.registered)
}
// HandleFor gets the canonical handle for a registered address, which can
// appear at either ID or Robust position.
func (a *Actors) HandleFor(addr address.Address) AddressHandle {
for _, r := range a.registered {
if r.handle.ID == addr || r.handle.Robust == addr {
return r.handle
}
}
a.b.Assert.FailNowf("asked for handle of unknown actor", "actor: %s", addr)
return AddressHandle{} // will never reach here.
}
// InitialBalance returns the initial balance of an actor that was registered
// during preconditions. It matches against both the ID and Robust
// addresses. It records an assertion failure if the actor is unknown.
func (a *Actors) InitialBalance(addr address.Address) abi.TokenAmount {
for _, r := range a.registered {
if r.handle.ID == addr || r.handle.Robust == addr {
return r.initial
}
}
a.b.Assert.FailNowf("asked for initial balance of unknown actor", "actor: %s", addr)
return big.Zero() // will never reach here.
}
// Handles returns the AddressHandles for all registered actors.
func (a *Actors) Handles() []AddressHandle {
ret := make([]AddressHandle, 0, len(a.registered))
for _, r := range a.registered {
ret = append(ret, r.handle)
}
return ret
}
// AccountN creates many account actors of the specified kind, with the
// specified balance, and places their addresses in the supplied AddressHandles.
func (a *Actors) AccountN(typ address.Protocol, balance abi.TokenAmount, handles ...*AddressHandle) {
for _, handle := range handles {
h := a.Account(typ, balance)
*handle = h
}
}
// Account creates a single account actor of the specified kind, with the
// specified balance, and returns its AddressHandle.
func (a *Actors) Account(typ address.Protocol, balance abi.TokenAmount) AddressHandle {
a.b.Assert.In(typ, address.SECP256K1, address.BLS)
var addr address.Address
switch typ {
case address.SECP256K1:
addr = a.b.Wallet.NewSECP256k1Account()
case address.BLS:
addr = a.b.Wallet.NewBLSAccount()
}
actorState := &account.State{Address: addr}
handle := a.CreateActor(builtin.AccountActorCodeID, addr, balance, actorState)
a.registered = append(a.registered, registeredActor{handle, balance})
return handle
}
type MinerActorCfg struct {
SealProofType abi.RegisteredSealProof
PeriodBoundary abi.ChainEpoch
OwnerBalance abi.TokenAmount
}
// Miner creates an owner account, a worker account, and a miner actor managed
// by those initial.
func (a *Actors) Miner(cfg MinerActorCfg) (minerActor, owner, worker AddressHandle) {
owner = a.Account(address.SECP256K1, cfg.OwnerBalance)
worker = a.Account(address.BLS, big.Zero())
// expectedMinerActorIDAddress := chain.MustNewIDAddr(chain.MustIDFromAddress(minerWorkerID) + 1)
// minerActorAddrs := computeInitActorExecReturn(minerWorkerPk, 0, 1, expectedMinerActorIDAddress)
ss, err := cfg.SealProofType.SectorSize()
a.b.Assert.NoError(err, "seal proof sector size")
ps, err := cfg.SealProofType.WindowPoStPartitionSectors()
a.b.Assert.NoError(err, "seal proof window PoSt partition sectors")
mi := &miner.MinerInfo{
Owner: owner.ID,
Worker: worker.ID,
PendingWorkerKey: nil,
PeerId: abi.PeerID("chain-validation"),
Multiaddrs: nil,
SealProofType: cfg.SealProofType,
SectorSize: ss,
WindowPoStPartitionSectors: ps,
}
infoCid, err := a.b.Stores.CBORStore.Put(context.Background(), mi)
if err != nil {
panic(err)
}
// create the miner actor s.t. it exists in the init actors map
minerState, err := miner.ConstructState(infoCid,
cfg.PeriodBoundary,
EmptyBitfieldCid,
EmptyArrayCid,
EmptyMapCid,
EmptyDeadlinesCid,
EmptyVestingFundsCid,
)
if err != nil {
panic(err)
}
minerActorAddr := worker.NextActorAddress(0, 0)
handle := a.CreateActor(builtin.StorageMinerActorCodeID, minerActorAddr, big.Zero(), minerState)
// assert miner actor has been created, exists in the state tree, and has an entry in the init actor.
// next update the storage power actor to track the miner
var spa power.State
a.ActorState(builtin.StoragePowerActorAddr, &spa)
// set the miners claim
hm, err := adt.AsMap(adt.WrapStore(context.Background(), a.b.Stores.CBORStore), spa.Claims)
if err != nil {
panic(err)
}
// add claim for the miner
err = hm.Put(adt.AddrKey(handle.ID), &power.Claim{
RawBytePower: abi.NewStoragePower(0),
QualityAdjPower: abi.NewTokenAmount(0),
})
if err != nil {
panic(err)
}
// save the claim
spa.Claims, err = hm.Root()
if err != nil {
panic(err)
}
// update miner count
spa.MinerCount += 1
// update storage power actor's state in the tree
_, err = a.b.Stores.CBORStore.Put(context.Background(), &spa)
if err != nil {
panic(err)
}
a.registered = append(a.registered, registeredActor{handle, big.Zero()})
return handle, owner, worker
}
// CreateActor creates an actor in the state tree, of the specified kind, with
// the specified address and balance, and sets its state to the supplied state.
func (a *Actors) CreateActor(code cid.Cid, addr address.Address, balance abi.TokenAmount, state runtime.CBORMarshaler) AddressHandle {
var id address.Address
if addr.Protocol() != address.ID {
var err error
id, err = a.b.StateTree.RegisterNewAddress(addr)
if err != nil {
log.Panicf("register new address for actor: %v", err)
}
}
// Store the new state.
head, err := a.b.StateTree.Store.Put(context.Background(), state)
if err != nil {
panic(err)
}
// Set the actor's head to point to that state.
actr := &types.Actor{
Code: code,
Head: head,
Balance: balance,
}
if err := a.b.StateTree.SetActor(addr, actr); err != nil {
log.Panicf("setting new actor for actor: %v", err)
}
return AddressHandle{id, addr}
}
// ActorState retrieves the state of the supplied actor, and sets it in the
// provided object. It also returns the actor's header from the state tree.
func (a *Actors) ActorState(addr address.Address, out cbg.CBORUnmarshaler) *types.Actor {
actor := a.Header(addr)
err := a.b.StateTree.Store.Get(context.Background(), actor.Head, out)
a.b.Assert.NoError(err, "failed to load state for actorr %s; head=%s", addr, actor.Head)
return actor
}
// Header returns the actor's header from the state tree.
func (a *Actors) Header(addr address.Address) *types.Actor {
actor, err := a.b.StateTree.GetActor(addr)
a.b.Assert.NoError(err, "failed to fetch actor %s from state", addr)
return actor
}
// Balance is a shortcut for Header(addr).Balance.
func (a *Actors) Balance(addr address.Address) abi.TokenAmount {
return a.Header(addr).Balance
}
// Head is a shortcut for Header(addr).Head.
func (a *Actors) Head(addr address.Address) cid.Cid {
return a.Header(addr).Head
}
// Nonce is a shortcut for Header(addr).Nonce.
func (a *Actors) Nonce(addr address.Address) uint64 {
return a.Header(addr).Nonce
}
// Code is a shortcut for Header(addr).Code.
func (a *Actors) Code(addr address.Address) cid.Cid {
return a.Header(addr).Code
}

View File

@ -1,102 +0,0 @@
package builders
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/multiformats/go-varint"
)
// AddressHandle encapsulates both the ID and Robust addresses of an actor.
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)
}
// NextActorAddress predicts the address of the next actor created by this address.
//
// Code is adapted from vm.Runtime#NewActorAddress()
func (ah *AddressHandle) NextActorAddress(nonce, numActorsCreated uint64) address.Address {
var b bytes.Buffer
if err := ah.Robust.MarshalCBOR(&b); err != nil {
panic(aerrors.Fatalf("writing caller address into assert buffer: %v", err))
}
if err := binary.Write(&b, binary.BigEndian, nonce); err != nil {
panic(aerrors.Fatalf("writing nonce address into assert buffer: %v", err))
}
if err := binary.Write(&b, binary.BigEndian, numActorsCreated); err != nil {
panic(aerrors.Fatalf("writing callSeqNum address into assert buffer: %v", err))
}
addr, err := address.NewActorAddress(b.Bytes())
if err != nil {
panic(aerrors.Fatalf("create actor address: %v", err))
}
return addr
}
// MustNewIDAddr returns an address.Address of kind ID.
func MustNewIDAddr(id uint64) address.Address {
addr, err := address.NewIDAddress(id)
if err != nil {
panic(err)
}
return addr
}
// MustNewSECP256K1Addr returns an address.Address of kind secp256k1.
func MustNewSECP256K1Addr(pubkey string) address.Address {
// the pubkey of assert secp256k1 address is hashed for consistent length.
addr, err := address.NewSecp256k1Address([]byte(pubkey))
if err != nil {
panic(err)
}
return addr
}
// MustNewBLSAddr returns an address.Address of kind bls.
func MustNewBLSAddr(seed int64) address.Address {
buf := make([]byte, address.BlsPublicKeyBytes)
binary.PutVarint(buf, seed)
addr, err := address.NewBLSAddress(buf)
if err != nil {
panic(err)
}
return addr
}
// MustNewActorAddr returns an address.Address of kind actor.
func MustNewActorAddr(data string) address.Address {
addr, err := address.NewActorAddress([]byte(data))
if err != nil {
panic(err)
}
return addr
}
// MustIDFromAddress returns the integer ID from an ID address.
func MustIDFromAddress(a address.Address) uint64 {
if a.Protocol() != address.ID {
panic("must be ID protocol address")
}
id, _, err := varint.FromUvarint(a.Payload())
if err != nil {
panic(err)
}
return id
}

View File

@ -1,143 +0,0 @@
package builders
import (
"fmt"
"os"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
)
// Asserter offers useful assertions to verify outcomes at various stages of
// the test vector creation.
type Asserter struct {
*require.Assertions
b *Builder
stage Stage
}
var _ require.TestingT = &Asserter{}
func newAsserter(b *Builder, stage Stage) *Asserter {
a := &Asserter{stage: stage, b: b}
a.Assertions = require.New(a)
return a
}
// In is assert fluid version of require.Contains. It inverts the argument order,
// such that the admissible set can be supplied through assert variadic argument.
func (a *Asserter) In(v interface{}, set ...interface{}) {
a.Contains(set, v, "set %v does not contain element %v", set, v)
}
// BalanceEq verifies that the balance of the address equals the expected one.
func (a *Asserter) BalanceEq(addr address.Address, expected abi.TokenAmount) {
actor, err := a.b.StateTree.GetActor(addr)
a.NoError(err, "failed to fetch actor %s from state", addr)
a.Equal(expected, actor.Balance, "balances mismatch for address %s", addr)
}
// NonceEq verifies that the nonce of the actor equals the expected one.
func (a *Asserter) NonceEq(addr address.Address, expected uint64) {
actor, err := a.b.StateTree.GetActor(addr)
a.NoError(err, "failed to fetch actor %s from state", addr)
a.Equal(expected, actor.Nonce, "expected actor %s nonce: %d, got: %d", addr, expected, actor.Nonce)
}
// HeadEq verifies that the head of the actor equals the expected one.
func (a *Asserter) HeadEq(addr address.Address, expected cid.Cid) {
actor, err := a.b.StateTree.GetActor(addr)
a.NoError(err, "failed to fetch actor %s from state", addr)
a.Equal(expected, actor.Head, "expected actor %s head: %v, got: %v", addr, expected, actor.Head)
}
// ActorExists verifies that the actor exists in the state tree.
func (a *Asserter) ActorExists(addr address.Address) {
_, err := a.b.StateTree.GetActor(addr)
a.NoError(err, "expected no error while looking up actor %s", addr)
}
// ActorExists verifies that the actor is absent from the state tree.
func (a *Asserter) ActorMissing(addr address.Address) {
_, err := a.b.StateTree.GetActor(addr)
a.Error(err, "expected error while looking up actor %s", addr)
}
// EveryMessageResultSatisfies verifies that every message result satisfies the
// provided predicate.
func (a *Asserter) EveryMessageResultSatisfies(predicate ApplyRetPredicate, except ...*ApplicableMessage) {
exceptm := make(map[*ApplicableMessage]struct{}, len(except))
for _, am := range except {
exceptm[am] = struct{}{}
}
for i, m := range a.b.Messages.messages {
if _, ok := exceptm[m]; ok {
continue
}
err := predicate(m.Result)
a.NoError(err, "message result predicate failed on message %d: %s", i, err)
}
}
// EveryMessageSenderSatisfies verifies that the sender actors of the supplied
// messages match a condition.
//
// This function groups ApplicableMessages by sender actor, and calls the
// predicate for each unique sender, passing in the initial state (when
// preconditions were committed), the final state (could be nil), and the
// ApplicableMessages themselves.
func (a *Asserter) MessageSendersSatisfy(predicate ActorPredicate, ams ...*ApplicableMessage) {
bysender := make(map[AddressHandle][]*ApplicableMessage, len(ams))
for _, am := range ams {
h := a.b.Actors.HandleFor(am.Message.From)
bysender[h] = append(bysender[h], am)
}
// we now have messages organized by unique senders.
for sender, amss := range bysender {
// get precondition state
pretree, err := state.LoadStateTree(a.b.Stores.CBORStore, a.b.PreRoot)
a.NoError(err)
prestate, err := pretree.GetActor(sender.Robust)
a.NoError(err)
// get postcondition state; if actor has been deleted, we store a nil.
poststate, _ := a.b.StateTree.GetActor(sender.Robust)
// invoke predicate.
err = predicate(sender, prestate, poststate, amss)
a.NoError(err, "'every sender actor' predicate failed for sender %s: %s", sender, err)
}
}
// EveryMessageSenderSatisfies is sugar for MessageSendersSatisfy(predicate, Messages.All()),
// but supports an exclusion set to restrict the messages that will actually be asserted.
func (a *Asserter) EveryMessageSenderSatisfies(predicate ActorPredicate, except ...*ApplicableMessage) {
ams := a.b.Messages.All()
if len(except) > 0 {
filtered := ams[:0]
for _, ex := range except {
for _, am := range ams {
if am == ex {
continue
}
filtered = append(filtered, am)
}
}
ams = filtered
}
a.MessageSendersSatisfy(predicate, ams...)
}
func (a *Asserter) FailNow() {
os.Exit(1)
}
func (a *Asserter) Errorf(format string, args ...interface{}) {
id := a.b.vector.Meta.ID
stage := a.stage
fmt.Printf("❌ id: %s, stage: %s:"+format, append([]interface{}{id, stage}, args...)...)
}

View File

@ -1,198 +0,0 @@
package builders
import (
"bytes"
"compress/gzip"
"context"
"encoding/json"
"io"
"log"
"os"
"github.com/filecoin-project/lotus/chain/state"
"github.com/ipfs/go-cid"
format "github.com/ipfs/go-ipld-format"
"github.com/ipld/go-car"
"github.com/filecoin-project/oni/tvx/lotus"
"github.com/filecoin-project/oni/tvx/schema"
ostate "github.com/filecoin-project/oni/tvx/state"
)
type Stage string
const (
StagePreconditions = Stage("preconditions")
StageApplies = Stage("applies")
StageChecks = Stage("checks")
StageFinished = Stage("finished")
)
func init() {
// disable logs, as we need a clean stdout output.
log.SetOutput(os.Stderr)
log.SetPrefix(">>> ")
_ = os.Setenv("LOTUS_DISABLE_VM_BUF", "iknowitsabadidea")
}
// TODO use stage.Surgeon with non-proxying blockstore.
type Builder struct {
Actors *Actors
Assert *Asserter
Messages *Messages
Driver *lotus.Driver
PreRoot cid.Cid
PostRoot cid.Cid
CurrRoot cid.Cid
Wallet *Wallet
StateTree *state.StateTree
Stores *ostate.Stores
vector schema.TestVector
stage Stage
}
// MessageVector creates a builder for a message-class vector.
func MessageVector(metadata *schema.Metadata) *Builder {
stores := ostate.NewLocalStores(context.Background())
// Create a brand new state tree.
st, err := state.NewStateTree(stores.CBORStore)
if err != nil {
panic(err)
}
b := &Builder{
stage: StagePreconditions,
Stores: stores,
StateTree: st,
PreRoot: cid.Undef,
Driver: lotus.NewDriver(context.Background()),
}
b.Wallet = newWallet()
b.Assert = newAsserter(b, StagePreconditions)
b.Actors = newActors(b)
b.Messages = &Messages{b: b}
b.vector.Class = schema.ClassMessage
b.vector.Meta = metadata
b.vector.Pre = &schema.Preconditions{}
b.vector.Post = &schema.Postconditions{}
b.initializeZeroState()
return b
}
func (b *Builder) CommitPreconditions() {
if b.stage != StagePreconditions {
panic("called CommitPreconditions at the wrong time")
}
// capture the preroot after applying all preconditions.
preroot := b.FlushState()
b.vector.Pre.Epoch = 0
b.vector.Pre.StateTree = &schema.StateTree{RootCID: preroot}
b.CurrRoot, b.PreRoot = preroot, preroot
b.stage = StageApplies
b.Assert = newAsserter(b, StageApplies)
}
func (b *Builder) CommitApplies() {
if b.stage != StageApplies {
panic("called CommitApplies at the wrong time")
}
for _, am := range b.Messages.All() {
// apply all messages that are pending application.
if am.Result == nil {
b.applyMessage(am)
}
}
b.PostRoot = b.CurrRoot
b.vector.Post.StateTree = &schema.StateTree{RootCID: b.CurrRoot}
b.stage = StageChecks
b.Assert = newAsserter(b, StageChecks)
}
// applyMessage executes the provided message via the driver, records the new
// root, refreshes the state tree, and updates the underlying vector with the
// message and its receipt.
func (b *Builder) applyMessage(am *ApplicableMessage) {
var err error
am.Result, b.CurrRoot, err = b.Driver.ExecuteMessage(am.Message, b.CurrRoot, b.Stores.Blockstore, am.Epoch)
b.Assert.NoError(err)
// replace the state tree.
b.StateTree, err = state.LoadStateTree(b.Stores.CBORStore, b.CurrRoot)
b.Assert.NoError(err)
b.vector.ApplyMessages = append(b.vector.ApplyMessages, schema.Message{
Bytes: MustSerialize(am.Message),
Epoch: &am.Epoch,
})
b.vector.Post.Receipts = append(b.vector.Post.Receipts, &schema.Receipt{
ExitCode: am.Result.ExitCode,
ReturnValue: am.Result.Return,
GasUsed: am.Result.GasUsed,
})
}
func (b *Builder) Finish(w io.Writer) {
if b.stage != StageChecks {
panic("called Finish at the wrong time")
}
out := new(bytes.Buffer)
gw := gzip.NewWriter(out)
if err := b.WriteCAR(gw, b.vector.Pre.StateTree.RootCID, b.vector.Post.StateTree.RootCID); err != nil {
panic(err)
}
if err := gw.Flush(); err != nil {
panic(err)
}
if err := gw.Close(); err != nil {
panic(err)
}
b.vector.CAR = out.Bytes()
b.stage = StageFinished
b.Assert = nil
encoder := json.NewEncoder(w)
if err := encoder.Encode(b.vector); err != nil {
panic(err)
}
}
// WriteCAR recursively writes the tree referenced by the root as assert CAR into the
// supplied io.Writer.
//
// TODO use state.Surgeon instead. (This is assert copy of Surgeon#WriteCAR).
func (b *Builder) WriteCAR(w io.Writer, roots ...cid.Cid) error {
carWalkFn := func(nd format.Node) (out []*format.Link, err error) {
for _, link := range nd.Links() {
if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed {
continue
}
out = append(out, link)
}
return out, nil
}
return car.WriteCarWithWalker(context.Background(), b.Stores.DAGService, roots, w, carWalkFn)
}
func (b *Builder) FlushState() cid.Cid {
preroot, err := b.StateTree.Flush(context.Background())
if err != nil {
panic(err)
}
return preroot
}

View File

@ -1,59 +0,0 @@
package builders
import (
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/oni/tvx/lotus"
)
const (
overuseNum = 11
overuseDen = 10
)
// CalculateDeduction returns the balance that shall be deducted from the
// sender's account as a result of applying this message.
func CalculateDeduction(am *ApplicableMessage) big.Int {
if am.Result.GasUsed == 0 {
return big.Zero()
}
m := am.Message
minerReward := GetMinerReward(m.GasLimit, m.GasPremium) // goes to the miner
burn := CalculateBurn(m.GasLimit, am.Result.GasUsed) // vanishes
deducted := big.Add(minerReward, burn) // sum of gas accrued
if am.Result.ExitCode.IsSuccess() {
deducted = big.Add(deducted, m.Value) // message value
}
return deducted
}
// GetMinerReward returns the amount that the miner gets to keep, which is
func GetMinerReward(gasLimit int64, gasPremium abi.TokenAmount) abi.TokenAmount {
return big.Mul(big.NewInt(gasLimit), gasPremium)
}
func GetMinerPenalty(gasLimit int64) big.Int {
return big.Mul(lotus.BaseFee, big.NewInt(gasLimit))
}
// CalculateBurn calcualtes the amount that will be burnt, a function of the
// gas limit and the gas actually used.
func CalculateBurn(gasLimit int64, gasUsed int64) big.Int {
over := gasLimit - (overuseNum*gasUsed)/overuseDen
if over < 0 {
over = 0
}
if over > gasUsed {
over = gasUsed
}
overestimateGas := big.NewInt(gasLimit - gasUsed)
overestimateGas = big.Mul(overestimateGas, big.NewInt(over))
overestimateGas = big.Div(overestimateGas, big.NewInt(gasUsed))
totalBurnGas := big.Add(overestimateGas, big.NewInt(gasUsed))
return big.Mul(lotus.BaseFee, totalBurnGas)
}

View File

@ -1,186 +0,0 @@
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>
// directory where test vector JSON files will be saved; if omitted,
// vectors will be written to stdout.
//
// -f <regex>
// 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:
// <output_dir>/<group>--<vector_id>.json
type Generator struct {
OutputPath string
Filter *regexp.Regexp
wg sync.WaitGroup
}
// genData is the generation data to stamp into vectors.
// TODO in the future this should contain the commit of this tool and
// the builder api.
var genData = schema.GenerationData{
Source: "script",
Version: "v0",
}
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|os.O_TRUNC, 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)
// stamp with our generation data.
b.Metadata.Gen = genData
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
}

View File

@ -1,168 +0,0 @@
package builders
import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
)
// TypedCall represents a call to a known built-in actor kind.
type TypedCall func() (method abi.MethodNum, params []byte)
// Messages accumulates the messages to be executed within the test vector.
type Messages struct {
b *Builder
defaults msgOpts
messages []*ApplicableMessage
}
// SetDefaults sets default options for all messages.
func (m *Messages) SetDefaults(opts ...MsgOpt) *Messages {
for _, opt := range opts {
opt(&m.defaults)
}
return m
}
// ApplicableMessage represents a message to be applied on the test vector.
type ApplicableMessage struct {
Epoch abi.ChainEpoch
Message *types.Message
Result *vm.ApplyRet
}
func (m *Messages) Sugar() *sugarMsg {
return &sugarMsg{m}
}
// All returns all ApplicableMessages that have been accumulated, in the same
// order they were added.
func (m *Messages) All() []*ApplicableMessage {
cpy := make([]*ApplicableMessage, len(m.messages))
copy(cpy, m.messages)
return cpy
}
// Typed adds a typed call to this message accumulator.
func (m *Messages) Typed(from, to address.Address, typedm TypedCall, opts ...MsgOpt) *ApplicableMessage {
method, params := typedm()
return m.Raw(from, to, method, params, opts...)
}
// Raw adds a raw message to this message accumulator.
func (m *Messages) Raw(from, to address.Address, method abi.MethodNum, params []byte, opts ...MsgOpt) *ApplicableMessage {
options := m.defaults
for _, opt := range opts {
opt(&options)
}
msg := &types.Message{
To: to,
From: from,
Nonce: options.nonce,
Value: options.value,
Method: method,
Params: params,
GasLimit: options.gasLimit,
GasFeeCap: options.gasFeeCap,
GasPremium: options.gasPremium,
}
am := &ApplicableMessage{
Epoch: options.epoch,
Message: msg,
}
m.messages = append(m.messages, am)
return am
}
// ApplyOne applies the provided message. The following constraints are checked:
// - all previous messages have been applied.
// - we know about this message (i.e. it has been added through Typed, Raw or Sugar).
func (m *Messages) ApplyOne(am *ApplicableMessage) {
var found bool
for i, other := range m.messages {
if other.Result != nil {
// message has been applied, continue.
continue
}
if am == other {
// we have scanned all preceding messages, and verified they had been applied.
// we are ready to perform the application.
found = true
break
}
// verify that preceding messages have been applied.
// this will abort if unsatisfied.
m.b.Assert.Nil(other.Result, "preceding messages must have been applied when calling Apply*; index of first unapplied: %d", i)
}
m.b.Assert.True(found, "ApplicableMessage not found")
m.b.applyMessage(am)
}
// ApplyN calls ApplyOne for the supplied messages, in the order they are passed.
// The constraints described in ApplyOne apply.
func (m *Messages) ApplyN(ams ...*ApplicableMessage) {
for _, am := range ams {
m.ApplyOne(am)
}
}
type msgOpts struct {
nonce uint64
value big.Int
gasLimit int64
gasFeeCap abi.TokenAmount
gasPremium abi.TokenAmount
epoch abi.ChainEpoch
}
// MsgOpt is an option configuring message value, gas parameters, execution
// epoch, and other elements.
type MsgOpt func(*msgOpts)
// Value sets a value on a message.
func Value(value big.Int) MsgOpt {
return func(opts *msgOpts) {
opts.value = value
}
}
// Nonce sets the nonce of a message.
func Nonce(n uint64) MsgOpt {
return func(opts *msgOpts) {
opts.nonce = n
}
}
// GasLimit sets the gas limit of a message.
func GasLimit(limit int64) MsgOpt {
return func(opts *msgOpts) {
opts.gasLimit = limit
}
}
// GasFeeCap sets the gas fee cap of a message.
func GasFeeCap(feeCap int64) MsgOpt {
return func(opts *msgOpts) {
opts.gasFeeCap = big.NewInt(feeCap)
}
}
// GasPremium sets the gas premium of a message.
func GasPremium(premium int64) MsgOpt {
return func(opts *msgOpts) {
opts.gasPremium = big.NewInt(premium)
}
}
// Epoch sets the epoch in which a message is to be executed.
func Epoch(epoch abi.ChainEpoch) MsgOpt {
return func(opts *msgOpts) {
opts.epoch = epoch
}
}

View File

@ -1,50 +0,0 @@
package builders
import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"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/power"
"github.com/libp2p/go-libp2p-core/peer"
)
type sugarMsg struct{ m *Messages }
// Transfer enlists a value transfer message.
func (s *sugarMsg) Transfer(from, to address.Address, opts ...MsgOpt) *ApplicableMessage {
return s.m.Typed(from, to, Transfer(), opts...)
}
func (s *sugarMsg) CreatePaychActor(from, to address.Address, opts ...MsgOpt) *ApplicableMessage {
ctorparams := &paych.ConstructorParams{
From: from,
To: to,
}
return s.m.Typed(from, builtin.InitActorAddr, InitExec(&init_.ExecParams{
CodeCID: builtin.PaymentChannelActorCodeID,
ConstructorParams: MustSerialize(ctorparams),
}), opts...)
}
func (s *sugarMsg) CreateMultisigActor(from address.Address, params *multisig.ConstructorParams, opts ...MsgOpt) *ApplicableMessage {
return s.m.Typed(from, builtin.InitActorAddr, InitExec(&init_.ExecParams{
CodeCID: builtin.MultisigActorCodeID,
ConstructorParams: MustSerialize(params),
}), opts...)
}
func (s *sugarMsg) CreateMinerActor(owner, worker address.Address, sealProofType abi.RegisteredSealProof, pid peer.ID, maddrs []abi.Multiaddrs, opts ...MsgOpt) *ApplicableMessage {
params := &power.CreateMinerParams{
Worker: worker,
Owner: owner,
SealProofType: sealProofType,
Peer: abi.PeerID(pid),
Multiaddrs: maddrs,
}
return s.m.Typed(owner, builtin.StoragePowerActorAddr, PowerCreateMiner(params), opts...)
}

View File

@ -1,379 +0,0 @@
package builders
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/puppet"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/cron"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"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/power"
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
)
func Transfer() TypedCall {
return func() (method abi.MethodNum, params []byte) {
return builtin.MethodSend, []byte{}
}
}
// ----------------------------------------------------------------------------
// | ACCOUNT
// ----------------------------------------------------------------------------
func AccountConstructor(params *address.Address) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsAccount.Constructor, MustSerialize(params)
}
}
func AccountPubkeyAddress(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsAccount.PubkeyAddress, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | MARKET
// ----------------------------------------------------------------------------
func MarketConstructor(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.Constructor, MustSerialize(params)
}
}
func MarketAddBalance(params *address.Address) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.AddBalance, MustSerialize(params)
}
}
func MarketWithdrawBalance(params *market.WithdrawBalanceParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.WithdrawBalance, MustSerialize(params)
}
}
func MarketPublishStorageDeals(params *market.PublishStorageDealsParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.PublishStorageDeals, MustSerialize(params)
}
}
func MarketVerifyDealsForActivation(params *market.VerifyDealsForActivationParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.VerifyDealsForActivation, MustSerialize(params)
}
}
func MarketActivateDeals(params *market.ActivateDealsParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.ActivateDeals, MustSerialize(params)
}
}
func MarketOnMinerSectorsTerminate(params *market.OnMinerSectorsTerminateParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.OnMinerSectorsTerminate, MustSerialize(params)
}
}
func MarketComputeDataCommitment(params *market.ComputeDataCommitmentParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.ComputeDataCommitment, MustSerialize(params)
}
}
func MarketCronTick(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMarket.CronTick, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | MINER
// ----------------------------------------------------------------------------
func MinerConstructor(params *power.MinerConstructorParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.Constructor, MustSerialize(params)
}
}
func MinerControlAddresses(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ControlAddresses, MustSerialize(params)
}
}
func MinerChangeWorkerAddress(params *miner.ChangeWorkerAddressParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ChangeWorkerAddress, MustSerialize(params)
}
}
func MinerChangePeerID(params *miner.ChangePeerIDParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ChangePeerID, MustSerialize(params)
}
}
func MinerSubmitWindowedPoSt(params *miner.SubmitWindowedPoStParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.SubmitWindowedPoSt, MustSerialize(params)
}
}
func MinerPreCommitSector(params *miner.SectorPreCommitInfo) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.PreCommitSector, MustSerialize(params)
}
}
func MinerProveCommitSector(params *miner.ProveCommitSectorParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ProveCommitSector, MustSerialize(params)
}
}
func MinerExtendSectorExpiration(params *miner.ExtendSectorExpirationParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ExtendSectorExpiration, MustSerialize(params)
}
}
func MinerTerminateSectors(params *miner.TerminateSectorsParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.TerminateSectors, MustSerialize(params)
}
}
func MinerDeclareFaults(params *miner.DeclareFaultsParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.DeclareFaults, MustSerialize(params)
}
}
func MinerDeclareFaultsRecovered(params *miner.DeclareFaultsRecoveredParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.DeclareFaultsRecovered, MustSerialize(params)
}
}
func MinerOnDeferredCronEvent(params *miner.CronEventPayload) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.OnDeferredCronEvent, MustSerialize(params)
}
}
func MinerCheckSectorProven(params *miner.CheckSectorProvenParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.CheckSectorProven, MustSerialize(params)
}
}
func MinerAddLockedFund(params *big.Int) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.AddLockedFund, MustSerialize(params)
}
}
func MinerReportConsensusFault(params *miner.ReportConsensusFaultParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ReportConsensusFault, MustSerialize(params)
}
}
func MinerWithdrawBalance(params *miner.WithdrawBalanceParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.WithdrawBalance, MustSerialize(params)
}
}
func MinerConfirmSectorProofsValid(params *builtin.ConfirmSectorProofsParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ConfirmSectorProofsValid, MustSerialize(params)
}
}
func MinerChangeMultiaddrs(params *miner.ChangeMultiaddrsParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMiner.ChangeMultiaddrs, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | MULTISIG
// ----------------------------------------------------------------------------
func MultisigConstructor(params *multisig.ConstructorParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.Constructor, MustSerialize(params)
}
}
func MultisigPropose(params *multisig.ProposeParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.Propose, MustSerialize(params)
}
}
func MultisigApprove(params *multisig.TxnIDParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.Approve, MustSerialize(params)
}
}
func MultisigCancel(params *multisig.TxnIDParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.Cancel, MustSerialize(params)
}
}
func MultisigAddSigner(params *multisig.AddSignerParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.AddSigner, MustSerialize(params)
}
}
func MultisigRemoveSigner(params *multisig.RemoveSignerParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.RemoveSigner, MustSerialize(params)
}
}
func MultisigSwapSigner(params *multisig.SwapSignerParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.SwapSigner, MustSerialize(params)
}
}
func MultisigChangeNumApprovalsThreshold(params *multisig.ChangeNumApprovalsThresholdParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsMultisig.ChangeNumApprovalsThreshold, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | POWER
// ----------------------------------------------------------------------------
func PowerConstructor(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.Constructor, MustSerialize(params)
}
}
func PowerCreateMiner(params *power.CreateMinerParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.CreateMiner, MustSerialize(params)
}
}
func PowerUpdateClaimedPower(params *power.UpdateClaimedPowerParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.UpdateClaimedPower, MustSerialize(params)
}
}
func PowerEnrollCronEvent(params *power.EnrollCronEventParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.EnrollCronEvent, MustSerialize(params)
}
}
func PowerOnEpochTickEnd(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.OnEpochTickEnd, MustSerialize(params)
}
}
func PowerUpdatePledgeTotal(params *big.Int) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.UpdatePledgeTotal, MustSerialize(params)
}
}
func PowerOnConsensusFault(params *big.Int) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.OnConsensusFault, MustSerialize(params)
}
}
func PowerSubmitPoRepForBulkVerify(params *abi.SealVerifyInfo) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.SubmitPoRepForBulkVerify, MustSerialize(params)
}
}
func PowerCurrentTotalPower(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPower.CurrentTotalPower, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | REWARD
// ----------------------------------------------------------------------------
func RewardConstructor(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsReward.Constructor, MustSerialize(params)
}
}
func RewardAwardBlockReward(params *reward.AwardBlockRewardParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsReward.AwardBlockReward, MustSerialize(params)
}
}
func RewardThisEpochReward(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsReward.ThisEpochReward, MustSerialize(params)
}
}
func RewardUpdateNetworkKPI(params *big.Int) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsReward.UpdateNetworkKPI, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | PAYCH
// ----------------------------------------------------------------------------
func PaychConstructor(params *paych.ConstructorParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPaych.Constructor, MustSerialize(params)
}
}
func PaychUpdateChannelState(params *paych.UpdateChannelStateParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPaych.UpdateChannelState, MustSerialize(params)
}
}
func PaychSettle(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPaych.Settle, MustSerialize(params)
}
}
func PaychCollect(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsPaych.Collect, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | CRON
// ----------------------------------------------------------------------------
func CronConstructor(params *cron.ConstructorParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsCron.Constructor, MustSerialize(params)
}
}
func CronEpochTick(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsCron.EpochTick, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | INIT
// ----------------------------------------------------------------------------
func InitConstructor(params *init_.ConstructorParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsInit.Constructor, MustSerialize(params)
}
}
func InitExec(params *init_.ExecParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return builtin.MethodsInit.Exec, MustSerialize(params)
}
}
// ----------------------------------------------------------------------------
// | PUPPET
// ----------------------------------------------------------------------------
func PuppetConstructor(params *adt.EmptyValue) TypedCall {
return func() (abi.MethodNum, []byte) {
return puppet.MethodsPuppet.Constructor, MustSerialize(params)
}
}
func PuppetSend(params *puppet.SendParams) TypedCall {
return func() (abi.MethodNum, []byte) {
return puppet.MethodsPuppet.Send, MustSerialize(params)
}
}

View File

@ -1,76 +0,0 @@
package builders
import (
"fmt"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
)
// ApplyRetPredicate evaluates a given condition against the result of a
// message application.
type ApplyRetPredicate func(ret *vm.ApplyRet) error
// OptionalActor is a marker type to warn that the value can be nil.
type OptionalActor = types.Actor
// ActorPredicate evaluates whether the actor that participates in the provided
// messages satisfies a given condition. The initial state (after preconditions)
// and final state (after applies) are supplied.
type ActorPredicate func(handle AddressHandle, initial *OptionalActor, final *OptionalActor, amss []*ApplicableMessage) error
// ExitCode returns an ApplyRetPredicate that passes if the exit code of the
// message execution matches the argument.
func ExitCode(expect exitcode.ExitCode) ApplyRetPredicate {
return func(ret *vm.ApplyRet) error {
if ret.ExitCode == expect {
return nil
}
return fmt.Errorf("message exit code was %d; expected %d", ret.ExitCode, expect)
}
}
// BalanceUpdated returns a ActorPredicate that checks whether the balance
// of the actor has been deducted the gas cost and the outgoing value transfers,
// and has been increased by the offset (or decreased, if the argument is negative).
func BalanceUpdated(offset abi.TokenAmount) ActorPredicate {
return func(handle AddressHandle, initial *types.Actor, final *OptionalActor, amss []*ApplicableMessage) error {
if initial == nil || final == nil {
return fmt.Errorf("BalanceUpdated predicate expected non-nil state")
}
// accumulate all balance deductions: ∑(burnt + premium + transferred value)
deducted := big.Zero()
for _, am := range amss {
d := CalculateDeduction(am)
deducted = big.Add(deducted, d)
}
expected := big.Sub(initial.Balance, deducted)
expected = big.Add(expected, offset)
if !final.Balance.Equals(expected) {
return fmt.Errorf("expected balance %s, was: %s", expected, final.Balance)
}
return nil
}
}
// NonceUpdated returns a ActorPredicate that checks whether the nonce
// of the actor has been updated to the nonce of the last message + 1.
func NonceUpdated() ActorPredicate {
return func(handle AddressHandle, initial *types.Actor, final *OptionalActor, amss []*ApplicableMessage) error {
if initial == nil || final == nil {
return fmt.Errorf("BalanceUpdated predicate expected non-nil state")
}
// the nonce should be equal to the nonce of the last message + 1.
last := amss[len(amss)-1]
if expected, actual := last.Message.Nonce+1, final.Nonce; expected != actual {
return fmt.Errorf("for actor: %s: expected nonce %d, got %d", handle, expected, actual)
}
return nil
}
}

View File

@ -1,38 +0,0 @@
package builders
import (
"bytes"
"fmt"
cbg "github.com/whyrusleeping/cbor-gen"
)
func MustSerialize(i cbg.CBORMarshaler) []byte {
out, err := Serialize(i)
if err != nil {
panic(err)
}
return out
}
func Serialize(i cbg.CBORMarshaler) ([]byte, error) {
buf := new(bytes.Buffer)
if err := i.MarshalCBOR(buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func MustDeserialize(b []byte, out interface{}) {
if err := Deserialize(b, out); err != nil {
panic(err)
}
}
func Deserialize(b []byte, out interface{}) error {
um, ok := out.(cbg.CBORUnmarshaler)
if !ok {
return fmt.Errorf("type %T does not implement UnmarshalCBOR", out)
}
return um.UnmarshalCBOR(bytes.NewReader(b))
}

View File

@ -1,172 +0,0 @@
package builders
import (
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
abi_spec "github.com/filecoin-project/specs-actors/actors/abi"
big_spec "github.com/filecoin-project/specs-actors/actors/abi/big"
builtin_spec "github.com/filecoin-project/specs-actors/actors/builtin"
account_spec "github.com/filecoin-project/specs-actors/actors/builtin/account"
cron_spec "github.com/filecoin-project/specs-actors/actors/builtin/cron"
init_spec "github.com/filecoin-project/specs-actors/actors/builtin/init"
market_spec "github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
power_spec "github.com/filecoin-project/specs-actors/actors/builtin/power"
reward_spec "github.com/filecoin-project/specs-actors/actors/builtin/reward"
"github.com/filecoin-project/specs-actors/actors/builtin/system"
runtime_spec "github.com/filecoin-project/specs-actors/actors/runtime"
adt_spec "github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/ipfs/go-cid"
)
const (
totalFilecoin = 2_000_000_000
filecoinPrecision = 1_000_000_000_000_000_000
)
var (
TotalNetworkBalance = big_spec.Mul(big_spec.NewInt(totalFilecoin), big_spec.NewInt(filecoinPrecision))
EmptyReturnValue = []byte{}
)
var (
// initialized by calling initializeStoreWithAdtRoots
EmptyArrayCid cid.Cid
EmptyDeadlinesCid cid.Cid
EmptyMapCid cid.Cid
EmptyMultiMapCid cid.Cid
EmptyBitfieldCid cid.Cid
EmptyVestingFundsCid cid.Cid
)
const (
TestSealProofType = abi_spec.RegisteredSealProof_StackedDrg2KiBV1
)
func (b *Builder) initializeZeroState() {
if err := insertEmptyStructures(b.Stores.ADTStore); err != nil {
panic(err)
}
type ActorState struct {
Addr address.Address
Balance abi_spec.TokenAmount
Code cid.Cid
State runtime_spec.CBORMarshaler
}
var actors []ActorState
actors = append(actors, ActorState{
Addr: builtin_spec.InitActorAddr,
Balance: big_spec.Zero(),
Code: builtin_spec.InitActorCodeID,
State: init_spec.ConstructState(EmptyMapCid, "chain-validation"),
})
zeroRewardState := reward_spec.ConstructState(big_spec.Zero())
zeroRewardState.ThisEpochReward = big_spec.NewInt(1e17)
actors = append(actors, ActorState{
Addr: builtin_spec.RewardActorAddr,
Balance: TotalNetworkBalance,
Code: builtin_spec.RewardActorCodeID,
State: zeroRewardState,
})
actors = append(actors, ActorState{
Addr: builtin_spec.BurntFundsActorAddr,
Balance: big_spec.Zero(),
Code: builtin_spec.AccountActorCodeID,
State: &account_spec.State{Address: builtin_spec.BurntFundsActorAddr},
})
actors = append(actors, ActorState{
Addr: builtin_spec.StoragePowerActorAddr,
Balance: big_spec.Zero(),
Code: builtin_spec.StoragePowerActorCodeID,
State: power_spec.ConstructState(EmptyMapCid, EmptyMultiMapCid),
})
actors = append(actors, ActorState{
Addr: builtin_spec.StorageMarketActorAddr,
Balance: big_spec.Zero(),
Code: builtin_spec.StorageMarketActorCodeID,
State: &market_spec.State{
Proposals: EmptyArrayCid,
States: EmptyArrayCid,
PendingProposals: EmptyMapCid,
EscrowTable: EmptyMapCid,
LockedTable: EmptyMapCid,
NextID: abi_spec.DealID(0),
DealOpsByEpoch: EmptyMultiMapCid,
LastCron: 0,
},
})
actors = append(actors, ActorState{
Addr: builtin_spec.SystemActorAddr,
Balance: big_spec.Zero(),
Code: builtin_spec.SystemActorCodeID,
State: &system.State{},
})
actors = append(actors, ActorState{
Addr: builtin_spec.CronActorAddr,
Balance: big_spec.Zero(),
Code: builtin_spec.CronActorCodeID,
State: &cron_spec.State{Entries: []cron_spec.Entry{
{
Receiver: builtin_spec.StoragePowerActorAddr,
MethodNum: builtin_spec.MethodsPower.OnEpochTickEnd,
},
}},
})
for _, act := range actors {
_ = b.Actors.CreateActor(act.Code, act.Addr, act.Balance, act.State)
}
}
func insertEmptyStructures(store adt_spec.Store) error {
var err error
_, err = store.Put(context.TODO(), []struct{}{})
if err != nil {
return err
}
EmptyArrayCid, err = adt_spec.MakeEmptyArray(store).Root()
if err != nil {
return err
}
EmptyMapCid, err = adt_spec.MakeEmptyMap(store).Root()
if err != nil {
return err
}
EmptyMultiMapCid, err = adt_spec.MakeEmptyMultimap(store).Root()
if err != nil {
return err
}
EmptyDeadlinesCid, err = store.Put(context.TODO(), miner.ConstructDeadline(EmptyArrayCid))
if err != nil {
return err
}
emptyBitfield := bitfield.NewFromSet(nil)
EmptyBitfieldCid, err = store.Put(context.TODO(), emptyBitfield)
if err != nil {
return err
}
EmptyVestingFundsCid, err = store.Put(context.Background(), miner.ConstructVestingFunds())
if err != nil {
return err
}
return nil
}

View File

@ -1,105 +0,0 @@
package builders
import (
"fmt"
"math/rand"
"github.com/minio/blake2b-simd"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-crypto"
acrypto "github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/chain/types"
)
type Wallet struct {
// Private keys by address
keys map[address.Address]*wallet.Key
// Seed for deterministic secp key generation.
secpSeed int64
// Seed for deterministic bls key generation.
blsSeed int64 // nolint: structcheck
}
func newWallet() *Wallet {
return &Wallet{
keys: make(map[address.Address]*wallet.Key),
secpSeed: 0,
}
}
func (w *Wallet) NewSECP256k1Account() address.Address {
secpKey := w.newSecp256k1Key()
w.keys[secpKey.Address] = secpKey
return secpKey.Address
}
func (w *Wallet) NewBLSAccount() address.Address {
blsKey := w.newBLSKey()
w.keys[blsKey.Address] = blsKey
return blsKey.Address
}
func (w *Wallet) Sign(addr address.Address, data []byte) (acrypto.Signature, error) {
ki, ok := w.keys[addr]
if !ok {
return acrypto.Signature{}, fmt.Errorf("unknown address %v", addr)
}
var sigType acrypto.SigType
if ki.Type == wallet.KTSecp256k1 {
sigType = acrypto.SigTypeBLS
hashed := blake2b.Sum256(data)
sig, err := crypto.Sign(ki.PrivateKey, hashed[:])
if err != nil {
return acrypto.Signature{}, err
}
return acrypto.Signature{
Type: sigType,
Data: sig,
}, nil
} else if ki.Type == wallet.KTBLS {
panic("lotus validator cannot sign BLS messages")
} else {
panic("unknown signature type")
}
}
func (w *Wallet) newSecp256k1Key() *wallet.Key {
randSrc := rand.New(rand.NewSource(w.secpSeed))
prv, err := crypto.GenerateKeyFromSeed(randSrc)
if err != nil {
panic(err)
}
w.secpSeed++
key, err := wallet.NewKey(types.KeyInfo{
Type: wallet.KTSecp256k1,
PrivateKey: prv,
})
if err != nil {
panic(err)
}
return key
}
func (w *Wallet) newBLSKey() *wallet.Key {
// FIXME: bls needs deterministic key generation
//sk := ffi.PrivateKeyGenerate(s.blsSeed)
// s.blsSeed++
sk := [32]byte{}
sk[0] = uint8(w.blsSeed) // hack to keep gas values determinist
w.blsSeed++
key, err := wallet.NewKey(types.KeyInfo{
Type: wallet.KTBLS,
PrivateKey: sk[:],
})
if err != nil {
panic(err)
}
return key
}

View File

@ -1,43 +0,0 @@
package main
import (
. "github.com/filecoin-project/oni/tvx/builders"
"github.com/filecoin-project/specs-actors/actors/abi/big"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/go-address"
)
func sequentialAddresses(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
initial := big.NewInt(1_000_000_000_000_000)
// Set up sender and receiver accounts.
var sender, receiver AddressHandle
v.Actors.AccountN(address.SECP256K1, initial, &sender, &receiver)
v.CommitPreconditions()
// Create 10 payment channels.
for i := uint64(0); i < 10; i++ {
v.Messages.Sugar().CreatePaychActor(sender.Robust, receiver.Robust, Value(big.NewInt(1000)), Nonce(i))
}
v.CommitApplies()
for i, am := range v.Messages.All() {
expectedActorAddr := AddressHandle{
ID: MustNewIDAddr(MustIDFromAddress(receiver.ID) + uint64(i) + 1),
Robust: sender.NextActorAddress(am.Message.Nonce, 0),
}
// Verify that the return contains the expected addresses.
var ret init_.ExecReturn
MustDeserialize(am.Result.Return, &ret)
v.Assert.Equal(expectedActorAddr.Robust, ret.RobustAddress)
v.Assert.Equal(expectedActorAddr.ID, ret.IDAddress)
}
v.Assert.EveryMessageSenderSatisfies(BalanceUpdated(big.Zero()))
v.Assert.EveryMessageSenderSatisfies(NonceUpdated())
}

View File

@ -1,82 +0,0 @@
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"
)
func main() {
g := NewGenerator()
g.MessageVectorGroup("addresses",
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "sequential-10",
Version: "v1",
Desc: "actor addresses are sequential",
},
Func: sequentialAddresses,
},
)
g.MessageVectorGroup("on_transfer",
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-create-secp256k1",
Version: "v1",
},
Func: actorCreationOnTransfer(actorCreationOnTransferParams{
senderType: address.SECP256K1,
senderBal: abi.NewTokenAmount(1_000_000_000_000_000),
receiverAddr: MustNewSECP256K1Addr("publickeyfoo"),
amount: abi.NewTokenAmount(10_000),
exitCode: exitcode.Ok,
}),
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-create-bls",
Version: "v1",
},
Func: actorCreationOnTransfer(actorCreationOnTransferParams{
senderType: address.SECP256K1,
senderBal: abi.NewTokenAmount(1_000_000_000_000_000),
receiverAddr: MustNewBLSAddr(1),
amount: abi.NewTokenAmount(10_000),
exitCode: exitcode.Ok,
}),
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-secp256k1-insufficient-balance",
Version: "v1",
},
Func: actorCreationOnTransfer(actorCreationOnTransferParams{
senderType: address.SECP256K1,
senderBal: abi.NewTokenAmount(9_999),
receiverAddr: MustNewSECP256K1Addr("publickeyfoo"),
amount: abi.NewTokenAmount(10_000),
exitCode: exitcode.SysErrSenderStateInvalid,
}),
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-bls-insufficient-balance",
Version: "v1",
},
Func: actorCreationOnTransfer(actorCreationOnTransferParams{
senderType: address.SECP256K1,
senderBal: abi.NewTokenAmount(9_999),
receiverAddr: MustNewBLSAddr(1),
amount: abi.NewTokenAmount(10_000),
exitCode: exitcode.SysErrSenderStateInvalid,
}),
},
)
g.Wait()
}

View File

@ -1,41 +0,0 @@
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 actorCreationOnTransferParams struct {
senderType address.Protocol
senderBal abi.TokenAmount
receiverAddr address.Address
amount abi.TokenAmount
exitCode exitcode.ExitCode
}
func actorCreationOnTransfer(params actorCreationOnTransferParams) func(v *Builder) {
return func(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
// Set up sender account.
sender := v.Actors.Account(params.senderType, params.senderBal)
v.CommitPreconditions()
// Perform the transfer.
v.Messages.Sugar().Transfer(sender.ID, params.receiverAddr, 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(params.receiverAddr, params.amount)
}
}
}

View File

@ -1,49 +0,0 @@
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), GasPremium(1), GasFeeCap(200))
// 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)
}

View File

@ -1,58 +0,0 @@
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), GasPremium(1), GasFeeCap(200))
alice := v.Actors.Account(address.SECP256K1, balance1T)
v.CommitPreconditions()
v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(0), GasPremium(1), GasLimit(8))
v.CommitApplies()
v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas))
}
func failCoverOnChainSizeGasCost(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
alice := v.Actors.Account(address.SECP256K1, balance1T)
v.CommitPreconditions()
v.Messages.Sugar().Transfer(alice.ID, alice.ID, Value(transferAmnt), Nonce(0), GasPremium(10), GasLimit(1))
v.CommitApplies()
v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas))
}
func failCoverTransferAccountCreationGasStepwise(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
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), GasPremium(1), GasLimit(tryGas))
nonce++
}
v.CommitApplies()
v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.SysErrOutOfGas), ref)
}

View File

@ -1,38 +0,0 @@
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), GasPremium(1), GasFeeCap(200))
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), GasPremium(1), GasFeeCap(200))
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))
}

View File

@ -1,98 +0,0 @@
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()
}

View File

@ -1,37 +0,0 @@
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), GasPremium(1), GasFeeCap(200))
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), GasPremium(1), GasFeeCap(200))
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))
}

View File

@ -1,51 +0,0 @@
package main
import (
. "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-create",
Version: "v1",
Desc: "multisig actor constructor ok",
},
Func: constructor,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-propose-and-cancel",
Version: "v1",
Desc: "multisig actor propose and cancel ok",
},
Func: proposeAndCancelOk,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-propose-and-approve",
Version: "v1",
Desc: "multisig actor propose, unauthorized proposals+approval, and approval ok",
},
Func: proposeAndApprove,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-add-signer",
Version: "v1",
Desc: "multisig actor accepts only AddSigner messages that go through a reflexive flow",
},
Func: addSigner,
},
)
}

View File

@ -1,305 +0,0 @@
package main
import (
"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"
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/runtime/exitcode"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/go-address"
"github.com/minio/blake2b-simd"
. "github.com/filecoin-project/oni/tvx/builders"
)
func constructor(v *Builder) {
var balance = abi.NewTokenAmount(1_000_000_000_000)
var amount = abi.NewTokenAmount(10)
v.Messages.SetDefaults(GasLimit(gasLimit), GasPremium(1), GasFeeCap(gasFeeCap))
// Set up one account.
alice := v.Actors.Account(address.SECP256K1, balance)
v.CommitPreconditions()
createMultisig(v, alice, []address.Address{alice.ID}, 1, Value(amount), Nonce(0))
v.CommitApplies()
}
func proposeAndCancelOk(v *Builder) {
var (
initial = abi.NewTokenAmount(1_000_000_000_000)
amount = abi.NewTokenAmount(10)
unlockDuration = abi.ChainEpoch(10)
)
v.Messages.SetDefaults(Value(big.Zero()), Epoch(1), GasLimit(gasLimit), GasPremium(1), GasFeeCap(gasFeeCap))
// Set up three accounts: alice and bob (signers), and charlie (outsider).
var alice, bob, charlie AddressHandle
v.Actors.AccountN(address.SECP256K1, initial, &alice, &bob, &charlie)
v.CommitPreconditions()
// create the multisig actor; created by alice.
multisigAddr := createMultisig(v, alice, []address.Address{alice.ID, bob.ID}, 2, Value(amount), Nonce(0))
// alice proposes that charlie should receive 'amount' FIL.
hash := proposeOk(v, proposeOpts{
multisigAddr: multisigAddr,
sender: alice.ID,
recipient: charlie.ID,
amount: amount,
}, Nonce(1))
// bob cancels alice's transaction. This fails as bob did not create alice's transaction.
bobCancelMsg := v.Messages.Typed(bob.ID, multisigAddr, MultisigCancel(&multisig.TxnIDParams{
ID: multisig.TxnID(0),
ProposalHash: hash,
}), Nonce(0))
v.Messages.ApplyOne(bobCancelMsg)
v.Assert.Equal(bobCancelMsg.Result.ExitCode, exitcode.ErrForbidden)
// alice cancels their transaction; charlie doesn't receive any FIL,
// the multisig actor's balance is empty, and the transaction is canceled.
aliceCancelMsg := v.Messages.Typed(alice.ID, multisigAddr, MultisigCancel(&multisig.TxnIDParams{
ID: multisig.TxnID(0),
ProposalHash: hash,
}), Nonce(2))
v.Messages.ApplyOne(aliceCancelMsg)
v.Assert.Equal(exitcode.Ok, aliceCancelMsg.Result.ExitCode)
v.CommitApplies()
// verify balance is untouched.
v.Assert.BalanceEq(multisigAddr, amount)
// reload the multisig state and verify
var multisigState multisig.State
v.Actors.ActorState(multisigAddr, &multisigState)
v.Assert.Equal(&multisig.State{
Signers: []address.Address{alice.ID, bob.ID},
NumApprovalsThreshold: 2,
NextTxnID: 1,
InitialBalance: amount,
StartEpoch: 1,
UnlockDuration: unlockDuration,
PendingTxns: EmptyMapCid,
}, &multisigState)
}
func proposeAndApprove(v *Builder) {
var (
initial = abi.NewTokenAmount(1_000_000_000_000)
amount = abi.NewTokenAmount(10)
unlockDuration = abi.ChainEpoch(10)
)
v.Messages.SetDefaults(Value(big.Zero()), Epoch(1), GasLimit(gasLimit), GasPremium(1), GasFeeCap(gasFeeCap))
// Set up three accounts: alice and bob (signers), and charlie (outsider).
var alice, bob, charlie AddressHandle
v.Actors.AccountN(address.SECP256K1, initial, &alice, &bob, &charlie)
v.CommitPreconditions()
// create the multisig actor; created by alice.
multisigAddr := createMultisig(v, alice, []address.Address{alice.ID, bob.ID}, 2, Value(amount), Nonce(0))
// alice proposes that charlie should receive 'amount' FIL.
hash := proposeOk(v, proposeOpts{
multisigAddr: multisigAddr,
sender: alice.ID,
recipient: charlie.ID,
amount: amount,
}, Nonce(1))
// charlie proposes himself -> fails.
charliePropose := v.Messages.Typed(charlie.ID, multisigAddr,
MultisigPropose(&multisig.ProposeParams{
To: charlie.ID,
Value: amount,
Method: builtin.MethodSend,
Params: nil,
}), Nonce(0))
v.Messages.ApplyOne(charliePropose)
v.Assert.Equal(exitcode.ErrForbidden, charliePropose.Result.ExitCode)
// charlie attempts to accept the pending transaction -> fails.
charlieApprove := v.Messages.Typed(charlie.ID, multisigAddr,
MultisigApprove(&multisig.TxnIDParams{
ID: multisig.TxnID(0),
ProposalHash: hash,
}), Nonce(1))
v.Messages.ApplyOne(charlieApprove)
v.Assert.Equal(exitcode.ErrForbidden, charlieApprove.Result.ExitCode)
// bob approves transfer of 'amount' FIL to charlie.
// epoch is unlockDuration + 1
bobApprove := v.Messages.Typed(bob.ID, multisigAddr,
MultisigApprove(&multisig.TxnIDParams{
ID: multisig.TxnID(0),
ProposalHash: hash,
}), Nonce(0), Epoch(unlockDuration+1))
v.Messages.ApplyOne(bobApprove)
v.Assert.Equal(exitcode.Ok, bobApprove.Result.ExitCode)
v.CommitApplies()
var approveRet multisig.ApproveReturn
MustDeserialize(bobApprove.Result.Return, &approveRet)
v.Assert.Equal(multisig.ApproveReturn{
Applied: true,
Code: 0,
Ret: nil,
}, approveRet)
// assert that the multisig balance has been drained, and charlie's incremented.
v.Assert.BalanceEq(multisigAddr, big.Zero())
v.Assert.MessageSendersSatisfy(BalanceUpdated(amount), charliePropose, charlieApprove)
// reload the multisig state and verify
var multisigState multisig.State
v.Actors.ActorState(multisigAddr, &multisigState)
v.Assert.Equal(&multisig.State{
Signers: []address.Address{alice.ID, bob.ID},
NumApprovalsThreshold: 2,
NextTxnID: 1,
InitialBalance: amount,
StartEpoch: 1,
UnlockDuration: unlockDuration,
PendingTxns: EmptyMapCid,
}, &multisigState)
}
func addSigner(v *Builder) {
var (
initial = abi.NewTokenAmount(1_000_000_000_000)
amount = abi.NewTokenAmount(10)
)
v.Messages.SetDefaults(Value(big.Zero()), Epoch(1), GasLimit(gasLimit), GasPremium(1), GasFeeCap(gasFeeCap))
// Set up three accounts: alice and bob (signers), and charlie (outsider).
var alice, bob, charlie AddressHandle
v.Actors.AccountN(address.SECP256K1, initial, &alice, &bob, &charlie)
v.CommitPreconditions()
// create the multisig actor; created by alice.
multisigAddr := createMultisig(v, alice, []address.Address{alice.ID}, 1, Value(amount), Nonce(0))
addParams := &multisig.AddSignerParams{
Signer: bob.ID,
Increase: false,
}
// attempt to add bob as a signer; this fails because the addition needs to go through
// the multisig flow, as it is subject to the same approval policy.
v.Messages.Typed(alice.ID, multisigAddr, MultisigAddSigner(addParams), Nonce(1))
// go through the multisig wallet.
// since approvals = 1, this auto-approves the transaction.
v.Messages.Typed(alice.ID, multisigAddr, MultisigPropose(&multisig.ProposeParams{
To: multisigAddr,
Value: big.Zero(),
Method: builtin.MethodsMultisig.AddSigner,
Params: MustSerialize(addParams),
}), Nonce(2))
// TODO also exercise the approvals = 2 case with explicit approval.
v.CommitApplies()
// reload the multisig state and verify that bob is now a signer.
var multisigState multisig.State
v.Actors.ActorState(multisigAddr, &multisigState)
v.Assert.Equal(&multisig.State{
Signers: []address.Address{alice.ID, bob.ID},
NumApprovalsThreshold: 1,
NextTxnID: 1,
InitialBalance: amount,
StartEpoch: 1,
UnlockDuration: 10,
PendingTxns: EmptyMapCid,
}, &multisigState)
}
type proposeOpts struct {
multisigAddr address.Address
sender address.Address
recipient address.Address
amount abi.TokenAmount
}
func proposeOk(v *Builder, proposeOpts proposeOpts, opts ...MsgOpt) []byte {
propose := &multisig.ProposeParams{
To: proposeOpts.recipient,
Value: proposeOpts.amount,
Method: builtin.MethodSend,
Params: nil,
}
proposeMsg := v.Messages.Typed(proposeOpts.sender, proposeOpts.multisigAddr, MultisigPropose(propose), opts...)
v.Messages.ApplyOne(proposeMsg)
// verify that the multisig state contains the outstanding TX.
var multisigState multisig.State
v.Actors.ActorState(proposeOpts.multisigAddr, &multisigState)
id := multisig.TxnID(0)
actualTxn := loadMultisigTxn(v, multisigState, id)
v.Assert.Equal(&multisig.Transaction{
To: propose.To,
Value: propose.Value,
Method: propose.Method,
Params: propose.Params,
Approved: []address.Address{proposeOpts.sender},
}, actualTxn)
return makeProposalHash(v, actualTxn)
}
func createMultisig(v *Builder, creator AddressHandle, approvers []address.Address, threshold uint64, opts ...MsgOpt) address.Address {
const unlockDuration = abi.ChainEpoch(10)
// create the multisig actor.
params := &multisig.ConstructorParams{
Signers: approvers,
NumApprovalsThreshold: threshold,
UnlockDuration: unlockDuration,
}
msg := v.Messages.Sugar().CreateMultisigActor(creator.ID, params, opts...)
v.Messages.ApplyOne(msg)
// verify ok
v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok))
// verify the assigned addess is as expected.
var ret init_.ExecReturn
MustDeserialize(msg.Result.Return, &ret)
v.Assert.Equal(creator.NextActorAddress(msg.Message.Nonce, 0), ret.RobustAddress)
handles := v.Actors.Handles()
v.Assert.Equal(MustNewIDAddr(MustIDFromAddress(handles[len(handles)-1].ID)+1), ret.IDAddress)
// the multisig address's balance is incremented by the value sent to it.
v.Assert.BalanceEq(ret.IDAddress, msg.Message.Value)
return ret.IDAddress
}
func loadMultisigTxn(v *Builder, state multisig.State, id multisig.TxnID) *multisig.Transaction {
pending, err := adt.AsMap(v.Stores.ADTStore, state.PendingTxns)
v.Assert.NoError(err)
var actualTxn multisig.Transaction
found, err := pending.Get(id, &actualTxn)
v.Assert.True(found)
v.Assert.NoError(err)
return &actualTxn
}
func makeProposalHash(v *Builder, txn *multisig.Transaction) []byte {
ret, err := multisig.ComputeProposalHash(txn, blake2b.Sum256)
v.Assert.NoError(err)
return ret
}

View File

@ -1,127 +0,0 @@
package main
import (
. "github.com/filecoin-project/oni/tvx/builders"
"github.com/filecoin-project/oni/tvx/schema"
)
func main() {
g := NewGenerator()
g.MessageVectorGroup("nested_sends",
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-basic",
Version: "v1",
Desc: "",
},
Func: nestedSends_OkBasic,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-to-new-actor",
Version: "v1",
Desc: "",
},
Func: nestedSends_OkToNewActor,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-to-new-actor-with-invoke",
Version: "v1",
Desc: "",
},
Func: nestedSends_OkToNewActorWithInvoke,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-recursive",
Version: "v1",
Desc: "",
},
Func: nestedSends_OkRecursive,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "ok-non-cbor-params-with-transfer",
Version: "v1",
Desc: "",
},
Func: nestedSends_OKNonCBORParamsWithTransfer,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-non-existent-id-address",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailNonexistentIDAddress,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-non-existent-actor-address",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailNonexistentActorAddress,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-invalid-method-num-new-actor",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailInvalidMethodNumNewActor,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-invalid-method-num-for-actor",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailInvalidMethodNumForActor,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-missing-params",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailMissingParams,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-mismatch-params",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailMismatchParams,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-inner-abort",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailInnerAbort,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-aborted-exec",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailAbortedExec,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "fail-insufficient-funds-for-transfer-in-inner-send",
Version: "v1",
Desc: "",
},
Func: nestedSends_FailInsufficientFundsForTransferInInnerSend,
},
)
g.Wait()
}

View File

@ -1,358 +0,0 @@
package main
import (
"bytes"
"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"
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"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/filecoin-project/specs-actors/actors/util/adt"
typegen "github.com/whyrusleeping/cbor-gen"
. "github.com/filecoin-project/oni/tvx/builders"
)
var (
acctDefaultBalance = abi.NewTokenAmount(1_000_000_000_000)
multisigBalance = abi.NewTokenAmount(1_000_000_000)
nonce = uint64(1)
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)
}
}
func nestedSends_OkBasic(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(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.Result.Receipt.GasUsed.Big()), nonce+1)
v.Assert.NonceEq(stage.creator, nonce+1)
v.Assert.BalanceEq(stage.creator, big.Sub(big.Add(balanceBefore, amtSent), CalculateDeduction(result)))
}
func nestedSends_OkToNewActor(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Multisig sends to new address.
newAddr := v.Wallet.NewSECP256k1Account()
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce)
v.Assert.BalanceEq(stage.msAddr, big.Sub(multisigBalance, amtSent))
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, CalculateDeduction(result)))
v.Assert.BalanceEq(newAddr, amtSent)
}
func nestedSends_OkToNewActorWithInvoke(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Multisig sends to new address and invokes pubkey method at the same time.
newAddr := v.Wallet.NewSECP256k1Account()
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.Result.Receipt.ReturnValue)
v.Assert.BalanceEq(stage.msAddr, big.Sub(multisigBalance, amtSent))
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, CalculateDeduction(result)))
v.Assert.BalanceEq(newAddr, amtSent)
}
func nestedSends_OkRecursive(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
another := v.Actors.Account(address.SECP256K1, big.Zero())
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
// Multisig sends to itself.
params := multisig.AddSignerParams{
Signer: another.ID,
Increase: false,
}
result := stage.sendOk(stage.msAddr, big.Zero(), builtin.MethodsMultisig.AddSigner, &params, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance)
v.Assert.Equal(big.Sub(balanceBefore, CalculateDeduction(result)), v.Actors.Balance(stage.creator))
var st multisig.State
v.Actors.ActorState(stage.msAddr, &st)
v.Assert.Equal([]address.Address{stage.creator, another.ID}, st.Signers)
}
func nestedSends_OKNonCBORParamsWithTransfer(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := v.Wallet.NewSECP256k1Account()
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, &params, nonce)
v.Assert.BalanceEq(stage.msAddr, big.Sub(multisigBalance, amtSent))
v.Assert.BalanceEq(newAddr, amtSent)
}
func nestedSends_FailNonexistentIDAddress(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := MustNewIDAddr(1234)
amtSent := abi.NewTokenAmount(1)
stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.ActorMissing(newAddr)
}
func nestedSends_FailNonexistentActorAddress(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := MustNewActorAddr("1234")
amtSent := abi.NewTokenAmount(1)
stage.sendOk(newAddr, amtSent, builtin.MethodSend, nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.ActorMissing(newAddr)
}
func nestedSends_FailInvalidMethodNumNewActor(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
newAddr := v.Wallet.NewSECP256k1Account()
amtSent := abi.NewTokenAmount(1)
stage.sendOk(newAddr, amtSent, abi.MethodNum(99), nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.ActorMissing(newAddr)
}
func nestedSends_FailInvalidMethodNumForActor(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(stage.creator)
amtSent := abi.NewTokenAmount(1)
result := stage.sendOk(stage.creator, amtSent, abi.MethodNum(99), nil, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, CalculateDeduction(result))) // Pay gas, don't receive funds.
}
func nestedSends_FailMissingParams(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(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)
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, CalculateDeduction(result)))
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.Equal(1, len(stage.state().Signers)) // No new signers
}
func nestedSends_FailMismatchParams(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
balanceBefore := v.Actors.Balance(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, &params, nonce)
v.Assert.BalanceEq(stage.creator, big.Sub(balanceBefore, CalculateDeduction(result)))
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.Equal(1, len(stage.state().Signers)) // No new signers
}
func nestedSends_FailInnerAbort(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
prevHead := v.Actors.Head(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, &params, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.HeadEq(builtin.RewardActorAddr, prevHead)
}
func nestedSends_FailAbortedExec(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
stage := prepareStage(v, acctDefaultBalance, multisigBalance)
prevHead := v.Actors.Head(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: MustSerialize(&ctorParams),
}
amtSent := abi.NewTokenAmount(1)
stage.sendOk(builtin.InitActorAddr, amtSent, builtin.MethodsInit.Exec, &execParams, nonce)
v.Assert.BalanceEq(stage.msAddr, multisigBalance) // No change.
v.Assert.HeadEq(builtin.InitActorAddr, prevHead) // Init state unchanged.
}
func nestedSends_FailInsufficientFundsForTransferInInnerSend(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
// puppet actor has zero funds
puppetBalance := big.Zero()
_ = v.Actors.CreateActor(puppet.PuppetActorCodeID, PuppetAddress, puppetBalance, &puppet.State{})
alice := v.Actors.Account(address.SECP256K1, acctDefaultBalance)
bob := v.Actors.Account(address.SECP256K1, big.Zero())
v.CommitPreconditions()
// 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)
msg := v.Messages.Typed(alice.ID, PuppetAddress, PuppetSend(&puppet.SendParams{
To: bob.ID,
Value: amtSent,
Method: builtin.MethodSend,
Params: nil,
}), Nonce(0), Value(big.Zero()))
v.Messages.ApplyOne(msg)
v.CommitApplies()
// the outer message should be applied successfully
v.Assert.Equal(exitcode.Ok, msg.Result.ExitCode)
var puppetRet puppet.SendReturn
MustDeserialize(msg.Result.MessageReceipt.Return, &puppetRet)
// the inner message should fail
v.Assert.Equal(exitcode.SysErrInsufficientFunds, puppetRet.Code)
// alice should be charged for the gas cost and bob should have not received any funds.
v.Assert.MessageSendersSatisfy(BalanceUpdated(big.Zero()), msg)
v.Assert.BalanceEq(bob.ID, big.Zero())
}
type msStage struct {
v *Builder
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(v *Builder, creatorBalance, msBalance abi.TokenAmount) *msStage {
// Set up sender and receiver accounts.
creator := v.Actors.Account(address.SECP256K1, creatorBalance)
v.CommitPreconditions()
msg := v.Messages.Sugar().CreateMultisigActor(creator.ID, &multisig.ConstructorParams{
Signers: []address.Address{creator.ID},
NumApprovalsThreshold: 1,
UnlockDuration: 0,
}, Value(msBalance), Nonce(0))
v.Messages.ApplyOne(msg)
v.Assert.Equal(msg.Result.ExitCode, exitcode.Ok)
// Verify init actor return.
var ret init_.ExecReturn
MustDeserialize(msg.Result.Return, &ret)
return &msStage{
v: v,
creator: creator.ID,
msAddr: ret.IDAddress,
}
}
func (s *msStage) sendOk(to address.Address, value abi.TokenAmount, method abi.MethodNum, params runtime.CBORMarshaler, approverNonce uint64) *ApplicableMessage {
buf := bytes.Buffer{}
if params != nil {
err := params.MarshalCBOR(&buf)
if err != nil {
panic(err)
}
}
pparams := multisig.ProposeParams{
To: to,
Value: value,
Method: method,
Params: buf.Bytes(),
}
msg := s.v.Messages.Typed(s.creator, s.msAddr, MultisigPropose(&pparams), Nonce(approverNonce), Value(big.NewInt(0)))
s.v.CommitApplies()
// all messages succeeded.
s.v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok))
return msg
}
func (s *msStage) state() *multisig.State {
var msState multisig.State
s.v.Actors.ActorState(s.msAddr, &msState)
return &msState
}

View File

@ -1,45 +0,0 @@
package main
import (
. "github.com/filecoin-project/oni/tvx/builders"
"github.com/filecoin-project/oni/tvx/schema"
"github.com/filecoin-project/specs-actors/actors/abi"
)
var (
initialBal = abi.NewTokenAmount(1_000_000_000_000)
toSend = abi.NewTokenAmount(10_000)
)
func main() {
g := NewGenerator()
g.MessageVectorGroup("paych",
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "create-ok",
Version: "v1",
Desc: "",
},
Func: happyPathCreate,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "update-ok",
Version: "v1",
Desc: "",
},
Func: happyPathUpdate,
},
&MessageVectorGenItem{
Metadata: &schema.Metadata{
ID: "collect-ok",
Version: "v1",
Desc: "",
},
Func: happyPathCollect,
},
)
g.Wait()
}

View File

@ -1,167 +0,0 @@
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"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"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/specs-actors/actors/util/adt"
. "github.com/filecoin-project/oni/tvx/builders"
)
func happyPathCreate(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
// Set up sender and receiver accounts.
var sender, receiver AddressHandle
v.Actors.AccountN(address.SECP256K1, initialBal, &sender, &receiver)
v.CommitPreconditions()
// Add the constructor message.
createMsg := v.Messages.Sugar().CreatePaychActor(sender.Robust, receiver.Robust, Value(toSend))
v.CommitApplies()
expectedActorAddr := AddressHandle{
ID: MustNewIDAddr(MustIDFromAddress(receiver.ID) + 1),
Robust: sender.NextActorAddress(0, 0),
}
// Verify init actor return.
var ret init_.ExecReturn
MustDeserialize(createMsg.Result.Return, &ret)
v.Assert.Equal(expectedActorAddr.Robust, ret.RobustAddress)
v.Assert.Equal(expectedActorAddr.ID, ret.IDAddress)
// Verify the paych state.
var state paych.State
actor := v.Actors.ActorState(ret.IDAddress, &state)
v.Assert.Equal(sender.ID, state.From)
v.Assert.Equal(receiver.ID, state.To)
v.Assert.Equal(toSend, actor.Balance)
v.Assert.EveryMessageSenderSatisfies(NonceUpdated())
}
func happyPathUpdate(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
var (
timelock = abi.ChainEpoch(0)
lane = uint64(123)
nonce = uint64(1)
amount = big.NewInt(10)
)
// Set up sender and receiver accounts.
var sender, receiver AddressHandle
var paychAddr AddressHandle
v.Actors.AccountN(address.SECP256K1, initialBal, &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(toSend))
// Update the payment channel.
v.Messages.Typed(sender.Robust, paychAddr.Robust, PaychUpdateChannelState(&paych.UpdateChannelStateParams{
Sv: paych.SignedVoucher{
ChannelAddr: paychAddr.Robust,
TimeLockMin: timelock,
TimeLockMax: 0, // TimeLockMax set to 0 means no timeout
Lane: lane,
Nonce: nonce,
Amount: amount,
MinSettleHeight: 0,
Signature: &crypto.Signature{
Type: crypto.SigTypeBLS,
Data: []byte("signature goes here"), // TODO may need to generate an actual signature
},
}}), Nonce(1), Value(big.Zero()))
v.CommitApplies()
// all messages succeeded.
v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok))
// Verify init actor return.
var ret init_.ExecReturn
MustDeserialize(createMsg.Result.Return, &ret)
// Verify the paych state.
var state paych.State
v.Actors.ActorState(ret.RobustAddress, &state)
arr, err := adt.AsArray(v.Stores.ADTStore, state.LaneStates)
v.Assert.NoError(err)
v.Assert.EqualValues(1, arr.Length())
var ls paych.LaneState
found, err := arr.Get(lane, &ls)
v.Assert.NoError(err)
v.Assert.True(found)
v.Assert.Equal(amount, ls.Redeemed)
v.Assert.Equal(nonce, ls.Nonce)
v.Assert.EveryMessageSenderSatisfies(NonceUpdated())
}
func happyPathCollect(v *Builder) {
v.Messages.SetDefaults(GasLimit(1_000_000_000), GasPremium(1), GasFeeCap(200))
// Set up sender and receiver accounts.
var sender, receiver AddressHandle
var paychAddr AddressHandle
v.Actors.AccountN(address.SECP256K1, initialBal, &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(toSend))
// Update the payment channel.
updateMsg := v.Messages.Typed(sender.Robust, paychAddr.Robust, PaychUpdateChannelState(&paych.UpdateChannelStateParams{
Sv: paych.SignedVoucher{
ChannelAddr: paychAddr.Robust,
TimeLockMin: 0,
TimeLockMax: 0, // TimeLockMax set to 0 means no timeout
Lane: 1,
Nonce: 1,
Amount: toSend,
MinSettleHeight: 0,
Signature: &crypto.Signature{
Type: crypto.SigTypeBLS,
Data: []byte("signature goes here"), // TODO may need to generate an actual signature
},
}}), Nonce(1), Value(big.Zero()))
settleMsg := v.Messages.Typed(receiver.Robust, paychAddr.Robust, PaychSettle(nil), Value(big.Zero()), Nonce(0))
// advance the epoch so the funds may be redeemed.
collectMsg := v.Messages.Typed(receiver.Robust, paychAddr.Robust, PaychCollect(nil), Value(big.Zero()), Nonce(1), Epoch(paych.SettleDelay))
v.CommitApplies()
// all messages succeeded.
v.Assert.EveryMessageResultSatisfies(ExitCode(exitcode.Ok))
v.Assert.MessageSendersSatisfy(BalanceUpdated(big.Zero()), createMsg, updateMsg)
v.Assert.MessageSendersSatisfy(BalanceUpdated(toSend), settleMsg, collectMsg)
v.Assert.EveryMessageSenderSatisfies(NonceUpdated())
// the paych actor should have been deleted after the collect
v.Assert.ActorMissing(paychAddr.Robust)
v.Assert.ActorMissing(paychAddr.ID)
}

View File

@ -1,43 +0,0 @@
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)
}
}
}

View File

@ -1,165 +0,0 @@
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),
},
)
}

View File

@ -1,30 +0,0 @@
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))
}
}

View File

@ -1,52 +0,0 @@
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)
}