Skip to content

Commit

Permalink
initial vm conversion
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Stebalien committed Sep 25, 2020
1 parent d9656f5 commit 8b35f48
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 97 deletions.
4 changes: 2 additions & 2 deletions chain/stmgr/forks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 1 addition & 2 deletions chain/vm/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)

Expand Down
6 changes: 3 additions & 3 deletions chain/vm/gas_v0.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
128 changes: 81 additions & 47 deletions chain/vm/invoker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(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, 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(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()
Expand Down Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion chain/vm/invoker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
38 changes: 19 additions & 19 deletions chain/vm/mkactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand All @@ -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")
}
Expand All @@ -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:
Expand All @@ -81,19 +81,19 @@ func makeActor(addr address.Address) (*types.Actor, aerrors.ActorError) {
}
}

func NewBLSAccountActor() *types.Actor {
nact := &types.Actor{
Code: builtin.AccountActorCodeID,
Balance: types.NewInt(0),
Head: EmptyObjectCid,
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")
}

return nact
}

func NewSecp256k1AccountActor() *types.Actor {
nact := &types.Actor{
Code: builtin.AccountActorCodeID,
Code: code,
Balance: types.NewInt(0),
Head: EmptyObjectCid,
}
Expand Down
18 changes: 4 additions & 14 deletions chain/vm/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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))
}
Expand Down

0 comments on commit 8b35f48

Please sign in to comment.