initial vm conversion

We're probably going to want to change some of these design decisions down the
road, but this is a good starting point.

* We may want to use a more general test for "is actor valid at epoch". Maybe
just a function?
* I'd like to push some of the actor metadata down into the actor types
themselves. Ideally, we'd be able to register actors with a simple
`Register(validation, manyActors...)` call.
This commit is contained in:
Steven Allen 2020-09-24 17:51:34 -07:00
parent d9656f5220
commit 8b35f480c4
11 changed files with 121 additions and 97 deletions

View File

@ -106,7 +106,7 @@ func TestForkHeightTriggers(t *testing.T) {
sm := NewStateManager(cg.ChainStore())
inv := vm.NewInvoker()
inv := vm.NewActorRegistry()
// predicting the address here... may break if other assumptions change
taddr, err := address.NewIDAddress(1002)
@ -143,7 +143,7 @@ func TestForkHeightTriggers(t *testing.T) {
return nil
}
inv.Register(builtin.PaymentChannelActorCodeID, &testActor{}, &testActorState{})
inv.Register(actors.Version0, builtin.PaymentChannelActorCodeID, &testActor{}, &testActorState{}, false)
sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (*vm.VM, error) {
nvm, err := vm.NewVM(ctx, vmopt)
if err != nil {

View File

@ -9,7 +9,6 @@ import (
addr "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime"
vmr "github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/ipfs/go-cid"
)
@ -210,7 +209,7 @@ func (ps pricedSyscalls) VerifyPoSt(vi proof.WindowPoStVerifyInfo) error {
// the "parent grinding fault", in which case it must be the sibling of h1 (same parent tipset) and one of the
// blocks in the parent of h2 (i.e. h2's grandparent).
// Returns nil and an error if the headers don't prove a fault.
func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte) (*runtime.ConsensusFault, error) {
func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte) (*vmr.ConsensusFault, error) {
ps.chargeGas(ps.pl.OnVerifyConsensusFault())
defer ps.chargeGas(gasOnActorExec)

View File

@ -8,7 +8,7 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/specs-actors/actors/builtin"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
)
type scalingCost struct {
@ -112,14 +112,14 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M
if big.Cmp(value, abi.NewTokenAmount(0)) != 0 {
ret += pl.sendTransferFunds
if methodNum == builtin.MethodSend {
if methodNum == builtin0.MethodSend {
// transfer only
ret += pl.sendTransferOnlyPremium
}
extra += "t"
}
if methodNum != builtin.MethodSend {
if methodNum != builtin0.MethodSend {
extra += "i"
// running actors is cheaper becase we hand over to actors
ret += pl.sendInvokeMethod

View File

@ -40,79 +40,113 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/types"
)
type Invoker struct {
builtInCode map[cid.Cid]nativeCode
builtInState map[cid.Cid]reflect.Type
type ActorRegistry struct {
actors map[cid.Cid]*actorInfo
}
type invokeFunc func(rt vmr.Runtime, params []byte) ([]byte, aerrors.ActorError)
type nativeCode []invokeFunc
func NewInvoker() *Invoker {
inv := &Invoker{
builtInCode: make(map[cid.Cid]nativeCode),
builtInState: make(map[cid.Cid]reflect.Type),
}
type actorInfo struct {
methods nativeCode
stateType reflect.Type
// TODO: consider making this a network version range?
version actors.Version
singleton bool
}
func NewActorRegistry() *ActorRegistry {
inv := &ActorRegistry{actors: make(map[cid.Cid]*actorInfo)}
// TODO: define all these properties on the actors themselves, in specs-actors.
// add builtInCode using: register(cid, singleton)
inv.Register(builtin0.SystemActorCodeID, system0.Actor{}, abi.EmptyValue{})
inv.Register(builtin0.InitActorCodeID, init0.Actor{}, init0.State{})
inv.Register(builtin0.RewardActorCodeID, reward0.Actor{}, reward0.State{})
inv.Register(builtin0.CronActorCodeID, cron0.Actor{}, cron0.State{})
inv.Register(builtin0.StoragePowerActorCodeID, power0.Actor{}, power0.State{})
inv.Register(builtin0.StorageMarketActorCodeID, market0.Actor{}, market0.State{})
inv.Register(builtin0.StorageMinerActorCodeID, miner0.Actor{}, miner0.State{})
inv.Register(builtin0.MultisigActorCodeID, msig0.Actor{}, msig0.State{})
inv.Register(builtin0.PaymentChannelActorCodeID, paych0.Actor{}, paych0.State{})
inv.Register(builtin0.VerifiedRegistryActorCodeID, verifreg0.Actor{}, verifreg0.State{})
inv.Register(builtin0.AccountActorCodeID, account0.Actor{}, account0.State{})
inv.Register(actors.Version0, builtin0.SystemActorCodeID, system0.Actor{}, abi.EmptyValue{}, true)
inv.Register(actors.Version0, builtin0.InitActorCodeID, init0.Actor{}, init0.State{}, true)
inv.Register(actors.Version0, builtin0.RewardActorCodeID, reward0.Actor{}, reward0.State{}, true)
inv.Register(actors.Version0, builtin0.CronActorCodeID, cron0.Actor{}, cron0.State{}, true)
inv.Register(actors.Version0, builtin0.StoragePowerActorCodeID, power0.Actor{}, power0.State{}, true)
inv.Register(actors.Version0, builtin0.StorageMarketActorCodeID, market0.Actor{}, market0.State{}, true)
inv.Register(actors.Version0, builtin0.VerifiedRegistryActorCodeID, verifreg0.Actor{}, verifreg0.State{}, true)
inv.Register(actors.Version0, builtin0.StorageMinerActorCodeID, miner0.Actor{}, miner0.State{}, false)
inv.Register(actors.Version0, builtin0.MultisigActorCodeID, msig0.Actor{}, msig0.State{}, false)
inv.Register(actors.Version0, builtin0.PaymentChannelActorCodeID, paych0.Actor{}, paych0.State{}, false)
inv.Register(actors.Version0, builtin0.AccountActorCodeID, account0.Actor{}, account0.State{}, false)
inv.Register(builtin1.SystemActorCodeID, system1.Actor{}, abi.EmptyValue{})
inv.Register(builtin1.InitActorCodeID, init1.Actor{}, init1.State{})
inv.Register(builtin1.RewardActorCodeID, reward1.Actor{}, reward1.State{})
inv.Register(builtin1.CronActorCodeID, cron1.Actor{}, cron1.State{})
inv.Register(builtin1.StoragePowerActorCodeID, power1.Actor{}, power1.State{})
inv.Register(builtin1.StorageMarketActorCodeID, market1.Actor{}, market1.State{})
inv.Register(builtin1.StorageMinerActorCodeID, miner1.Actor{}, miner1.State{})
inv.Register(builtin1.MultisigActorCodeID, msig1.Actor{}, msig1.State{})
inv.Register(builtin1.PaymentChannelActorCodeID, paych1.Actor{}, paych1.State{})
inv.Register(builtin1.VerifiedRegistryActorCodeID, verifreg1.Actor{}, verifreg1.State{})
inv.Register(builtin1.AccountActorCodeID, account1.Actor{}, account1.State{})
inv.Register(actors.Version0, builtin1.SystemActorCodeID, system1.Actor{}, abi.EmptyValue{}, true)
inv.Register(actors.Version0, builtin1.InitActorCodeID, init1.Actor{}, init1.State{}, true)
inv.Register(actors.Version0, builtin1.RewardActorCodeID, reward1.Actor{}, reward1.State{}, true)
inv.Register(actors.Version0, builtin1.CronActorCodeID, cron1.Actor{}, cron1.State{}, true)
inv.Register(actors.Version0, builtin1.StoragePowerActorCodeID, power1.Actor{}, power1.State{}, true)
inv.Register(actors.Version0, builtin1.StorageMarketActorCodeID, market1.Actor{}, market1.State{}, true)
inv.Register(actors.Version0, builtin1.VerifiedRegistryActorCodeID, verifreg1.Actor{}, verifreg1.State{}, true)
inv.Register(actors.Version0, builtin1.StorageMinerActorCodeID, miner1.Actor{}, miner1.State{}, false)
inv.Register(actors.Version0, builtin1.MultisigActorCodeID, msig1.Actor{}, msig1.State{}, false)
inv.Register(actors.Version0, builtin1.PaymentChannelActorCodeID, paych1.Actor{}, paych1.State{}, false)
inv.Register(actors.Version0, builtin1.AccountActorCodeID, account1.Actor{}, account1.State{}, false)
return inv
}
func (inv *Invoker) Invoke(codeCid cid.Cid, rt vmr.Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
code, ok := inv.builtInCode[codeCid]
func (ar *ActorRegistry) Invoke(codeCid cid.Cid, rt vmr.Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
act, ok := ar.actors[codeCid]
if !ok {
log.Errorf("no code for actor %s (Addr: %s)", codeCid, rt.Receiver())
return nil, aerrors.Newf(exitcode.SysErrorIllegalActor, "no code for actor %s(%d)(%s)", codeCid, method, hex.EncodeToString(params))
}
if method >= abi.MethodNum(len(code)) || code[method] == nil {
if method >= abi.MethodNum(len(act.methods)) || act.methods[method] == nil {
return nil, aerrors.Newf(exitcode.SysErrInvalidMethod, "no method %d on actor", method)
}
return code[method](rt, params)
if curVer := actors.VersionForNetwork(rt.NetworkVersion()); curVer != act.version {
return nil, aerrors.Newf(exitcode.SysErrInvalidMethod, "unsupported actors code version %d, expected %d", act.version, curVer)
}
return act.methods[method](rt, params)
}
func (inv *Invoker) Register(c cid.Cid, instance Invokee, state interface{}) {
code, err := inv.transform(instance)
func (ar *ActorRegistry) Register(version actors.Version, c cid.Cid, instance Invokee, state interface{}, singleton bool) {
code, err := ar.transform(instance)
if err != nil {
panic(xerrors.Errorf("%s: %w", string(c.Hash()), err))
}
inv.builtInCode[c] = code
inv.builtInState[c] = reflect.TypeOf(state)
ar.actors[c] = &actorInfo{
methods: code,
version: version,
stateType: reflect.TypeOf(state),
singleton: singleton,
}
}
func (ar *ActorRegistry) Create(codeCid cid.Cid, rt vmr.Runtime) (*types.Actor, aerrors.ActorError) {
act, ok := ar.actors[codeCid]
if !ok {
return nil, aerrors.Newf(exitcode.SysErrorIllegalArgument, "Can only create built-in actors.")
}
if version := actors.VersionForNetwork(rt.NetworkVersion()); act.version != version {
return nil, aerrors.Newf(exitcode.SysErrorIllegalArgument, "Can only create version %d actors, attempted to create version %d actor", version, act.version)
}
if act.singleton {
return nil, aerrors.Newf(exitcode.SysErrorIllegalArgument, "Can only have one instance of singleton actors.")
}
return &types.Actor{
Code: codeCid,
Head: EmptyObjectCid,
Nonce: 0,
Balance: abi.NewTokenAmount(0),
}, nil
}
type Invokee interface {
Exports() []interface{}
}
func (*Invoker) transform(instance Invokee) (nativeCode, error) {
func (*ActorRegistry) transform(instance Invokee) (nativeCode, error) {
itype := reflect.TypeOf(instance)
exports := instance.Exports()
runtimeType := reflect.TypeOf((*vmr.Runtime)(nil)).Elem()
@ -201,19 +235,19 @@ func DecodeParams(b []byte, out interface{}) error {
return um.UnmarshalCBOR(bytes.NewReader(b))
}
func DumpActorState(code cid.Cid, b []byte) (interface{}, error) {
if code == builtin0.AccountActorCodeID { // Account code special case
func DumpActorState(act *types.Actor, b []byte) (interface{}, error) {
if act.IsAccountActor() { // Account code special case
return nil, nil
}
i := NewInvoker() // TODO: register builtins in init block
i := NewActorRegistry() // TODO: register builtins in init block
typ, ok := i.builtInState[code]
actInfo, ok := i.actors[act.Code]
if !ok {
return nil, xerrors.Errorf("state type for actor %s not found", code)
return nil, xerrors.Errorf("state type for actor %s not found", act.Code)
}
rv := reflect.New(typ)
rv := reflect.New(actInfo.stateType)
um, ok := rv.Interface().(cbg.CBORUnmarshaler)
if !ok {
return nil, xerrors.New("state type does not implement CBORUnmarshaler")

View File

@ -77,7 +77,7 @@ func (basicContract) InvokeSomething10(rt runtime.Runtime, params *basicParams)
}
func TestInvokerBasic(t *testing.T) {
inv := Invoker{}
inv := ActorRegistry{}
code, err := inv.transform(basicContract{})
assert.NoError(t, err)

View File

@ -6,11 +6,13 @@ import (
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
builtin1 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/types"
@ -39,7 +41,7 @@ func TryCreateAccountActor(rt *Runtime, addr address.Address) (*types.Actor, aer
return nil, aerrors.Escalate(err, "registering actor address")
}
act, aerr := makeActor(addr)
act, aerr := makeActor(actors.VersionForNetwork(rt.NetworkVersion()), addr)
if aerr != nil {
return nil, aerr
}
@ -54,7 +56,7 @@ func TryCreateAccountActor(rt *Runtime, addr address.Address) (*types.Actor, aer
}
// call constructor on account
_, aerr = rt.internalSend(builtin.SystemActorAddr, addrID, builtin.MethodsAccount.Constructor, big.Zero(), p)
_, aerr = rt.internalSend(builtin0.SystemActorAddr, addrID, builtin0.MethodsAccount.Constructor, big.Zero(), p)
if aerr != nil {
return nil, aerrors.Wrap(aerr, "failed to invoke account constructor")
}
@ -66,12 +68,10 @@ func TryCreateAccountActor(rt *Runtime, addr address.Address) (*types.Actor, aer
return act, nil
}
func makeActor(addr address.Address) (*types.Actor, aerrors.ActorError) {
func makeActor(ver actors.Version, addr address.Address) (*types.Actor, aerrors.ActorError) {
switch addr.Protocol() {
case address.BLS:
return NewBLSAccountActor(), nil
case address.SECP256K1:
return NewSecp256k1AccountActor(), nil
case address.BLS, address.SECP256K1:
return newAccountActor(ver), nil
case address.ID:
return nil, aerrors.Newf(exitcode.SysErrInvalidReceiver, "no actor with given ID: %s", addr)
case address.Actor:
@ -81,19 +81,19 @@ func makeActor(addr address.Address) (*types.Actor, aerrors.ActorError) {
}
}
func NewBLSAccountActor() *types.Actor {
func newAccountActor(ver actors.Version) *types.Actor {
// TODO: ActorsUpgrade use a global actor registry?
var code cid.Cid
switch ver {
case actors.Version0:
code = builtin0.AccountActorCodeID
case actors.Version1:
code = builtin1.AccountActorCodeID
default:
panic("unsupported actors version")
}
nact := &types.Actor{
Code: builtin.AccountActorCodeID,
Balance: types.NewInt(0),
Head: EmptyObjectCid,
}
return nact
}
func NewSecp256k1AccountActor() *types.Actor {
nact := &types.Actor{
Code: builtin.AccountActorCodeID,
Code: code,
Balance: types.NewInt(0),
Head: EmptyObjectCid,
}

View File

@ -10,13 +10,11 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/cbor"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/go-state-types/network"
rtt "github.com/filecoin-project/go-state-types/rt"
"github.com/filecoin-project/specs-actors/actors/builtin"
rt0 "github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/ipfs/go-cid"
ipldcbor "github.com/ipfs/go-ipld-cbor"
@ -216,12 +214,9 @@ func (rt *Runtime) NewActorAddress() address.Address {
}
func (rt *Runtime) CreateActor(codeID cid.Cid, address address.Address) {
if !builtin.IsBuiltinActor(codeID) {
rt.Abortf(exitcode.SysErrorIllegalArgument, "Can only create built-in actors.")
}
if builtin.IsSingletonActor(codeID) {
rt.Abortf(exitcode.SysErrorIllegalArgument, "Can only have one instance of singleton actors.")
act, aerr := rt.vm.areg.Create(codeID, rt)
if aerr != nil {
rt.Abortf(aerr.RetCode(), aerr.Error())
}
_, err := rt.state.GetActor(address)
@ -231,12 +226,7 @@ func (rt *Runtime) CreateActor(codeID cid.Cid, address address.Address) {
rt.chargeGas(rt.Pricelist().OnCreateActor())
err = rt.state.SetActor(address, &types.Actor{
Code: codeID,
Head: EmptyObjectCid,
Nonce: 0,
Balance: big.Zero(),
})
err = rt.state.SetActor(address, act)
if err != nil {
panic(aerrors.Fatalf("creating actor entry: %v", err))
}

View File

@ -152,7 +152,7 @@ type VM struct {
cst *cbor.BasicIpldStore
buf *bufbstore.BufferedBS
blockHeight abi.ChainEpoch
inv *Invoker
areg *ActorRegistry
rand Rand
circSupplyCalc CircSupplyCalculator
ntwkVersion NtwkVersionGetter
@ -186,7 +186,7 @@ func NewVM(ctx context.Context, opts *VMOpts) (*VM, error) {
cst: cst,
buf: buf,
blockHeight: opts.Epoch,
inv: NewInvoker(),
areg: NewActorRegistry(),
rand: opts.Rand, // TODO: Probably should be a syscall
circSupplyCalc: opts.CircSupplyCalc,
ntwkVersion: opts.NtwkVersion,
@ -734,15 +734,15 @@ func (vm *VM) Invoke(act *types.Actor, rt *Runtime, method abi.MethodNum, params
defer func() {
rt.ctx = oldCtx
}()
ret, err := vm.inv.Invoke(act.Code, rt, method, params)
ret, err := vm.areg.Invoke(act.Code, rt, method, params)
if err != nil {
return nil, err
}
return ret, nil
}
func (vm *VM) SetInvoker(i *Invoker) {
vm.inv = i
func (vm *VM) SetInvoker(i *ActorRegistry) {
vm.areg = i
}
func (vm *VM) GetNtwkVersion(ctx context.Context, ce abi.ChainEpoch) network.Version {

View File

@ -5,6 +5,7 @@ import (
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
@ -140,11 +141,11 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, preroot cid.Cid, epoch
return nil, cid.Undef, err
}
invoker := vm.NewInvoker()
invoker := vm.NewActorRegistry()
// register the chaos actor if required by the vector.
if chaosOn, ok := d.selector["chaos_actor"]; ok && chaosOn == "true" {
invoker.Register(chaos.ChaosActorCodeCID, chaos.Actor{}, chaos.State{})
invoker.Register(actors.Version0, chaos.ChaosActorCodeCID, chaos.Actor{}, chaos.State{}, true)
}
lvm.SetInvoker(invoker)

View File

@ -422,7 +422,7 @@ func resolveOnce(bs blockstore.Blockstore) func(ctx context.Context, ds ipld.Nod
return nil, nil, xerrors.Errorf("getting actor head for @state: %w", err)
}
m, err := vm.DumpActorState(act.Code, head.RawData())
m, err := vm.DumpActorState(&act, head.RawData())
if err != nil {
return nil, nil, err
}

View File

@ -411,7 +411,7 @@ func (a *StateAPI) StateReadState(ctx context.Context, actor address.Address, ts
return nil, xerrors.Errorf("getting actor head: %w", err)
}
oif, err := vm.DumpActorState(act.Code, blk.RawData())
oif, err := vm.DumpActorState(act, blk.RawData())
if err != nil {
return nil, xerrors.Errorf("dumping actor state (a:%s): %w", actor, err)
}