squash the spec shim and the vmcontext into the new runtime type
This commit is contained in:
parent
f3798f9d40
commit
114cb2d2aa
@ -125,6 +125,30 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) {
|
||||
return &act, nil
|
||||
}
|
||||
|
||||
func (st *StateTree) DeleteActor(addr address.Address) error {
|
||||
if addr == address.Undef {
|
||||
return xerrors.Errorf("DeleteActor called on undefined address")
|
||||
}
|
||||
|
||||
iaddr, err := st.LookupID(addr)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, init_.ErrAddressNotFound) {
|
||||
return xerrors.Errorf("resolution lookup failed (%s): %w", addr, err)
|
||||
}
|
||||
return xerrors.Errorf("address resolution: %w", err)
|
||||
}
|
||||
|
||||
addr = iaddr
|
||||
|
||||
delete(st.actorcache, addr)
|
||||
|
||||
if err := st.root.Delete(context.TODO(), string(addr.Bytes())); err != nil {
|
||||
return xerrors.Errorf("failed to delete actor: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "stateTree.Flush")
|
||||
defer span.End()
|
||||
|
@ -1,17 +1,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
@ -31,26 +24,6 @@ type StateTree interface {
|
||||
GetActor(addr address.Address) (*Actor, error)
|
||||
}
|
||||
|
||||
type VMContext interface {
|
||||
Message() *Message
|
||||
Origin() address.Address
|
||||
Ipld() cbor.IpldStore
|
||||
Send(to address.Address, method abi.MethodNum, value BigInt, params []byte) ([]byte, aerrors.ActorError)
|
||||
BlockHeight() abi.ChainEpoch
|
||||
GasUsed() BigInt
|
||||
Storage() Storage
|
||||
StateTree() (StateTree, aerrors.ActorError)
|
||||
ActorCodeCID(address.Address) (cid.Cid, error)
|
||||
LookupID(address.Address) (address.Address, error)
|
||||
VerifySignature(sig *crypto.Signature, from address.Address, data []byte) aerrors.ActorError
|
||||
ChargeGas(uint64) aerrors.ActorError
|
||||
GetRandomness(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, aerrors.ActorError)
|
||||
GetBalance(address.Address) (BigInt, aerrors.ActorError)
|
||||
Sys() runtime.Syscalls
|
||||
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
type storageWrapper struct {
|
||||
s Storage
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/system"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
vmr "github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
@ -33,7 +34,7 @@ type invoker struct {
|
||||
builtInState map[cid.Cid]reflect.Type
|
||||
}
|
||||
|
||||
type invokeFunc func(act *types.Actor, vmctx types.VMContext, params []byte) ([]byte, aerrors.ActorError)
|
||||
type invokeFunc func(act *types.Actor, rt runtime.Runtime, params []byte) ([]byte, aerrors.ActorError)
|
||||
type nativeCode []invokeFunc
|
||||
|
||||
func NewInvoker() *invoker {
|
||||
@ -56,7 +57,7 @@ func NewInvoker() *invoker {
|
||||
return inv
|
||||
}
|
||||
|
||||
func (inv *invoker) Invoke(act *types.Actor, vmctx types.VMContext, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
|
||||
func (inv *invoker) Invoke(act *types.Actor, rt runtime.Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
|
||||
|
||||
if act.Code == builtin.AccountActorCodeID {
|
||||
return nil, aerrors.Newf(254, "cannot invoke methods on account actors")
|
||||
@ -64,13 +65,13 @@ func (inv *invoker) Invoke(act *types.Actor, vmctx types.VMContext, method abi.M
|
||||
|
||||
code, ok := inv.builtInCode[act.Code]
|
||||
if !ok {
|
||||
log.Errorf("no code for actor %s (Addr: %s)", act.Code, vmctx.Message().To)
|
||||
log.Errorf("no code for actor %s (Addr: %s)", act.Code, rt.Message().Receiver())
|
||||
return nil, aerrors.Newf(255, "no code for actor %s(%d)(%s)", act.Code, method, hex.EncodeToString(params))
|
||||
}
|
||||
if method >= abi.MethodNum(len(code)) || code[method] == nil {
|
||||
return nil, aerrors.Newf(255, "no method %d on actor", method)
|
||||
}
|
||||
return code[method](act, vmctx, params)
|
||||
return code[method](act, rt, params)
|
||||
|
||||
}
|
||||
|
||||
@ -87,7 +88,6 @@ type Invokee interface {
|
||||
Exports() []interface{}
|
||||
}
|
||||
|
||||
var tVMContext = reflect.TypeOf((*types.VMContext)(nil)).Elem()
|
||||
var tAError = reflect.TypeOf((*aerrors.ActorError)(nil)).Elem()
|
||||
|
||||
func (*invoker) transform(instance Invokee) (nativeCode, error) {
|
||||
@ -117,7 +117,7 @@ func (*invoker) transform(instance Invokee) (nativeCode, error) {
|
||||
return nil, newErr("first arguemnt should be vmr.Runtime")
|
||||
}
|
||||
if t.In(1).Kind() != reflect.Ptr {
|
||||
return nil, newErr("second argument should be types.VMContext")
|
||||
return nil, newErr("second argument should be Runtime")
|
||||
}
|
||||
|
||||
if t.NumOut() != 1 {
|
||||
@ -150,10 +150,10 @@ func (*invoker) transform(instance Invokee) (nativeCode, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
shim := &runtimeShim{vmctx: in[1].Interface().(*VMContext)}
|
||||
rval, aerror := shim.shimCall(func() interface{} {
|
||||
rt := in[1].Interface().(*Runtime)
|
||||
rval, aerror := rt.shimCall(func() interface{} {
|
||||
ret := meth.Call([]reflect.Value{
|
||||
reflect.ValueOf(shim),
|
||||
reflect.ValueOf(rt),
|
||||
param,
|
||||
})
|
||||
return ret[0].Interface()
|
||||
|
@ -84,7 +84,7 @@ func TestInvokerBasic(t *testing.T) {
|
||||
bParam, err := actors.SerializeParams(&basicParams{B: 1})
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, aerr := code[0](nil, &VMContext{}, bParam)
|
||||
_, aerr := code[0](nil, &Runtime{}, bParam)
|
||||
|
||||
assert.Equal(t, byte(1), aerrors.RetCode(aerr), "return code should be 1")
|
||||
if aerrors.IsFatal(aerr) {
|
||||
@ -96,14 +96,14 @@ func TestInvokerBasic(t *testing.T) {
|
||||
bParam, err := actors.SerializeParams(&basicParams{B: 2})
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, aerr := code[10](nil, &VMContext{}, bParam)
|
||||
_, aerr := code[10](nil, &Runtime{}, bParam)
|
||||
assert.Equal(t, byte(12), aerrors.RetCode(aerr), "return code should be 12")
|
||||
if aerrors.IsFatal(aerr) {
|
||||
t.Fatal("err should not be fatal")
|
||||
}
|
||||
}
|
||||
|
||||
_, aerr := code[1](nil, &VMContext{}, []byte{99})
|
||||
_, aerr := code[1](nil, &Runtime{}, []byte{99})
|
||||
if aerrors.IsFatal(aerr) {
|
||||
t.Fatal("err should not be fatal")
|
||||
}
|
||||
|
428
chain/vm/runtime.go
Normal file
428
chain/vm/runtime.go
Normal file
@ -0,0 +1,428 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"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/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
vmr "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"
|
||||
"github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type Runtime struct {
|
||||
ctx context.Context
|
||||
|
||||
vm *VM
|
||||
state *state.StateTree
|
||||
msg *types.Message
|
||||
height abi.ChainEpoch
|
||||
cst cbor.IpldStore
|
||||
|
||||
gasAvailable types.BigInt
|
||||
gasUsed types.BigInt
|
||||
|
||||
sys runtime.Syscalls
|
||||
|
||||
// address that started invoke chain
|
||||
origin address.Address
|
||||
|
||||
internalExecutions []*ExecutionResult
|
||||
}
|
||||
|
||||
func (rs *Runtime) ResolveAddress(address address.Address) (ret address.Address, ok bool) {
|
||||
r, err := rs.LookupID(address)
|
||||
if err != nil { // TODO: check notfound
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "resolve address: %v", err)
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
|
||||
func (rs *Runtime) Get(c cid.Cid, o vmr.CBORUnmarshaler) bool {
|
||||
if err := rs.cst.Get(context.TODO(), c, o); err != nil {
|
||||
// TODO: err not found?
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "storage get: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (rs *Runtime) Put(x vmr.CBORMarshaler) cid.Cid {
|
||||
c, err := rs.cst.Put(context.TODO(), x)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "storage put: %v", err) // todo: spec code?
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
var _ vmr.Runtime = (*Runtime)(nil)
|
||||
|
||||
func (rs *Runtime) shimCall(f func() interface{}) (rval []byte, aerr aerrors.ActorError) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if ar, ok := r.(aerrors.ActorError); ok {
|
||||
log.Warn("VM.Call failure: ", ar)
|
||||
debug.PrintStack()
|
||||
aerr = ar
|
||||
return
|
||||
}
|
||||
debug.PrintStack()
|
||||
log.Errorf("ERROR")
|
||||
aerr = aerrors.Newf(1, "spec actors failure: %s", r)
|
||||
}
|
||||
}()
|
||||
|
||||
ret := f()
|
||||
switch ret := ret.(type) {
|
||||
case []byte:
|
||||
return ret, nil
|
||||
case *adt.EmptyValue:
|
||||
return nil, nil
|
||||
case cbg.CBORMarshaler:
|
||||
buf := new(bytes.Buffer)
|
||||
if err := ret.MarshalCBOR(buf); err != nil {
|
||||
return nil, aerrors.Absorb(err, 2, "failed to marshal response to cbor")
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, aerrors.New(3, "could not determine type for response from call")
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *Runtime) Message() vmr.Message {
|
||||
var err error
|
||||
|
||||
rawm := *rs.msg
|
||||
rawm.From, err = rs.LookupID(rawm.From)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "resolve from address: %v", err)
|
||||
}
|
||||
|
||||
rawm.To, err = rs.LookupID(rawm.To)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "resolve to address: %v", err)
|
||||
}
|
||||
|
||||
return &rawm
|
||||
}
|
||||
|
||||
func (rs *Runtime) ValidateImmediateCallerAcceptAny() {
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *Runtime) CurrentBalance() abi.TokenAmount {
|
||||
b, err := rs.GetBalance(rs.Message().Receiver())
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ExitCode(err.RetCode()), "get current balance: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (rs *Runtime) GetActorCodeCID(addr address.Address) (ret cid.Cid, ok bool) {
|
||||
act, err := rs.state.GetActor(addr)
|
||||
if err != nil {
|
||||
// todo: notfound
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "%v", err)
|
||||
}
|
||||
|
||||
return act.Code, true
|
||||
}
|
||||
|
||||
func (rt *Runtime) GetRandomness(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness {
|
||||
res, err := rt.vm.rand.GetRandomness(rt.ctx, personalization, int64(randEpoch), entropy)
|
||||
if err != nil {
|
||||
rt.Abortf(exitcode.SysErrInternal, "could not get randomness: %s", err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (rs *Runtime) Store() vmr.Store {
|
||||
return rs
|
||||
}
|
||||
|
||||
func (rt *Runtime) NewActorAddress() address.Address {
|
||||
var b bytes.Buffer
|
||||
if err := rt.Message().Caller().MarshalCBOR(&b); err != nil { // todo: spec says cbor; why not just bytes?
|
||||
rt.Abortf(exitcode.ErrSerialization, "writing caller address into a buffer: %v", err)
|
||||
}
|
||||
|
||||
act, err := rt.state.GetActor(rt.origin)
|
||||
if err != nil {
|
||||
rt.Abortf(exitcode.SysErrInternal, "getting top level actor: %v", err)
|
||||
}
|
||||
|
||||
if err := binary.Write(&b, binary.BigEndian, act.Nonce); err != nil {
|
||||
rt.Abortf(exitcode.ErrSerialization, "writing nonce address into a buffer: %v", err)
|
||||
}
|
||||
if err := binary.Write(&b, binary.BigEndian, uint64(0)); err != nil { // TODO: expose on vm
|
||||
rt.Abortf(exitcode.ErrSerialization, "writing callSeqNum address into a buffer: %v", err)
|
||||
}
|
||||
addr, err := address.NewActorAddress(b.Bytes())
|
||||
if err != nil {
|
||||
rt.Abortf(exitcode.ErrSerialization, "create actor address: %v", err)
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func (rt *Runtime) CreateActor(codeId cid.Cid, address address.Address) {
|
||||
var err error
|
||||
|
||||
err = rt.state.SetActor(address, &types.Actor{
|
||||
Code: codeId,
|
||||
Head: EmptyObjectCid,
|
||||
Nonce: 0,
|
||||
Balance: big.Zero(),
|
||||
})
|
||||
if err != nil {
|
||||
rt.Abortf(exitcode.SysErrInternal, "creating actor entry: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *Runtime) DeleteActor() {
|
||||
if err := rt.state.DeleteActor(rt.Message().Receiver()); err != nil {
|
||||
rt.Abortf(exitcode.SysErrInternal, "failed to delete actor: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
const GasVerifySignature = 50
|
||||
|
||||
func (rs *Runtime) Syscalls() vmr.Syscalls {
|
||||
// TODO: Make sure this is wrapped in something that charges gas for each of the calls
|
||||
return rs.sys
|
||||
}
|
||||
|
||||
func (rs *Runtime) StartSpan(name string) vmr.TraceSpan {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (rt *Runtime) ValidateImmediateCallerIs(as ...address.Address) {
|
||||
imm, err := rt.LookupID(rt.Message().Caller())
|
||||
if err != nil {
|
||||
rt.Abortf(exitcode.ErrIllegalState, "couldn't resolve immediate caller")
|
||||
}
|
||||
|
||||
for _, a := range as {
|
||||
if imm == a {
|
||||
return
|
||||
}
|
||||
}
|
||||
rt.Abortf(exitcode.ErrForbidden, "caller %s is not one of %s", rt.Message().Caller(), as)
|
||||
}
|
||||
|
||||
func (rt *Runtime) Context() context.Context {
|
||||
return rt.ctx
|
||||
}
|
||||
|
||||
func (rs *Runtime) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) {
|
||||
panic(aerrors.NewfSkip(2, uint8(code), msg, args...))
|
||||
}
|
||||
|
||||
func (rs *Runtime) AbortStateMsg(msg string) {
|
||||
panic(aerrors.NewfSkip(3, 101, msg))
|
||||
}
|
||||
|
||||
func (rt *Runtime) ValidateImmediateCallerType(ts ...cid.Cid) {
|
||||
callerCid, ok := rt.GetActorCodeCID(rt.Message().Caller())
|
||||
if !ok {
|
||||
rt.Abortf(exitcode.ErrIllegalArgument, "failed to lookup code cid for caller")
|
||||
}
|
||||
for _, t := range ts {
|
||||
if t == callerCid {
|
||||
return
|
||||
}
|
||||
}
|
||||
rt.Abortf(exitcode.ErrForbidden, "caller cid type %q was not one of %v", callerCid, ts)
|
||||
}
|
||||
|
||||
func (rs *Runtime) CurrEpoch() abi.ChainEpoch {
|
||||
return rs.height
|
||||
}
|
||||
|
||||
type dumbWrapperType struct {
|
||||
val []byte
|
||||
}
|
||||
|
||||
func (dwt *dumbWrapperType) Into(um vmr.CBORUnmarshaler) error {
|
||||
return um.UnmarshalCBOR(bytes.NewReader(dwt.val))
|
||||
}
|
||||
|
||||
func (rs *Runtime) Send(to address.Address, method abi.MethodNum, m vmr.CBORMarshaler, value abi.TokenAmount) (vmr.SendReturn, exitcode.ExitCode) {
|
||||
var params []byte
|
||||
if m != nil {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := m.MarshalCBOR(buf); err != nil {
|
||||
rs.Abortf(exitcode.SysErrInvalidParameters, "failed to marshal input parameters: %s", err)
|
||||
}
|
||||
params = buf.Bytes()
|
||||
}
|
||||
|
||||
ret, err := rs.internalSend(to, method, types.BigInt(value), params)
|
||||
if err != nil {
|
||||
if err.IsFatal() {
|
||||
panic(err)
|
||||
}
|
||||
log.Warnf("vmctx send failed: to: %s, method: %d: ret: %d, err: %s", to, method, ret, err)
|
||||
return nil, exitcode.ExitCode(err.RetCode())
|
||||
}
|
||||
return &dumbWrapperType{ret}, 0
|
||||
}
|
||||
|
||||
func (rt *Runtime) internalSend(to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
|
||||
fmt.Println("internal send: ", to, method)
|
||||
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("to", to.String()),
|
||||
trace.Int64Attribute("method", int64(method)),
|
||||
trace.StringAttribute("value", value.String()),
|
||||
)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
From: rt.Message().Receiver(),
|
||||
To: to,
|
||||
Method: method,
|
||||
Value: value,
|
||||
Params: params,
|
||||
GasLimit: rt.gasAvailable,
|
||||
}
|
||||
|
||||
st := rt.state
|
||||
if err := st.Snapshot(ctx); err != nil {
|
||||
return nil, aerrors.Fatalf("snapshot failed: %s", err)
|
||||
}
|
||||
defer st.ClearSnapshot()
|
||||
|
||||
ret, err, subrt := rt.vm.send(ctx, msg, rt, 0)
|
||||
if err != nil {
|
||||
fmt.Println("send failed")
|
||||
if err := st.Revert(); err != nil {
|
||||
return nil, aerrors.Escalate(err, "failed to revert state tree after failed subcall")
|
||||
}
|
||||
}
|
||||
fmt.Println("after 'send'", ret)
|
||||
|
||||
mr := types.MessageReceipt{
|
||||
ExitCode: exitcode.ExitCode(aerrors.RetCode(err)),
|
||||
Return: ret,
|
||||
GasUsed: types.EmptyInt,
|
||||
}
|
||||
|
||||
var es = ""
|
||||
if err != nil {
|
||||
es = err.Error()
|
||||
}
|
||||
er := ExecutionResult{
|
||||
Msg: msg,
|
||||
MsgRct: &mr,
|
||||
Error: es,
|
||||
Subcalls: subrt.internalExecutions,
|
||||
}
|
||||
|
||||
fmt.Println("keeping execution!")
|
||||
rt.internalExecutions = append(rt.internalExecutions, &er)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (rs *Runtime) State() vmr.StateHandle {
|
||||
return &shimStateHandle{rs: rs}
|
||||
}
|
||||
|
||||
type shimStateHandle struct {
|
||||
rs *Runtime
|
||||
}
|
||||
|
||||
func (ssh *shimStateHandle) Create(obj vmr.CBORMarshaler) {
|
||||
c := ssh.rs.Put(obj)
|
||||
ssh.rs.stateCommit(EmptyObjectCid, c)
|
||||
}
|
||||
|
||||
func (ssh *shimStateHandle) Readonly(obj vmr.CBORUnmarshaler) {
|
||||
act, err := ssh.rs.state.GetActor(ssh.rs.Message().Receiver())
|
||||
if err != nil {
|
||||
ssh.rs.Abortf(exitcode.SysErrInternal, "failed to get actor for Readonly state: %s", err)
|
||||
}
|
||||
ssh.rs.Get(act.Head, obj)
|
||||
}
|
||||
|
||||
func (ssh *shimStateHandle) Transaction(obj vmr.CBORer, f func() interface{}) interface{} {
|
||||
act, err := ssh.rs.state.GetActor(ssh.rs.Message().Receiver())
|
||||
if err != nil {
|
||||
ssh.rs.Abortf(exitcode.SysErrInternal, "failed to get actor for Readonly state: %s", err)
|
||||
}
|
||||
baseState := act.Head
|
||||
ssh.rs.Get(baseState, obj)
|
||||
|
||||
out := f()
|
||||
|
||||
c := ssh.rs.Put(obj)
|
||||
|
||||
ssh.rs.stateCommit(baseState, c)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (rt *Runtime) LookupID(a address.Address) (address.Address, error) {
|
||||
return rt.state.LookupID(a)
|
||||
}
|
||||
|
||||
func (rt *Runtime) GetBalance(a address.Address) (types.BigInt, aerrors.ActorError) {
|
||||
act, err := rt.state.GetActor(a)
|
||||
switch err {
|
||||
default:
|
||||
return types.EmptyInt, aerrors.Escalate(err, "failed to look up actor balance")
|
||||
case hamt.ErrNotFound:
|
||||
return types.NewInt(0), nil
|
||||
case nil:
|
||||
return act.Balance, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
|
||||
rt.ChargeGas(gasCommit)
|
||||
|
||||
// TODO: we can make this more efficient in the future...
|
||||
act, err := rt.state.GetActor(rt.Message().Receiver())
|
||||
if err != nil {
|
||||
rt.Abortf(exitcode.SysErrInternal, "failed to get actor to commit state: %s", err)
|
||||
}
|
||||
|
||||
if act.Head != oldh {
|
||||
rt.Abortf(exitcode.ErrIllegalState, "failed to update, inconsistent base reference")
|
||||
}
|
||||
|
||||
act.Head = newh
|
||||
|
||||
if err := rt.state.SetActor(rt.Message().Receiver(), act); err != nil {
|
||||
rt.Abortf(exitcode.SysErrInternal, "failed to set actor in commit state: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *Runtime) ChargeGas(amount uint64) {
|
||||
toUse := types.NewInt(amount)
|
||||
rt.gasUsed = types.BigAdd(rt.gasUsed, toUse)
|
||||
if rt.gasUsed.GreaterThan(rt.gasAvailable) {
|
||||
rt.Abortf(exitcode.SysErrOutOfGas, "not enough gas: used=%s, available=%s", rt.gasUsed, rt.gasAvailable)
|
||||
}
|
||||
}
|
@ -1,309 +0,0 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"runtime/debug"
|
||||
|
||||
"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/crypto"
|
||||
vmr "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"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type runtimeShim struct {
|
||||
vmctx types.VMContext
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) ResolveAddress(address address.Address) (ret address.Address, ok bool) {
|
||||
r, err := rs.vmctx.LookupID(address)
|
||||
if err != nil { // TODO: check notfound
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "resolve address: %v", err)
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Get(c cid.Cid, o vmr.CBORUnmarshaler) bool {
|
||||
err := rs.vmctx.Storage().Get(c, o)
|
||||
if err != nil { // todo: not found
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "storage get: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Put(x vmr.CBORMarshaler) cid.Cid {
|
||||
c, err := rs.vmctx.Storage().Put(x)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "storage put: %v", err) // todo: spec code?
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
var _ vmr.Runtime = (*runtimeShim)(nil)
|
||||
|
||||
func (rs *runtimeShim) shimCall(f func() interface{}) (rval []byte, aerr aerrors.ActorError) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if ar, ok := r.(aerrors.ActorError); ok {
|
||||
log.Warn("VM.Call failure: ", ar)
|
||||
debug.PrintStack()
|
||||
aerr = ar
|
||||
return
|
||||
}
|
||||
debug.PrintStack()
|
||||
log.Errorf("ERROR")
|
||||
aerr = aerrors.Newf(1, "spec actors failure: %s", r)
|
||||
}
|
||||
}()
|
||||
|
||||
ret := f()
|
||||
switch ret := ret.(type) {
|
||||
case []byte:
|
||||
return ret, nil
|
||||
case *adt.EmptyValue:
|
||||
return nil, nil
|
||||
case cbg.CBORMarshaler:
|
||||
buf := new(bytes.Buffer)
|
||||
if err := ret.MarshalCBOR(buf); err != nil {
|
||||
return nil, aerrors.Absorb(err, 2, "failed to marshal response to cbor")
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, aerrors.New(3, "could not determine type for response from call")
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Message() vmr.Message {
|
||||
var err error
|
||||
|
||||
rawm := *rs.vmctx.Message() // TODO: normalize addresses earlier
|
||||
rawm.From, err = rs.vmctx.LookupID(rawm.From)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "resolve from address: %v", err)
|
||||
}
|
||||
|
||||
rawm.To, err = rs.vmctx.LookupID(rawm.To)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "resolve to address: %v", err)
|
||||
}
|
||||
|
||||
return &rawm
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) ValidateImmediateCallerAcceptAny() {
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) CurrentBalance() abi.TokenAmount {
|
||||
b, err := rs.vmctx.GetBalance(rs.vmctx.Message().To)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ExitCode(err.RetCode()), "get current balance: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) GetActorCodeCID(addr address.Address) (ret cid.Cid, ok bool) {
|
||||
ret, err := rs.vmctx.ActorCodeCID(addr)
|
||||
if err != nil {
|
||||
// todo: notfound
|
||||
rs.Abortf(exitcode.ErrPlaceholder, "%v", err)
|
||||
}
|
||||
|
||||
return ret, true
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) GetRandomness(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness {
|
||||
r, err := rs.vmctx.GetRandomness(personalization, randEpoch, entropy)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.SysErrInternal, "getting randomness: %v", err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Store() vmr.Store {
|
||||
return rs
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) NewActorAddress() address.Address {
|
||||
var b bytes.Buffer
|
||||
if err := rs.ImmediateCaller().MarshalCBOR(&b); err != nil { // todo: spec says cbor; why not just bytes?
|
||||
rs.Abortf(exitcode.ErrSerialization, "writing caller address into a buffer: %v", err)
|
||||
}
|
||||
|
||||
var err error
|
||||
st, err := rs.vmctx.StateTree()
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.SysErrInternal, "getting statetree: %v", err)
|
||||
}
|
||||
act, err := st.GetActor(rs.vmctx.Origin())
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.SysErrInternal, "getting top level actor: %v", err)
|
||||
}
|
||||
|
||||
if err := binary.Write(&b, binary.BigEndian, act.Nonce); err != nil {
|
||||
rs.Abortf(exitcode.ErrSerialization, "writing nonce address into a buffer: %v", err)
|
||||
}
|
||||
if err := binary.Write(&b, binary.BigEndian, uint64(0)); err != nil { // TODO: expose on vm
|
||||
rs.Abortf(exitcode.ErrSerialization, "writing callSeqNum address into a buffer: %v", err)
|
||||
}
|
||||
addr, err := address.NewActorAddress(b.Bytes())
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrSerialization, "create actor address: %v", err)
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) CreateActor(codeId cid.Cid, address address.Address) {
|
||||
var err error
|
||||
st, err := rs.vmctx.StateTree()
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.SysErrInternal, "getting statetree: %v", err)
|
||||
}
|
||||
|
||||
err = st.SetActor(address, &types.Actor{
|
||||
Code: codeId,
|
||||
Head: EmptyObjectCid,
|
||||
Nonce: 0,
|
||||
Balance: big.Zero(),
|
||||
})
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.SysErrInternal, "creating actor entry: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) DeleteActor() {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Syscalls() vmr.Syscalls {
|
||||
return rs.vmctx.Sys()
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) StartSpan(name string) vmr.TraceSpan {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) ValidateImmediateCallerIs(as ...address.Address) {
|
||||
imm, err := rs.vmctx.LookupID(rs.vmctx.Message().From)
|
||||
if err != nil {
|
||||
rs.Abortf(exitcode.ErrIllegalState, "couldn't resolve immediate caller")
|
||||
}
|
||||
|
||||
for _, a := range as {
|
||||
if imm == a {
|
||||
return
|
||||
}
|
||||
}
|
||||
rs.Abortf(exitcode.ErrForbidden, "caller %s is not one of %s", rs.vmctx.Message().From, as)
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) ImmediateCaller() address.Address {
|
||||
return rs.vmctx.Message().From
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Context() context.Context {
|
||||
return rs.vmctx.Context()
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) {
|
||||
panic(aerrors.NewfSkip(2, uint8(code), msg, args...))
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) AbortStateMsg(msg string) {
|
||||
panic(aerrors.NewfSkip(3, 101, msg))
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) ValidateImmediateCallerType(...cid.Cid) {
|
||||
log.Info("validate caller type is dumb")
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) CurrEpoch() abi.ChainEpoch {
|
||||
return rs.vmctx.BlockHeight()
|
||||
}
|
||||
|
||||
type dumbWrapperType struct {
|
||||
val []byte
|
||||
}
|
||||
|
||||
func (dwt *dumbWrapperType) Into(um vmr.CBORUnmarshaler) error {
|
||||
return um.UnmarshalCBOR(bytes.NewReader(dwt.val))
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) Send(to address.Address, method abi.MethodNum, m vmr.CBORMarshaler, value abi.TokenAmount) (vmr.SendReturn, exitcode.ExitCode) {
|
||||
var params []byte
|
||||
if m != nil {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := m.MarshalCBOR(buf); err != nil {
|
||||
rs.Abortf(exitcode.SysErrInvalidParameters, "failed to marshal input parameters: %s", err)
|
||||
}
|
||||
params = buf.Bytes()
|
||||
}
|
||||
|
||||
ret, err := rs.vmctx.Send(to, method, types.BigInt(value), params)
|
||||
if err != nil {
|
||||
if err.IsFatal() {
|
||||
panic(err)
|
||||
}
|
||||
log.Warnf("vmctx send failed: to: %s, method: %d: ret: %d, err: %s", to, method, ret, err)
|
||||
return nil, exitcode.ExitCode(err.RetCode())
|
||||
}
|
||||
return &dumbWrapperType{ret}, 0
|
||||
}
|
||||
|
||||
func (rs *runtimeShim) State() vmr.StateHandle {
|
||||
return &shimStateHandle{rs: rs}
|
||||
}
|
||||
|
||||
type shimStateHandle struct {
|
||||
rs *runtimeShim
|
||||
}
|
||||
|
||||
func (ssh *shimStateHandle) Create(obj vmr.CBORMarshaler) {
|
||||
c, err := ssh.rs.vmctx.Storage().Put(obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ssh.rs.vmctx.Storage().Commit(EmptyObjectCid, c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ssh *shimStateHandle) Readonly(obj vmr.CBORUnmarshaler) {
|
||||
if err := ssh.rs.vmctx.Storage().Get(ssh.rs.vmctx.Storage().GetHead(), obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ssh *shimStateHandle) Transaction(obj vmr.CBORer, f func() interface{}) interface{} {
|
||||
head := ssh.rs.vmctx.Storage().GetHead()
|
||||
if err := ssh.rs.vmctx.Storage().Get(head, obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
out := f()
|
||||
|
||||
c, err := ssh.rs.vmctx.Storage().Put(obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ssh.rs.vmctx.Storage().Commit(head, c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
285
chain/vm/vm.go
285
chain/vm/vm.go
@ -11,7 +11,6 @@ import (
|
||||
|
||||
block "github.com/ipfs/go-block-format"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
@ -33,7 +32,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/bufbstore"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
)
|
||||
|
||||
var log = logging.Logger("vm")
|
||||
@ -60,208 +58,11 @@ type ExecutionResult struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
type VMContext struct {
|
||||
ctx context.Context
|
||||
|
||||
vm *VM
|
||||
state *state.StateTree
|
||||
msg *types.Message
|
||||
height abi.ChainEpoch
|
||||
cst cbor.IpldStore
|
||||
|
||||
gasAvailable types.BigInt
|
||||
gasUsed types.BigInt
|
||||
|
||||
sys runtime.Syscalls
|
||||
|
||||
// root cid of the state of the actor this invocation will be on
|
||||
sroot cid.Cid
|
||||
|
||||
// address that started invoke chain
|
||||
origin address.Address
|
||||
|
||||
internalExecutions []*ExecutionResult
|
||||
}
|
||||
|
||||
// Message is the message that kicked off the current invocation
|
||||
func (vmc *VMContext) Message() *types.Message {
|
||||
return vmc.msg
|
||||
}
|
||||
|
||||
func (vmc *VMContext) GetRandomness(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, aerrors.ActorError) {
|
||||
res, err := vmc.vm.rand.GetRandomness(vmc.ctx, personalization, int64(randEpoch), entropy)
|
||||
if err != nil {
|
||||
return nil, aerrors.Escalate(err, "could not get randomness")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (vmc *VMContext) Sys() runtime.Syscalls {
|
||||
return vmc.sys
|
||||
}
|
||||
|
||||
// Storage interface
|
||||
|
||||
func (vmc *VMContext) Put(i cbg.CBORMarshaler) (cid.Cid, aerrors.ActorError) {
|
||||
c, err := vmc.cst.Put(context.TODO(), i)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, fmt.Sprintf("putting object %T", i))
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (vmc *VMContext) Get(c cid.Cid, out cbg.CBORUnmarshaler) aerrors.ActorError {
|
||||
err := vmc.cst.Get(context.TODO(), c, out)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "getting cid")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vmc *VMContext) GetHead() cid.Cid {
|
||||
return vmc.sroot
|
||||
}
|
||||
|
||||
func (vmc *VMContext) Commit(oldh, newh cid.Cid) aerrors.ActorError {
|
||||
if err := vmc.ChargeGas(gasCommit); err != nil {
|
||||
return aerrors.Wrap(err, "out of gas")
|
||||
}
|
||||
if vmc.sroot != oldh {
|
||||
return aerrors.New(1, "failed to update, inconsistent base reference")
|
||||
}
|
||||
|
||||
vmc.sroot = newh
|
||||
return nil
|
||||
}
|
||||
|
||||
// End of storage interface
|
||||
|
||||
// Storage provides access to the VM storage layer
|
||||
func (vmc *VMContext) Storage() types.Storage {
|
||||
return vmc
|
||||
}
|
||||
|
||||
func (vmc *VMContext) Ipld() cbor.IpldStore {
|
||||
return vmc.cst
|
||||
}
|
||||
|
||||
func (vmc *VMContext) Origin() address.Address {
|
||||
return vmc.origin
|
||||
}
|
||||
|
||||
// Send allows the current execution context to invoke methods on other actors in the system
|
||||
func (vmc *VMContext) Send(to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
|
||||
ctx, span := trace.StartSpan(vmc.ctx, "vmc.Send")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("to", to.String()),
|
||||
trace.Int64Attribute("method", int64(method)),
|
||||
trace.StringAttribute("value", value.String()),
|
||||
)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
From: vmc.msg.To,
|
||||
To: to,
|
||||
Method: method,
|
||||
Value: value,
|
||||
Params: params,
|
||||
GasLimit: vmc.gasAvailable,
|
||||
}
|
||||
|
||||
st := vmc.state
|
||||
if err := st.Snapshot(ctx); err != nil {
|
||||
return nil, aerrors.Fatalf("snapshot failed: %s", err)
|
||||
}
|
||||
defer st.ClearSnapshot()
|
||||
|
||||
ret, err, _ := vmc.vm.send(ctx, msg, vmc, 0)
|
||||
if err != nil {
|
||||
if err := st.Revert(); err != nil {
|
||||
return nil, aerrors.Escalate(err, "failed to revert state tree after failed subcall")
|
||||
}
|
||||
}
|
||||
|
||||
mr := types.MessageReceipt{
|
||||
ExitCode: exitcode.ExitCode(aerrors.RetCode(err)),
|
||||
Return: ret,
|
||||
GasUsed: types.EmptyInt,
|
||||
}
|
||||
|
||||
var es = ""
|
||||
if err != nil {
|
||||
es = err.Error()
|
||||
}
|
||||
er := ExecutionResult{
|
||||
Msg: msg,
|
||||
MsgRct: &mr,
|
||||
Error: es,
|
||||
}
|
||||
|
||||
vmc.internalExecutions = append(vmc.internalExecutions, &er)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// BlockHeight returns the height of the block this message was added to the chain in
|
||||
func (vmc *VMContext) BlockHeight() abi.ChainEpoch {
|
||||
return vmc.height
|
||||
}
|
||||
|
||||
func (vmc *VMContext) GasUsed() types.BigInt {
|
||||
return vmc.gasUsed
|
||||
}
|
||||
|
||||
func (vmc *VMContext) ChargeGas(amount uint64) aerrors.ActorError {
|
||||
toUse := types.NewInt(amount)
|
||||
vmc.gasUsed = types.BigAdd(vmc.gasUsed, toUse)
|
||||
if vmc.gasUsed.GreaterThan(vmc.gasAvailable) {
|
||||
return aerrors.Newf(uint8(exitcode.SysErrOutOfGas), "not enough gas: used=%s, available=%s", vmc.gasUsed, vmc.gasAvailable)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vmc *VMContext) StateTree() (types.StateTree, aerrors.ActorError) {
|
||||
if vmc.msg.To != builtin.InitActorAddr {
|
||||
return nil, aerrors.Escalate(fmt.Errorf("only init actor can access state tree directly"), "invalid use of StateTree")
|
||||
}
|
||||
|
||||
return vmc.state, nil
|
||||
}
|
||||
|
||||
func (vmc *VMContext) ActorCodeCID(addr address.Address) (ret cid.Cid, err error) {
|
||||
act, err := vmc.state.GetActor(addr)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
return act.Code, nil
|
||||
}
|
||||
|
||||
func (vmc *VMContext) LookupID(a address.Address) (address.Address, error) {
|
||||
return vmc.state.LookupID(a)
|
||||
}
|
||||
|
||||
const GasVerifySignature = 50
|
||||
|
||||
func (vmctx *VMContext) VerifySignature(sig *crypto.Signature, act address.Address, data []byte) aerrors.ActorError {
|
||||
if err := vmctx.ChargeGas(GasVerifySignature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if act.Protocol() == address.ID {
|
||||
kaddr, err := ResolveToKeyAddr(vmctx.state, vmctx.cst, act)
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "failed to resolve address to key address")
|
||||
}
|
||||
act = kaddr
|
||||
}
|
||||
|
||||
if err := sigs.Verify(sig, act, data); err != nil {
|
||||
return aerrors.New(2, "signature verification failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Address) (address.Address, aerrors.ActorError) {
|
||||
if addr.Protocol() == address.BLS || addr.Protocol() == address.SECP256K1 {
|
||||
@ -285,56 +86,35 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
|
||||
return aast.Address, nil
|
||||
}
|
||||
|
||||
func (vmctx *VMContext) GetBalance(a address.Address) (types.BigInt, aerrors.ActorError) {
|
||||
act, err := vmctx.state.GetActor(a)
|
||||
switch err {
|
||||
default:
|
||||
return types.EmptyInt, aerrors.Escalate(err, "failed to look up actor balance")
|
||||
case hamt.ErrNotFound:
|
||||
return types.NewInt(0), nil
|
||||
case nil:
|
||||
return act.Balance, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (vmctx *VMContext) Context() context.Context {
|
||||
return vmctx.ctx
|
||||
}
|
||||
|
||||
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
||||
|
||||
type gasChargingBlocks struct {
|
||||
chargeGas func(uint64) aerrors.ActorError
|
||||
chargeGas func(uint64)
|
||||
under cbor.IpldBlockstore
|
||||
}
|
||||
|
||||
func (bs *gasChargingBlocks) Get(c cid.Cid) (block.Block, error) {
|
||||
if err := bs.chargeGas(gasGetObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bs.chargeGas(gasGetObj)
|
||||
blk, err := bs.under.Get(c)
|
||||
if err != nil {
|
||||
return nil, aerrors.Escalate(err, "failed to get block from blockstore")
|
||||
}
|
||||
if err := bs.chargeGas(uint64(len(blk.RawData())) * gasGetPerByte); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bs.chargeGas(uint64(len(blk.RawData())) * gasGetPerByte)
|
||||
|
||||
return blk, nil
|
||||
}
|
||||
|
||||
func (bs *gasChargingBlocks) Put(blk block.Block) error {
|
||||
if err := bs.chargeGas(gasPutObj + uint64(len(blk.RawData()))*gasPutPerByte); err != nil {
|
||||
return err
|
||||
}
|
||||
bs.chargeGas(gasPutObj + uint64(len(blk.RawData()))*gasPutPerByte)
|
||||
|
||||
if err := bs.under.Put(blk); err != nil {
|
||||
return aerrors.Escalate(err, "failed to write data to disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VM) makeVMContext(ctx context.Context, sroot cid.Cid, msg *types.Message, origin address.Address, usedGas types.BigInt) *VMContext {
|
||||
vmc := &VMContext{
|
||||
func (vm *VM) makeRuntime(ctx context.Context, sroot cid.Cid, msg *types.Message, origin address.Address, usedGas types.BigInt) *Runtime {
|
||||
rt := &Runtime{
|
||||
ctx: ctx,
|
||||
vm: vm,
|
||||
state: vm.cstate,
|
||||
@ -347,11 +127,12 @@ func (vm *VM) makeVMContext(ctx context.Context, sroot cid.Cid, msg *types.Messa
|
||||
gasUsed: usedGas,
|
||||
gasAvailable: msg.GasLimit,
|
||||
}
|
||||
vmc.cst = &cbor.BasicIpldStore{
|
||||
Blocks: &gasChargingBlocks{vmc.ChargeGas, vm.cst.Blocks},
|
||||
rt.cst = &cbor.BasicIpldStore{
|
||||
Blocks: &gasChargingBlocks{rt.ChargeGas, vm.cst.Blocks},
|
||||
Atlas: vm.cst.Atlas,
|
||||
}
|
||||
return vmc
|
||||
|
||||
return rt
|
||||
}
|
||||
|
||||
type VM struct {
|
||||
@ -399,8 +180,8 @@ type ApplyRet struct {
|
||||
InternalExecutions []*ExecutionResult
|
||||
}
|
||||
|
||||
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *VMContext,
|
||||
gasCharge uint64) ([]byte, aerrors.ActorError, *VMContext) {
|
||||
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||
gasCharge uint64) ([]byte, aerrors.ActorError, *Runtime) {
|
||||
|
||||
st := vm.cstate
|
||||
|
||||
@ -428,17 +209,15 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *VMContext,
|
||||
gasUsed = types.BigAdd(parent.gasUsed, gasUsed)
|
||||
origin = parent.origin
|
||||
}
|
||||
vmctx := vm.makeVMContext(ctx, toActor.Head, msg, origin, gasUsed)
|
||||
rt := vm.makeRuntime(ctx, toActor.Head, msg, origin, gasUsed)
|
||||
if parent != nil {
|
||||
defer func() {
|
||||
parent.gasUsed = vmctx.gasUsed
|
||||
parent.gasUsed = rt.gasUsed
|
||||
}()
|
||||
}
|
||||
|
||||
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
|
||||
if aerr := vmctx.ChargeGas(gasFundTransfer); aerr != nil {
|
||||
return nil, aerrors.Wrap(aerr, "sending funds"), nil
|
||||
}
|
||||
rt.ChargeGas(gasFundTransfer)
|
||||
|
||||
if err := Transfer(fromActor, toActor, msg.Value); err != nil {
|
||||
return nil, aerrors.Absorb(err, 1, "failed to transfer funds"), nil
|
||||
@ -446,14 +225,14 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *VMContext,
|
||||
}
|
||||
|
||||
if msg.Method != 0 {
|
||||
ret, err := vm.Invoke(toActor, vmctx, msg.Method, msg.Params)
|
||||
ret, err := vm.Invoke(toActor, rt, msg.Method, msg.Params)
|
||||
if !aerrors.IsFatal(err) {
|
||||
toActor.Head = vmctx.Storage().GetHead()
|
||||
toActor.Head = rt.sroot
|
||||
}
|
||||
return ret, err, vmctx
|
||||
return ret, err, rt
|
||||
}
|
||||
|
||||
return nil, nil, vmctx
|
||||
return nil, nil, rt
|
||||
}
|
||||
|
||||
func checkMessage(msg *types.Message) error {
|
||||
@ -546,7 +325,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
}
|
||||
fromActor.Nonce++
|
||||
|
||||
ret, actorErr, vmctx := vm.send(ctx, msg, nil, msgGasCost)
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, msgGasCost)
|
||||
|
||||
if aerrors.IsFatal(actorErr) {
|
||||
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
|
||||
@ -565,8 +344,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
return nil, xerrors.Errorf("revert state failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
gasUsed = rt.gasUsed
|
||||
// refund unused gas
|
||||
gasUsed = vmctx.GasUsed()
|
||||
refund := types.BigMul(types.BigSub(msg.GasLimit, gasUsed), msg.GasPrice)
|
||||
if err := Transfer(gasHolder, fromActor, refund); err != nil {
|
||||
return nil, xerrors.Errorf("failed to refund gas")
|
||||
@ -594,7 +373,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
|
||||
GasUsed: gasUsed,
|
||||
},
|
||||
ActorErr: actorErr,
|
||||
InternalExecutions: vmctx.internalExecutions,
|
||||
InternalExecutions: rt.internalExecutions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -744,26 +523,24 @@ func (vm *VM) SetBlockHeight(h abi.ChainEpoch) {
|
||||
vm.blockHeight = h
|
||||
}
|
||||
|
||||
func (vm *VM) Invoke(act *types.Actor, vmctx *VMContext, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
|
||||
ctx, span := trace.StartSpan(vmctx.ctx, "vm.Invoke")
|
||||
func (vm *VM) Invoke(act *types.Actor, rt *Runtime, method abi.MethodNum, params []byte) ([]byte, aerrors.ActorError) {
|
||||
ctx, span := trace.StartSpan(rt.ctx, "vm.Invoke")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("to", vmctx.Message().To.String()),
|
||||
trace.StringAttribute("to", rt.Message().Receiver().String()),
|
||||
trace.Int64Attribute("method", int64(method)),
|
||||
trace.StringAttribute("value", vmctx.Message().Value.String()),
|
||||
trace.StringAttribute("value", rt.Message().ValueReceived().String()),
|
||||
)
|
||||
}
|
||||
|
||||
var oldCtx context.Context
|
||||
oldCtx, vmctx.ctx = vmctx.ctx, ctx
|
||||
oldCtx, rt.ctx = rt.ctx, ctx
|
||||
defer func() {
|
||||
vmctx.ctx = oldCtx
|
||||
rt.ctx = oldCtx
|
||||
}()
|
||||
if err := vmctx.ChargeGas(gasInvoke); err != nil {
|
||||
return nil, aerrors.Wrap(err, "invokeing")
|
||||
}
|
||||
ret, err := vm.inv.Invoke(act, vmctx, method, params)
|
||||
rt.ChargeGas(gasInvoke)
|
||||
ret, err := vm.inv.Invoke(act, rt, method, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user