-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test vector builder API + partial migration of suites (#229)
Co-authored-by: Anton Evangelatov <anton.evangelatov@gmail.com>
- Loading branch information
Showing
20 changed files
with
2,072 additions
and
487 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
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" | ||
abi_spec "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" | ||
) | ||
|
||
// Actors is an object that manages actors in the test vector. | ||
type Actors struct { | ||
accounts []AddressHandle | ||
miners []AddressHandle | ||
|
||
b *Builder | ||
} | ||
|
||
// 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.accounts = append(a.accounts, handle) | ||
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 accounts. | ||
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, | ||
) | ||
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.miners = append(a.miners, handle) | ||
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_spec.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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package builders | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
|
||
"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 | ||
} | ||
|
||
// 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package builders | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/filecoin-project/go-address" | ||
"github.com/filecoin-project/specs-actors/actors/abi" | ||
"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) | ||
} | ||
|
||
// 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 | ||
} | ||
a.NoError(predicate(m.Result), "message result predicate failed on message %d", i) | ||
} | ||
} | ||
|
||
func (a *Asserter) FailNow() { | ||
os.Exit(1) | ||
} | ||
|
||
func (a *Asserter) Errorf(format string, args ...interface{}) { | ||
fmt.Printf("%s: "+format, append([]interface{}{a.stage}, args...)) | ||
} |
Oops, something went wrong.