dbbcf4b2ee
This is now "FVM" native. Changes include: 1. Don't treat "trace" messages like off-chain messages. E.g., don't include CIDs, versions, etc. 2. Include IPLD codecs where applicable. 3. Remove fields that aren't filled by the FVM (timing, some errors, code locations, etc.).
633 lines
18 KiB
Go
633 lines
18 KiB
Go
package vm
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/ipfs/go-cid"
|
|
ipldcbor "github.com/ipfs/go-ipld-cbor"
|
|
"go.opencensus.io/trace"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
actorstypes "github.com/filecoin-project/go-state-types/actors"
|
|
"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"
|
|
rt0 "github.com/filecoin-project/specs-actors/actors/runtime"
|
|
rt2 "github.com/filecoin-project/specs-actors/v2/actors/runtime"
|
|
rt3 "github.com/filecoin-project/specs-actors/v3/actors/runtime"
|
|
rt4 "github.com/filecoin-project/specs-actors/v4/actors/runtime"
|
|
rt5 "github.com/filecoin-project/specs-actors/v5/actors/runtime"
|
|
rt6 "github.com/filecoin-project/specs-actors/v6/actors/runtime"
|
|
rt7 "github.com/filecoin-project/specs-actors/v7/actors/runtime"
|
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
"github.com/filecoin-project/lotus/chain/actors"
|
|
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
|
"github.com/filecoin-project/lotus/chain/state"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
)
|
|
|
|
type Message struct {
|
|
msg types.Message
|
|
}
|
|
|
|
func (m *Message) Caller() address.Address {
|
|
if m.msg.From.Protocol() != address.ID {
|
|
panic("runtime message has a non-ID caller")
|
|
}
|
|
return m.msg.From
|
|
}
|
|
|
|
func (m *Message) Receiver() address.Address {
|
|
if m.msg.To != address.Undef && m.msg.To.Protocol() != address.ID {
|
|
panic("runtime message has a non-ID receiver")
|
|
}
|
|
return m.msg.To
|
|
}
|
|
|
|
func (m *Message) ValueReceived() abi.TokenAmount {
|
|
return m.msg.Value
|
|
}
|
|
|
|
// EnableDetailedTracing has different behaviour in the LegacyVM and FVM.
|
|
// In the LegacyVM, it enables detailed gas tracing, slowing down execution.
|
|
// In the FVM, it enables execution traces, which are primarily used to observe subcalls.
|
|
var EnableDetailedTracing = os.Getenv("LOTUS_VM_ENABLE_TRACING") == "1"
|
|
|
|
type Runtime struct {
|
|
rt7.Message
|
|
rt7.Syscalls
|
|
|
|
ctx context.Context
|
|
|
|
vm *LegacyVM
|
|
state *state.StateTree
|
|
height abi.ChainEpoch
|
|
cst ipldcbor.IpldStore
|
|
pricelist Pricelist
|
|
|
|
gasAvailable int64
|
|
gasUsed int64
|
|
|
|
// address that started invoke chain
|
|
origin address.Address
|
|
originNonce uint64
|
|
|
|
executionTrace types.ExecutionTrace
|
|
depth uint64
|
|
numActorsCreated uint64
|
|
allowInternal bool
|
|
callerValidated bool
|
|
lastGasChargeTime time.Time
|
|
lastGasCharge *types.GasTrace
|
|
}
|
|
|
|
func (rt *Runtime) BaseFee() abi.TokenAmount {
|
|
return rt.vm.baseFee
|
|
}
|
|
|
|
func (rt *Runtime) NetworkVersion() network.Version {
|
|
return rt.vm.networkVersion
|
|
}
|
|
|
|
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
|
|
cs, err := rt.vm.GetCircSupply(rt.ctx)
|
|
if err != nil {
|
|
rt.Abortf(exitcode.ErrIllegalState, "failed to get total circ supply: %s", err)
|
|
}
|
|
|
|
return cs
|
|
}
|
|
|
|
func (rt *Runtime) ResolveAddress(addr address.Address) (ret address.Address, ok bool) {
|
|
r, err := rt.state.LookupID(addr)
|
|
if err != nil {
|
|
if xerrors.Is(err, types.ErrActorNotFound) {
|
|
return address.Undef, false
|
|
}
|
|
panic(aerrors.Fatalf("failed to resolve address %s: %s", addr, err))
|
|
}
|
|
return r, true
|
|
}
|
|
|
|
type notFoundErr interface {
|
|
IsNotFound() bool
|
|
}
|
|
|
|
func (rt *Runtime) StoreGet(c cid.Cid, o cbor.Unmarshaler) bool {
|
|
if err := rt.cst.Get(context.TODO(), c, o); err != nil {
|
|
var nfe notFoundErr
|
|
if xerrors.As(err, &nfe) && nfe.IsNotFound() {
|
|
if xerrors.As(err, new(ipldcbor.SerializationError)) {
|
|
panic(aerrors.Newf(exitcode.ErrSerialization, "failed to unmarshal cbor object %s", err))
|
|
}
|
|
return false
|
|
}
|
|
|
|
panic(aerrors.Fatalf("failed to get cbor object %s: %s", c, err))
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (rt *Runtime) StorePut(x cbor.Marshaler) cid.Cid {
|
|
c, err := rt.cst.Put(context.TODO(), x)
|
|
if err != nil {
|
|
if xerrors.As(err, new(ipldcbor.SerializationError)) {
|
|
panic(aerrors.Newf(exitcode.ErrSerialization, "failed to marshal cbor object %s", err))
|
|
}
|
|
panic(aerrors.Fatalf("failed to put cbor object: %s", err))
|
|
}
|
|
return c
|
|
}
|
|
|
|
var _ rt0.Runtime = (*Runtime)(nil)
|
|
var _ rt5.Runtime = (*Runtime)(nil)
|
|
var _ rt2.Runtime = (*Runtime)(nil)
|
|
var _ rt3.Runtime = (*Runtime)(nil)
|
|
var _ rt4.Runtime = (*Runtime)(nil)
|
|
var _ rt5.Runtime = (*Runtime)(nil)
|
|
var _ rt6.Runtime = (*Runtime)(nil)
|
|
var _ rt7.Runtime = (*Runtime)(nil)
|
|
|
|
func (rt *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.Warnf("LegacyVM.Call failure in call from: %s to %s: %+v", rt.Caller(), rt.Receiver(), ar)
|
|
aerr = ar
|
|
return
|
|
}
|
|
// log.Desugar().WithOptions(zap.AddStacktrace(zapcore.ErrorLevel)).
|
|
// Sugar().Errorf("spec actors failure: %s", r)
|
|
log.Errorf("spec actors failure: %s", r)
|
|
if rt.NetworkVersion() <= network.Version3 {
|
|
aerr = aerrors.Newf(1, "spec actors failure: %s", r)
|
|
} else {
|
|
aerr = aerrors.Newf(exitcode.SysErrIllegalInstruction, "spec actors failure: %s", r)
|
|
}
|
|
}
|
|
}()
|
|
|
|
ret := f()
|
|
|
|
if !rt.callerValidated {
|
|
rt.Abortf(exitcode.SysErrorIllegalActor, "Caller MUST be validated during method execution")
|
|
}
|
|
|
|
switch ret := ret.(type) {
|
|
case []byte:
|
|
return ret, nil
|
|
case *abi.EmptyValue:
|
|
return nil, nil
|
|
case cbor.Marshaler:
|
|
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 (rt *Runtime) ValidateImmediateCallerAcceptAny() {
|
|
rt.abortIfAlreadyValidated()
|
|
return
|
|
}
|
|
|
|
func (rt *Runtime) CurrentBalance() abi.TokenAmount {
|
|
b, err := rt.GetBalance(rt.Receiver())
|
|
if err != nil {
|
|
rt.Abortf(err.RetCode(), "get current balance: %v", err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (rt *Runtime) GetActorCodeCID(addr address.Address) (ret cid.Cid, ok bool) {
|
|
act, err := rt.state.GetActor(addr)
|
|
if err != nil {
|
|
if xerrors.Is(err, types.ErrActorNotFound) {
|
|
return cid.Undef, false
|
|
}
|
|
|
|
panic(aerrors.Fatalf("failed to get actor: %s", err))
|
|
}
|
|
|
|
return act.Code, true
|
|
}
|
|
|
|
func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness {
|
|
res, err := rt.vm.rand.GetChainRandomness(rt.ctx, personalization, randEpoch, entropy)
|
|
|
|
if err != nil {
|
|
panic(aerrors.Fatalf("could not get ticket randomness: %s", err))
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (rt *Runtime) GetRandomnessFromBeacon(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness {
|
|
res, err := rt.vm.rand.GetBeaconRandomness(rt.ctx, personalization, randEpoch, entropy)
|
|
|
|
if err != nil {
|
|
panic(aerrors.Fatalf("could not get beacon randomness: %s", err))
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (rt *Runtime) NewActorAddress() address.Address {
|
|
var b bytes.Buffer
|
|
oa, _ := ResolveToDeterministicAddr(rt.vm.cstate, rt.vm.cst, rt.origin)
|
|
if err := oa.MarshalCBOR(&b); err != nil { // todo: spec says cbor; why not just bytes?
|
|
panic(aerrors.Fatalf("writing caller address into a buffer: %v", err))
|
|
}
|
|
|
|
if err := binary.Write(&b, binary.BigEndian, rt.originNonce); err != nil {
|
|
panic(aerrors.Fatalf("writing nonce address into a buffer: %v", err))
|
|
}
|
|
if err := binary.Write(&b, binary.BigEndian, rt.numActorsCreated); err != nil { // TODO: expose on vm
|
|
panic(aerrors.Fatalf("writing callSeqNum address into a buffer: %v", err))
|
|
}
|
|
addr, err := address.NewActorAddress(b.Bytes())
|
|
if err != nil {
|
|
panic(aerrors.Fatalf("create actor address: %v", err))
|
|
}
|
|
|
|
rt.incrementNumActorsCreated()
|
|
return addr
|
|
}
|
|
|
|
func (rt *Runtime) CreateActor(codeID cid.Cid, addr address.Address) {
|
|
if addr == address.Undef && rt.NetworkVersion() >= network.Version7 {
|
|
rt.Abortf(exitcode.SysErrorIllegalArgument, "CreateActor with Undef address")
|
|
}
|
|
act, aerr := rt.vm.areg.Create(codeID, rt)
|
|
if aerr != nil {
|
|
rt.Abortf(aerr.RetCode(), aerr.Error())
|
|
}
|
|
|
|
_, err := rt.state.GetActor(addr)
|
|
if err == nil {
|
|
rt.Abortf(exitcode.SysErrorIllegalArgument, "Actor address already exists")
|
|
}
|
|
|
|
rt.chargeGas(rt.Pricelist().OnCreateActor())
|
|
|
|
err = rt.state.SetActor(addr, act)
|
|
if err != nil {
|
|
panic(aerrors.Fatalf("creating actor entry: %v", err))
|
|
}
|
|
_ = rt.chargeGasSafe(gasOnActorExec)
|
|
}
|
|
|
|
// DeleteActor deletes the executing actor from the state tree, transferring
|
|
// any balance to beneficiary.
|
|
// Aborts if the beneficiary does not exist or is the calling actor.
|
|
// May only be called by the actor itself.
|
|
func (rt *Runtime) DeleteActor(beneficiary address.Address) {
|
|
rt.chargeGas(rt.Pricelist().OnDeleteActor())
|
|
act, err := rt.state.GetActor(rt.Receiver())
|
|
if err != nil {
|
|
if xerrors.Is(err, types.ErrActorNotFound) {
|
|
rt.Abortf(exitcode.SysErrorIllegalActor, "failed to load actor in delete actor: %s", err)
|
|
}
|
|
panic(aerrors.Fatalf("failed to get actor: %s", err))
|
|
}
|
|
if !act.Balance.IsZero() {
|
|
// TODO: Should be safe to drop the version-check,
|
|
// since only the paych actor called this pre-version 7, but let's leave it for now
|
|
if rt.NetworkVersion() >= network.Version7 {
|
|
beneficiaryId, found := rt.ResolveAddress(beneficiary)
|
|
if !found {
|
|
rt.Abortf(exitcode.SysErrorIllegalArgument, "beneficiary doesn't exist")
|
|
}
|
|
|
|
if beneficiaryId == rt.Receiver() {
|
|
rt.Abortf(exitcode.SysErrorIllegalArgument, "benefactor cannot be beneficiary")
|
|
}
|
|
}
|
|
|
|
// Transfer the executing actor's balance to the beneficiary
|
|
if err := rt.vm.transfer(rt.Receiver(), beneficiary, act.Balance, rt.NetworkVersion()); err != nil {
|
|
panic(aerrors.Fatalf("failed to transfer balance to beneficiary actor: %s", err))
|
|
}
|
|
}
|
|
|
|
// Delete the executing actor
|
|
if err := rt.state.DeleteActor(rt.Receiver()); err != nil {
|
|
panic(aerrors.Fatalf("failed to delete actor: %s", err))
|
|
}
|
|
_ = rt.chargeGasSafe(gasOnActorExec)
|
|
}
|
|
|
|
func (rt *Runtime) StartSpan(name string) func() {
|
|
panic("implement me")
|
|
}
|
|
|
|
func (rt *Runtime) ValidateImmediateCallerIs(as ...address.Address) {
|
|
rt.abortIfAlreadyValidated()
|
|
imm := rt.Caller()
|
|
|
|
for _, a := range as {
|
|
if imm == a {
|
|
return
|
|
}
|
|
}
|
|
rt.Abortf(exitcode.SysErrForbidden, "caller %s is not one of %s", rt.Caller(), as)
|
|
}
|
|
|
|
func (rt *Runtime) Context() context.Context {
|
|
return rt.ctx
|
|
}
|
|
|
|
func (rt *Runtime) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) {
|
|
log.Warnf("Abortf: " + fmt.Sprintf(msg, args...))
|
|
panic(aerrors.NewfSkip(2, code, msg, args...))
|
|
}
|
|
|
|
func (rt *Runtime) AbortStateMsg(msg string) {
|
|
panic(aerrors.NewfSkip(3, 101, msg))
|
|
}
|
|
|
|
func (rt *Runtime) ValidateImmediateCallerType(ts ...cid.Cid) {
|
|
rt.abortIfAlreadyValidated()
|
|
callerCid, ok := rt.GetActorCodeCID(rt.Caller())
|
|
if !ok {
|
|
panic(aerrors.Fatalf("failed to lookup code cid for caller"))
|
|
}
|
|
for _, t := range ts {
|
|
if t == callerCid {
|
|
return
|
|
}
|
|
|
|
// this really only for genesis in tests; nv16 will be running on FVM anyway.
|
|
if nv := rt.NetworkVersion(); nv >= network.Version16 {
|
|
av, err := actorstypes.VersionForNetwork(nv)
|
|
if err != nil {
|
|
panic(aerrors.Fatalf("failed to get actors version for network version %d", nv))
|
|
}
|
|
|
|
name := actors.CanonicalName(builtin.ActorNameByCode(t))
|
|
ac, ok := actors.GetActorCodeID(av, name)
|
|
if ok && ac == callerCid {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
rt.Abortf(exitcode.SysErrForbidden, "caller cid type %q was not one of %v", callerCid, ts)
|
|
}
|
|
|
|
func (rt *Runtime) CurrEpoch() abi.ChainEpoch {
|
|
return rt.height
|
|
}
|
|
|
|
func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m cbor.Marshaler, value abi.TokenAmount, out cbor.Er) exitcode.ExitCode {
|
|
if !rt.allowInternal {
|
|
rt.Abortf(exitcode.SysErrorIllegalActor, "runtime.Send() is currently disallowed")
|
|
}
|
|
var params []byte
|
|
if m != nil {
|
|
buf := new(bytes.Buffer)
|
|
if err := m.MarshalCBOR(buf); err != nil {
|
|
rt.Abortf(exitcode.ErrSerialization, "failed to marshal input parameters: %s", err)
|
|
}
|
|
params = buf.Bytes()
|
|
}
|
|
|
|
ret, err := rt.internalSend(rt.Receiver(), to, method, value, params)
|
|
if err != nil {
|
|
if err.IsFatal() {
|
|
panic(err)
|
|
}
|
|
log.Warnf("vmctx send failed: from: %s to: %s, method: %d: err: %s", rt.Receiver(), to, method, err)
|
|
return err.RetCode()
|
|
}
|
|
_ = rt.chargeGasSafe(gasOnActorExec)
|
|
|
|
if err := out.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
|
rt.Abortf(exitcode.ErrSerialization, "failed to unmarshal return value: %s", err)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
|
|
start := build.Clock.Now()
|
|
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: from,
|
|
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, errSend, subrt := rt.vm.send(ctx, msg, rt, nil, start)
|
|
if errSend != nil {
|
|
if errRevert := st.Revert(); errRevert != nil {
|
|
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
|
|
}
|
|
}
|
|
|
|
if subrt != nil {
|
|
rt.numActorsCreated = subrt.numActorsCreated
|
|
rt.executionTrace.Subcalls = append(rt.executionTrace.Subcalls, subrt.executionTrace)
|
|
}
|
|
return ret, errSend
|
|
}
|
|
|
|
func (rt *Runtime) StateCreate(obj cbor.Marshaler) {
|
|
c := rt.StorePut(obj)
|
|
err := rt.stateCommit(EmptyObjectCid, c)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to commit state after creating object: %w", err))
|
|
}
|
|
}
|
|
|
|
func (rt *Runtime) StateReadonly(obj cbor.Unmarshaler) {
|
|
act, err := rt.state.GetActor(rt.Receiver())
|
|
if err != nil {
|
|
rt.Abortf(exitcode.SysErrorIllegalArgument, "failed to get actor for Readonly state: %s", err)
|
|
}
|
|
rt.StoreGet(act.Head, obj)
|
|
}
|
|
|
|
func (rt *Runtime) StateTransaction(obj cbor.Er, f func()) {
|
|
if obj == nil {
|
|
rt.Abortf(exitcode.SysErrorIllegalActor, "Must not pass nil to Transaction()")
|
|
}
|
|
|
|
act, err := rt.state.GetActor(rt.Receiver())
|
|
if err != nil {
|
|
rt.Abortf(exitcode.SysErrorIllegalActor, "failed to get actor for Transaction: %s", err)
|
|
}
|
|
baseState := act.Head
|
|
rt.StoreGet(baseState, obj)
|
|
|
|
rt.allowInternal = false
|
|
f()
|
|
rt.allowInternal = true
|
|
|
|
c := rt.StorePut(obj)
|
|
|
|
err = rt.stateCommit(baseState, c)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to commit state after transaction: %w", err))
|
|
}
|
|
}
|
|
|
|
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 types.ErrActorNotFound:
|
|
return types.NewInt(0), nil
|
|
case nil:
|
|
return act.Balance, nil
|
|
}
|
|
}
|
|
|
|
func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
|
|
// TODO: we can make this more efficient in the future...
|
|
act, err := rt.state.GetActor(rt.Receiver())
|
|
if err != nil {
|
|
return aerrors.Escalate(err, "failed to get actor to commit state")
|
|
}
|
|
|
|
if act.Head != oldh {
|
|
return aerrors.Fatal("failed to update, inconsistent base reference")
|
|
}
|
|
|
|
act.Head = newh
|
|
|
|
if err := rt.state.SetActor(rt.Receiver(), act); err != nil {
|
|
return aerrors.Fatalf("failed to set actor in commit state: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (rt *Runtime) finilizeGasTracing() {
|
|
if EnableDetailedTracing {
|
|
if rt.lastGasCharge != nil {
|
|
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ChargeGas is spec actors function
|
|
func (rt *Runtime) ChargeGas(name string, compute int64, virtual int64) {
|
|
err := rt.chargeGasInternal(newGasCharge(name, compute, 0).WithVirtual(virtual, 0), 1)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (rt *Runtime) chargeGas(gas GasCharge) {
|
|
err := rt.chargeGasInternal(gas, 1)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func (rt *Runtime) chargeGasFunc(skip int) func(GasCharge) {
|
|
return func(gas GasCharge) {
|
|
err := rt.chargeGasInternal(gas, 1+skip)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func (rt *Runtime) chargeGasInternal(gas GasCharge, skip int) aerrors.ActorError {
|
|
toUse := gas.Total()
|
|
if EnableDetailedTracing {
|
|
now := build.Clock.Now()
|
|
if rt.lastGasCharge != nil {
|
|
rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
|
|
}
|
|
|
|
gasTrace := types.GasTrace{
|
|
Name: gas.Name,
|
|
|
|
TotalGas: toUse,
|
|
ComputeGas: gas.ComputeGas,
|
|
StorageGas: gas.StorageGas,
|
|
}
|
|
|
|
rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
|
|
rt.lastGasChargeTime = now
|
|
rt.lastGasCharge = &gasTrace
|
|
}
|
|
|
|
// overflow safe
|
|
if rt.gasUsed > rt.gasAvailable-toUse {
|
|
gasUsed := rt.gasUsed
|
|
rt.gasUsed = rt.gasAvailable
|
|
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d, use=%d",
|
|
gasUsed, rt.gasAvailable, toUse)
|
|
}
|
|
rt.gasUsed += toUse
|
|
return nil
|
|
}
|
|
|
|
func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
|
|
return rt.chargeGasInternal(gas, 1)
|
|
}
|
|
|
|
func (rt *Runtime) Pricelist() Pricelist {
|
|
return rt.pricelist
|
|
}
|
|
|
|
func (rt *Runtime) incrementNumActorsCreated() {
|
|
rt.numActorsCreated++
|
|
}
|
|
|
|
func (rt *Runtime) abortIfAlreadyValidated() {
|
|
if rt.callerValidated {
|
|
rt.Abortf(exitcode.SysErrorIllegalActor, "Method must validate caller identity exactly once")
|
|
}
|
|
rt.callerValidated = true
|
|
}
|
|
|
|
func (rt *Runtime) Log(level rtt.LogLevel, msg string, args ...interface{}) {
|
|
switch level {
|
|
case rtt.DEBUG:
|
|
actorLog.Debugf(msg, args...)
|
|
case rtt.INFO:
|
|
actorLog.Infof(msg, args...)
|
|
case rtt.WARN:
|
|
actorLog.Warnf(msg, args...)
|
|
case rtt.ERROR:
|
|
actorLog.Errorf(msg, args...)
|
|
}
|
|
}
|