877ccb3803
License: MIT Signed-off-by: Jakub Sztandera <kubuxu@protonmail.ch>
235 lines
5.6 KiB
Go
235 lines
5.6 KiB
Go
package actors
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/filecoin-project/go-lotus/chain/actors/aerrors"
|
|
"github.com/filecoin-project/go-lotus/chain/address"
|
|
"github.com/filecoin-project/go-lotus/chain/types"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/ipfs/go-cid"
|
|
hamt "github.com/ipfs/go-hamt-ipld"
|
|
cbor "github.com/ipfs/go-ipld-cbor"
|
|
logging "github.com/ipfs/go-log"
|
|
mh "github.com/multiformats/go-multihash"
|
|
)
|
|
|
|
var log = logging.Logger("actors")
|
|
|
|
var EmptyCBOR cid.Cid
|
|
|
|
func init() {
|
|
cbor.RegisterCborType(ExecParams{})
|
|
cbor.RegisterCborType(struct{}{})
|
|
|
|
n, err := cbor.WrapObject(map[string]string{}, mh.SHA2_256, -1)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
EmptyCBOR = n.Cid()
|
|
}
|
|
|
|
type InitActor struct{}
|
|
|
|
type InitActorState struct {
|
|
// TODO: this needs to be a HAMT, its a dumb map for now
|
|
AddressMap cid.Cid
|
|
|
|
NextID uint64
|
|
}
|
|
|
|
func (ia InitActor) Exports() []interface{} {
|
|
return []interface{}{
|
|
nil,
|
|
ia.Exec,
|
|
}
|
|
}
|
|
|
|
type ExecParams struct {
|
|
Code cid.Cid
|
|
Params []byte
|
|
}
|
|
|
|
func CreateExecParams(act cid.Cid, obj interface{}) ([]byte, aerrors.ActorError) {
|
|
encparams, err := SerializeParams(obj)
|
|
if err != nil {
|
|
return nil, aerrors.Wrap(err, "creating ExecParams")
|
|
}
|
|
|
|
var ep ExecParams
|
|
ep.Code = act
|
|
ep.Params = encparams
|
|
|
|
return SerializeParams(ep)
|
|
}
|
|
|
|
func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams) ([]byte, aerrors.ActorError) {
|
|
beginState := vmctx.Storage().GetHead()
|
|
|
|
var self InitActorState
|
|
if err := vmctx.Storage().Get(beginState, &self); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Make sure that only the actors defined in the spec can be launched.
|
|
if !IsBuiltinActor(p.Code) {
|
|
return nil, aerrors.New(1,
|
|
"cannot launch actor instance that is not a builtin actor")
|
|
}
|
|
|
|
// Ensure that singeltons can be only launched once.
|
|
// TODO: do we want to enforce this? If so how should actors be marked as such?
|
|
if IsSingletonActor(p.Code) {
|
|
return nil, aerrors.New(1, "cannot launch another actor of this type")
|
|
}
|
|
|
|
// This generates a unique address for this actor that is stable across message
|
|
// reordering
|
|
creator := vmctx.Message().From
|
|
nonce := vmctx.Message().Nonce
|
|
addr, err := ComputeActorAddress(creator, nonce)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Set up the actor itself
|
|
actor := types.Actor{
|
|
Code: p.Code,
|
|
Balance: vmctx.Message().Value,
|
|
Head: EmptyCBOR,
|
|
Nonce: 0,
|
|
}
|
|
_, err = vmctx.Storage().Put(struct{}{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// The call to the actors constructor will set up the initial state
|
|
// from the given parameters, setting `actor.Head` to a new value when successful.
|
|
// TODO: can constructors fail?
|
|
//actor.Constructor(p.Params)
|
|
|
|
// Store the mapping of address to actor ID.
|
|
idAddr, nerr := self.AddActor(vmctx, addr)
|
|
if nerr != nil {
|
|
return nil, aerrors.Escalate(err, "adding new actor mapping")
|
|
}
|
|
|
|
// NOTE: This is a privileged call that only the init actor is allowed to make
|
|
// FIXME: Had to comment this because state is not in interface
|
|
state, err := vmctx.StateTree()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := state.SetActor(idAddr, &actor); err != nil {
|
|
if xerrors.Is(err, types.ErrActorNotFound) {
|
|
return nil, aerrors.Absorb(err, 1, "SetActor, actor not found")
|
|
} else {
|
|
return nil, aerrors.Escalate(err, "inserting new actor into state tree")
|
|
}
|
|
}
|
|
|
|
_, err = vmctx.Send(idAddr, 0, vmctx.Message().Value, p.Params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c, err := vmctx.Storage().Put(self)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := vmctx.Storage().Commit(beginState, c); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return idAddr.Bytes(), nil
|
|
}
|
|
|
|
func IsBuiltinActor(code cid.Cid) bool {
|
|
switch code {
|
|
case StorageMinerCodeCid, StorageMinerCodeCid, AccountActorCodeCid, InitActorCodeCid, MultisigActorCodeCid:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func IsSingletonActor(code cid.Cid) bool {
|
|
return code == StorageMarketActorCodeCid || code == InitActorCodeCid
|
|
}
|
|
|
|
func (ias *InitActorState) AddActor(vmctx types.VMContext, addr address.Address) (address.Address, error) {
|
|
nid := ias.NextID
|
|
ias.NextID++
|
|
|
|
amap, err := hamt.LoadNode(context.TODO(), vmctx.Ipld(), ias.AddressMap)
|
|
if err != nil {
|
|
return address.Undef, err
|
|
}
|
|
|
|
if err := amap.Set(context.TODO(), string(addr.Bytes()), nid); err != nil {
|
|
return address.Undef, err
|
|
}
|
|
|
|
if err := amap.Flush(context.TODO()); err != nil {
|
|
return address.Undef, err
|
|
}
|
|
|
|
ncid, err := vmctx.Ipld().Put(context.TODO(), amap)
|
|
if err != nil {
|
|
return address.Undef, err
|
|
}
|
|
ias.AddressMap = ncid
|
|
|
|
return NewIDAddress(nid)
|
|
}
|
|
|
|
func (ias *InitActorState) Lookup(cst *hamt.CborIpldStore, addr address.Address) (address.Address, error) {
|
|
amap, err := hamt.LoadNode(context.TODO(), cst, ias.AddressMap)
|
|
if err != nil {
|
|
return address.Undef, err
|
|
}
|
|
|
|
val, err := amap.Find(context.TODO(), string(addr.Bytes()))
|
|
if err != nil {
|
|
return address.Undef, err
|
|
}
|
|
|
|
ival, ok := val.(uint64)
|
|
if !ok {
|
|
return address.Undef, fmt.Errorf("invalid value in init actor state, expected uint64, got %T", val)
|
|
}
|
|
|
|
return address.NewIDAddress(ival)
|
|
}
|
|
|
|
type AccountActorState struct {
|
|
Address address.Address
|
|
}
|
|
|
|
func ComputeActorAddress(creator address.Address, nonce uint64) (address.Address, ActorError) {
|
|
buf := new(bytes.Buffer)
|
|
_, err := buf.Write(creator.Bytes())
|
|
if err != nil {
|
|
return address.Undef, aerrors.Escalate(err, "could not write address")
|
|
}
|
|
|
|
err = binary.Write(buf, binary.BigEndian, nonce)
|
|
if err != nil {
|
|
return address.Undef, aerrors.Escalate(err, "could not write nonce")
|
|
}
|
|
|
|
addr, err := address.NewActorAddress(buf.Bytes())
|
|
if err != nil {
|
|
return address.Undef, aerrors.Escalate(err, "could not create address")
|
|
}
|
|
return addr, nil
|
|
}
|