VM Runtime Environment (Inside the VM) #


Receipts #

A MessageReceipt contains the result of a top-level message execution.

A syntactically valid receipt has:

  • a non-negative ExitCode,
  • a non empty ReturnValue only if the exit code is zero,
  • a non-negative GasUsed.

vm/runtime interface #

package runtime

import (
	"bytes"
	"context"
	"io"

	"github.com/filecoin-project/go-address"
	addr "github.com/filecoin-project/go-address"
	cid "github.com/ipfs/go-cid"

	abi "github.com/filecoin-project/specs-actors/actors/abi"
	crypto "github.com/filecoin-project/specs-actors/actors/crypto"
	exitcode "github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
)

// Specifies importance of message, LogLevel numbering is consistent with the uber-go/zap package.
type LogLevel int

const (
	// DebugLevel logs are typically voluminous, and are usually disabled in
	// production.
	DEBUG LogLevel = iota - 1
	// InfoLevel is the default logging priority.
	INFO
	// WarnLevel logs are more important than Info, but don't need individual
	// human review.
	WARN
	// ErrorLevel logs are high-priority. If an application is running smoothly,
	// it shouldn't generate any error-level logs.
	ERROR
)

// Runtime is the VM's internal runtime object.
// this is everything that is accessible to actors, beyond parameters.
type Runtime interface {
	// Information related to the current message being executed.
	Message() Message

	// The current chain epoch number. The genesis block has epoch zero.
	CurrEpoch() abi.ChainEpoch

	// Validates the caller against some predicate.
	// Exported actor methods must invoke at least one caller validation before returning.
	ValidateImmediateCallerAcceptAny()
	ValidateImmediateCallerIs(addrs ...addr.Address)
	ValidateImmediateCallerType(types ...cid.Cid)

	// The balance of the receiver.
	CurrentBalance() abi.TokenAmount

	// Resolves an address of any protocol to an ID address (via the Init actor's table).
	// This allows resolution of externally-provided SECP, BLS, or actor addresses to the canonical form.
	// If the argument is an ID address it is returned directly.
	ResolveAddress(address addr.Address) (addr.Address, bool)

	// Look up the code ID at an actor address.
	GetActorCodeCID(addr addr.Address) (ret cid.Cid, ok bool)

	// Randomness returns a (pseudo)random byte array drawing from a
	// random beacon at a given epoch and incorporating reequisite entropy
	GetRandomness(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness

	// Provides a handle for the actor's state object.
	State() StateHandle

	Store() Store

	// Sends a message to another actor, returning the exit code and return value envelope.
	// If the invoked method does not return successfully, its state changes (and that of any messages it sent in turn)
	// will be rolled back.
	// The result is never a bare nil, but may be (a wrapper of) adt.Empty.
	Send(toAddr addr.Address, methodNum abi.MethodNum, params CBORMarshaler, value abi.TokenAmount) (SendReturn, exitcode.ExitCode)

	// Halts execution upon an error from which the receiver cannot recover. The caller will receive the exitcode and
	// an empty return value. State changes made within this call will be rolled back.
	// This method does not return.
	// The message and args are for diagnostic purposes and do not persist on chain. They should be suitable for
	// passing to fmt.Errorf(msg, args...).
	Abortf(errExitCode exitcode.ExitCode, msg string, args ...interface{})

	// Computes an address for a new actor. The returned address is intended to uniquely refer to
	// the actor even in the event of a chain re-org (whereas an ID-address might refer to a
	// different actor after messages are re-ordered).
	// Always an ActorExec address.
	NewActorAddress() addr.Address

	// Creates an actor with code `codeID` and address `address`, with empty state. May only be called by Init actor.
	CreateActor(codeId cid.Cid, address addr.Address)

	// Deletes the executing actor from the state tree, transferring any balance to beneficiary.
	// Aborts if the beneficiary does not exist.
	// May only be called by the actor itself.
	DeleteActor(beneficiary addr.Address)

	// Provides the system call interface.
	Syscalls() Syscalls

	// Returns the total token supply in circulation at the beginning of the current epoch.
	// The circulating supply is the sum of:
	// - rewards emitted by the reward actor,
	// - funds vested from lock-ups in the genesis state,
	// less the sum of:
	// - funds burnt,
	// - pledge collateral locked in storage miner actors (recorded in the storage power actor)
	// - deal collateral locked by the storage market actor
	TotalFilCircSupply() abi.TokenAmount

	// Provides a Go context for use by HAMT, etc.
	// The VM is intended to provide an idealised machine abstraction, with infinite storage etc, so this context
	// should not be used by actor code directly.
	Context() context.Context

	// Starts a new tracing span. The span must be End()ed explicitly, typically with a deferred invocation.
	StartSpan(name string) TraceSpan

	// ChargeGas charges specified amount of `gas` for execution.
	// `name` provides information about gas charging point
	// `virtual` sets virtual amount of gas to charge, this amount is not counted
	// toward execution cost. This functionality is used for observing global changes
	// in total gas charged if amount of gas charged was to be changed.
	ChargeGas(name string, gas int64, virtual int64)

	// Note events that may make debugging easier
	Log(level LogLevel, msg string, args ...interface{})
}

// Store defines the storage module exposed to actors.
type Store interface {
	// Retrieves and deserializes an object from the store into `o`. Returns whether successful.
	Get(c cid.Cid, o CBORUnmarshaler) bool
	// Serializes and stores an object, returning its CID.
	Put(x CBORMarshaler) cid.Cid
}

// Message contains information available to the actor about the executing message.
type Message interface {
	// The address of the immediate calling actor. Always an ID-address.
	Caller() addr.Address

	// The address of the actor receiving the message. Always an ID-address.
	Receiver() addr.Address

	// The value attached to the message being processed, implicitly added to CurrentBalance() before method invocation.
	ValueReceived() abi.TokenAmount
}

// Pure functions implemented as primitives by the runtime.
type Syscalls interface {
	// Verifies that a signature is valid for an address and plaintext.
	VerifySignature(signature crypto.Signature, signer addr.Address, plaintext []byte) error
	// Hashes input data using blake2b with 256 bit output.
	HashBlake2b(data []byte) [32]byte
	// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes.
	ComputeUnsealedSectorCID(reg abi.RegisteredSealProof, pieces []abi.PieceInfo) (cid.Cid, error)
	// Verifies a sector seal proof.
	VerifySeal(vi abi.SealVerifyInfo) error

	BatchVerifySeals(vis map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error)

	// Verifies a proof of spacetime.
	VerifyPoSt(vi abi.WindowPoStVerifyInfo) error
	// Verifies that two block headers provide proof of a consensus fault:
	// - both headers mined by the same actor
	// - headers are different
	// - first header is of the same or lower epoch as the second
	// - the headers provide evidence of a fault (see the spec for the different fault types).
	// The parameters are all serialized block headers. The third "extra" parameter is consulted only for
	// the "parent grinding fault", in which case it must be the sibling of h1 (same parent tipset) and one of the
	// blocks in an ancestor of h2.
	// Returns nil and an error if the headers don't prove a fault.
	VerifyConsensusFault(h1, h2, extra []byte) (*ConsensusFault, error)
}

// The return type from a message send from one actor to another. This abstracts over the internal representation of
// the return, in particular whether it has been serialized to bytes or just passed through.
// Production code is expected to de/serialize, but test and other code may pass the value straight through.
type SendReturn interface {
	Into(CBORUnmarshaler) error
}

// Provides (minimal) tracing facilities to actor code.
type TraceSpan interface {
	// Ends the span
	End()
}

// StateHandle provides mutable, exclusive access to actor state.
type StateHandle interface {
	// Create initializes the state object.
	// This is only valid in a constructor function and when the state has not yet been initialized.
	Create(obj CBORMarshaler)

	// Readonly loads a readonly copy of the state into the argument.
	//
	// Any modification to the state is illegal and will result in an abort.
	Readonly(obj CBORUnmarshaler)

	// Transaction loads a mutable version of the state into the `obj` argument and protects
	// the execution from side effects (including message send).
	//
	// The second argument is a function which allows the caller to mutate the state.
	// The return value from that function will be returned from the call to Transaction().
	//
	// If the state is modified after this function returns, execution will abort.
	//
	// The gas cost of this method is that of a Store.Put of the mutated state object.
	//
	// Note: the Go signature is not ideal due to lack of type system power.
	//
	// # Usage
	// ```go
	// var state SomeState
	// ret := rt.State().Transaction(&state, func() (interface{}) {
	//   // make some changes
	//	 st.ImLoaded = True
	//   return st.Thing, nil
	// })
	// // state.ImLoaded = False // BAD!! state is readonly outside the lambda, it will panic
	// ```
	Transaction(obj CBORer, f func() interface{}) interface{}
}

// Result of checking two headers for a consensus fault.
type ConsensusFault struct {
	// Address of the miner at fault (always an ID address).
	Target addr.Address
	// Epoch of the fault, which is the higher epoch of the two blocks causing it.
	Epoch abi.ChainEpoch
	// Type of fault.
	Type ConsensusFaultType
}

type ConsensusFaultType int64

const (
	//ConsensusFaultNone             ConsensusFaultType = 0
	ConsensusFaultDoubleForkMining ConsensusFaultType = 1
	ConsensusFaultParentGrinding   ConsensusFaultType = 2
	ConsensusFaultTimeOffsetMining ConsensusFaultType = 3
)

// These interfaces are intended to match those from whyrusleeping/cbor-gen, such that code generated from that
// system is automatically usable here (but not mandatory).
type CBORMarshaler interface {
	MarshalCBOR(w io.Writer) error
}

type CBORUnmarshaler interface {
	UnmarshalCBOR(r io.Reader) error
}

type CBORer interface {
	CBORMarshaler
	CBORUnmarshaler
}

// Wraps already-serialized bytes as CBOR-marshalable.
type CBORBytes []byte

func (b CBORBytes) MarshalCBOR(w io.Writer) error {
	_, err := w.Write(b)
	return err
}

func (b *CBORBytes) UnmarshalCBOR(r io.Reader) error {
	var c bytes.Buffer
	_, err := c.ReadFrom(r)
	*b = c.Bytes()
	return err
}

vm/runtime implementation #

package impl

import (
	"bytes"
	"encoding/binary"
	"fmt"

	addr "github.com/filecoin-project/go-address"
	actor "github.com/filecoin-project/specs-actors/actors"
	abi "github.com/filecoin-project/specs-actors/actors/abi"
	builtin "github.com/filecoin-project/specs-actors/actors/builtin"
	acctact "github.com/filecoin-project/specs-actors/actors/builtin/account"
	initact "github.com/filecoin-project/specs-actors/actors/builtin/init"
	vmr "github.com/filecoin-project/specs-actors/actors/runtime"
	exitcode "github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
	indices "github.com/filecoin-project/specs-actors/actors/runtime/indices"
	ipld "github.com/filecoin-project/specs/libraries/ipld"
	chain "github.com/filecoin-project/specs/systems/filecoin_blockchain/struct/chain"
	actstate "github.com/filecoin-project/specs/systems/filecoin_vm/actor"
	msg "github.com/filecoin-project/specs/systems/filecoin_vm/message"
	gascost "github.com/filecoin-project/specs/systems/filecoin_vm/runtime/gascost"
	st "github.com/filecoin-project/specs/systems/filecoin_vm/state_tree"
	util "github.com/filecoin-project/specs/util"
	cid "github.com/ipfs/go-cid"
	cbornode "github.com/ipfs/go-ipld-cbor"
	mh "github.com/multiformats/go-multihash"
)

type ActorSubstateCID = actor.ActorSubstateCID
type ExitCode = exitcode.ExitCode
type CallerPattern = vmr.CallerPattern
type Runtime = vmr.Runtime
type InvocInput = vmr.InvocInput
type InvocOutput = vmr.InvocOutput
type ActorStateHandle = vmr.ActorStateHandle

var EnsureErrorCode = exitcode.EnsureErrorCode

type Bytes = util.Bytes

var Assert = util.Assert
var IMPL_FINISH = util.IMPL_FINISH
var IMPL_TODO = util.IMPL_TODO
var TODO = util.TODO

var EmptyCBOR cid.Cid

type RuntimeError struct {
	ExitCode ExitCode
	ErrMsg   string
}

func init() {
	n, err := cbornode.WrapObject(map[string]struct{}{}, mh.SHA2_256, -1)
	Assert(err == nil)
	EmptyCBOR = n.Cid()
}

func (x *RuntimeError) String() string {
	ret := fmt.Sprintf("Runtime error: %v", x.ExitCode)
	if x.ErrMsg != "" {
		ret += fmt.Sprintf(" (\"%v\")", x.ErrMsg)
	}
	return ret
}

func RuntimeError_Make(exitCode ExitCode, errMsg string) *RuntimeError {
	exitCode = EnsureErrorCode(exitCode)
	return &RuntimeError{
		ExitCode: exitCode,
		ErrMsg:   errMsg,
	}
}

func ActorSubstateCID_Equals(x, y ActorSubstateCID) bool {
	IMPL_FINISH()
	panic("")
}

type ActorStateHandle_I struct {
	_initValue *ActorSubstateCID
	_rt        *VMContext
}

func (h *ActorStateHandle_I) UpdateRelease(newStateCID ActorSubstateCID) {
	h._rt._updateReleaseActorSubstate(newStateCID)
}

func (h *ActorStateHandle_I) Release(checkStateCID ActorSubstateCID) {
	h._rt._releaseActorSubstate(checkStateCID)
}

func (h *ActorStateHandle_I) Take() ActorSubstateCID {
	if h._initValue == nil {
		h._rt._apiError("Must call Take() only once on actor substate object")
	}
	ret := *h._initValue
	h._initValue = nil
	return ret
}

// Concrete instantiation of the Runtime interface. This should be instantiated by the
// interpreter once per actor method invocation, and responds to that method's Runtime
// API calls.
type VMContext struct {
	_store              ipld.GraphStore
	_globalStateInit    st.StateTree
	_globalStatePending st.StateTree
	_running            bool
	_chain              chain.Chain
	_actorAddress       addr.Address
	_actorStateAcquired bool
	// Tracks whether actor substate has changed in order to charge gas just once
	// regardless of how many times it's written.
	_actorSubstateUpdated bool

	_immediateCaller addr.Address
	// Note: This is the actor in the From field of the initial on-chain message.
	// Not necessarily the immediate caller.
	_toplevelSender      addr.Address
	_toplevelBlockWinner addr.Address
	// call sequence number of the top level message that began this execution sequence
	_toplevelMsgCallSeqNum actstate.CallSeqNum
	// Sequence number representing the total number of calls (to any actor, any method)
	// during the current top-level message execution.
	// Note: resets with every top-level message, and therefore not necessarily monotonic.
	_internalCallSeqNum actstate.CallSeqNum
	_valueReceived      abi.TokenAmount
	_gasRemaining       msg.GasAmount
	_numValidateCalls   int
	_output             vmr.InvocOutput
}

func VMContext_Make(
	store ipld.GraphStore,
	chain chain.Chain,
	toplevelSender addr.Address,
	toplevelBlockWinner addr.Address,
	toplevelMsgCallSeqNum actstate.CallSeqNum,
	internalCallSeqNum actstate.CallSeqNum,
	globalState st.StateTree,
	actorAddress addr.Address,
	valueReceived abi.TokenAmount,
	gasRemaining msg.GasAmount) *VMContext {

	return &VMContext{
		_store:                store,
		_chain:                chain,
		_globalStateInit:      globalState,
		_globalStatePending:   globalState,
		_running:              false,
		_actorAddress:         actorAddress,
		_actorStateAcquired:   false,
		_actorSubstateUpdated: false,

		_toplevelSender:        toplevelSender,
		_toplevelBlockWinner:   toplevelBlockWinner,
		_toplevelMsgCallSeqNum: toplevelMsgCallSeqNum,
		_internalCallSeqNum:    internalCallSeqNum,
		_valueReceived:         valueReceived,
		_gasRemaining:          gasRemaining,
		_numValidateCalls:      0,
		_output:                vmr.InvocOutput{},
	}
}

func (rt *VMContext) AbortArgMsg(msg string) {
	rt.Abort(exitcode.InvalidArguments_User, msg)
}

func (rt *VMContext) AbortArg() {
	rt.AbortArgMsg("Invalid arguments")
}

func (rt *VMContext) AbortStateMsg(msg string) {
	rt.Abort(exitcode.InconsistentState_User, msg)
}

func (rt *VMContext) AbortState() {
	rt.AbortStateMsg("Inconsistent state")
}

func (rt *VMContext) AbortFundsMsg(msg string) {
	rt.Abort(exitcode.InsufficientFunds_User, msg)
}

func (rt *VMContext) AbortFunds() {
	rt.AbortFundsMsg("Insufficient funds")
}

func (rt *VMContext) AbortAPI(msg string) {
	rt.Abort(exitcode.RuntimeAPIError, msg)
}

func (rt *VMContext) CreateActor(codeID abi.ActorCodeID, address addr.Address) {
	if rt._actorAddress != builtin.InitActorAddr {
		rt.AbortAPI("Only InitActor may call rt.CreateActor")
	}
	if address.Protocol() != addr.ID {
		rt.AbortAPI("New actor adddress must be an ID-address")
	}

	rt._createActor(codeID, address)
}

func (rt *VMContext) _createActor(codeID abi.ActorCodeID, address addr.Address) {
	// Create empty actor state.
	actorState := &actstate.ActorState_I{
		CodeID_:     codeID,
		State_:      actor.ActorSubstateCID(EmptyCBOR),
		Balance_:    abi.TokenAmount(0),
		CallSeqNum_: 0,
	}

	// Put it in the state tree.
	actorStateCID := actstate.ActorSystemStateCID(rt.IpldPut(actorState))
	rt._updateActorSystemStateInternal(address, actorStateCID)

	rt._rtAllocGas(gascost.ExecNewActor)
}

func (rt *VMContext) DeleteActor(address addr.Address) {
	// Only a given actor may delete itself.
	if rt._actorAddress != address {
		rt.AbortAPI("Invalid actor deletion request")
	}

	rt._deleteActor(address)
}

func (rt *VMContext) _deleteActor(address addr.Address) {
	rt._globalStatePending = rt._globalStatePending.Impl().WithDeleteActorSystemState(address)
	rt._rtAllocGas(gascost.DeleteActor)
}

func (rt *VMContext) _updateActorSystemStateInternal(actorAddress addr.Address, newStateCID actstate.ActorSystemStateCID) {
	newGlobalStatePending, err := rt._globalStatePending.Impl().WithActorSystemState(rt._actorAddress, newStateCID)
	if err != nil {
		panic("Error in runtime implementation: failed to update actor system state")
	}
	rt._globalStatePending = newGlobalStatePending
}

func (rt *VMContext) _updateActorSubstateInternal(actorAddress addr.Address, newStateCID actor.ActorSubstateCID) {
	newGlobalStatePending, err := rt._globalStatePending.Impl().WithActorSubstate(rt._actorAddress, newStateCID)
	if err != nil {
		panic("Error in runtime implementation: failed to update actor substate")
	}
	rt._globalStatePending = newGlobalStatePending
}

func (rt *VMContext) _updateReleaseActorSubstate(newStateCID ActorSubstateCID) {
	rt._checkRunning()
	rt._checkActorStateAcquired()
	rt._updateActorSubstateInternal(rt._actorAddress, newStateCID)
	rt._actorSubstateUpdated = true
	rt._actorStateAcquired = false
}

func (rt *VMContext) _releaseActorSubstate(checkStateCID ActorSubstateCID) {
	rt._checkRunning()
	rt._checkActorStateAcquired()

	prevState, ok := rt._globalStatePending.GetActor(rt._actorAddress)
	util.Assert(ok)
	prevStateCID := prevState.State()
	if !ActorSubstateCID_Equals(prevStateCID, checkStateCID) {
		rt.AbortAPI("State CID differs upon release call")
	}

	rt._actorStateAcquired = false
}

func (rt *VMContext) Assert(cond bool) {
	if !cond {
		rt.Abort(exitcode.RuntimeAssertFailure, "Runtime assertion failed")
	}
}

func (rt *VMContext) _checkActorStateAcquiredFlag(expected bool) {
	rt._checkRunning()
	if rt._actorStateAcquired != expected {
		rt._apiError("State updates and message sends must be disjoint")
	}
}

func (rt *VMContext) _checkActorStateAcquired() {
	rt._checkActorStateAcquiredFlag(true)
}

func (rt *VMContext) _checkActorStateNotAcquired() {
	rt._checkActorStateAcquiredFlag(false)
}

func (rt *VMContext) Abort(errExitCode exitcode.ExitCode, errMsg string) {
	errExitCode = exitcode.EnsureErrorCode(errExitCode)
	rt._throwErrorFull(errExitCode, errMsg)
}

func (rt *VMContext) ImmediateCaller() addr.Address {
	return rt._immediateCaller
}

func (rt *VMContext) CurrReceiver() addr.Address {
	return rt._actorAddress
}

func (rt *VMContext) ToplevelBlockWinner() addr.Address {
	return rt._toplevelBlockWinner
}

func (rt *VMContext) ValidateImmediateCallerMatches(
	callerExpectedPattern CallerPattern) {

	rt._checkRunning()
	rt._checkNumValidateCalls(0)
	caller := rt.ImmediateCaller()
	if !callerExpectedPattern.Matches(caller) {
		rt.AbortAPI("Method invoked by incorrect caller")
	}
	rt._numValidateCalls += 1
}

func CallerPattern_MakeAcceptAnyOfTypes(rt *VMContext, types []abi.ActorCodeID) CallerPattern {
	return CallerPattern{
		Matches: func(y addr.Address) bool {
			codeID, ok := rt.GetActorCodeID(y)
			if !ok {
				panic("Internal runtime error: actor not found")
			}

			for _, type_ := range types {
				if codeID == type_ {
					return true
				}
			}
			return false
		},
	}
}

func (rt *VMContext) ValidateImmediateCallerIs(callerExpected addr.Address) {
	rt.ValidateImmediateCallerMatches(vmr.CallerPattern_MakeSingleton(callerExpected))
}

func (rt *VMContext) ValidateImmediateCallerInSet(callersExpected []addr.Address) {
	rt.ValidateImmediateCallerMatches(vmr.CallerPattern_MakeSet(callersExpected))
}

func (rt *VMContext) ValidateImmediateCallerAcceptAnyOfType(type_ abi.ActorCodeID) {
	rt.ValidateImmediateCallerAcceptAnyOfTypes([]abi.ActorCodeID{type_})
}

func (rt *VMContext) ValidateImmediateCallerAcceptAnyOfTypes(types []abi.ActorCodeID) {
	rt.ValidateImmediateCallerMatches(CallerPattern_MakeAcceptAnyOfTypes(rt, types))
}

func (rt *VMContext) ValidateImmediateCallerAcceptAny() {
	rt.ValidateImmediateCallerMatches(vmr.CallerPattern_MakeAcceptAny())
}

func (rt *VMContext) _checkNumValidateCalls(x int) {
	if rt._numValidateCalls != x {
		rt.AbortAPI("Method must validate caller identity exactly once")
	}
}

func (rt *VMContext) _checkRunning() {
	if !rt._running {
		panic("Internal runtime error: actor API called with no actor code running")
	}
}
func (rt *VMContext) SuccessReturn() InvocOutput {
	return vmr.InvocOutput_Make(nil)
}

func (rt *VMContext) ValueReturn(value util.Bytes) InvocOutput {
	return vmr.InvocOutput_Make(value)
}

func (rt *VMContext) _throwError(exitCode ExitCode) {
	rt._throwErrorFull(exitCode, "")
}

func (rt *VMContext) _throwErrorFull(exitCode ExitCode, errMsg string) {
	panic(RuntimeError_Make(exitCode, errMsg))
}

func (rt *VMContext) _apiError(errMsg string) {
	rt._throwErrorFull(exitcode.RuntimeAPIError, errMsg)
}

func _gasAmountAssertValid(x msg.GasAmount) {
	if x.LessThan(msg.GasAmount_Zero()) {
		panic("Interpreter error: negative gas amount")
	}
}

// Deduct an amount of gas corresponding to cost about to be incurred, but not necessarily
// incurred yet.
func (rt *VMContext) _rtAllocGas(x msg.GasAmount) {
	_gasAmountAssertValid(x)
	var ok bool
	rt._gasRemaining, ok = rt._gasRemaining.SubtractIfNonnegative(x)
	if !ok {
		rt._throwError(exitcode.OutOfGas)
	}
}

func (rt *VMContext) _transferFunds(from addr.Address, to addr.Address, amount abi.TokenAmount) error {
	rt._checkRunning()
	rt._checkActorStateNotAcquired()

	newGlobalStatePending, err := rt._globalStatePending.Impl().WithFundsTransfer(from, to, amount)
	if err != nil {
		return err
	}

	rt._globalStatePending = newGlobalStatePending
	return nil
}

func (rt *VMContext) GetActorCodeID(actorAddr addr.Address) (ret abi.ActorCodeID, ok bool) {
	IMPL_FINISH()
	panic("")
}

type ErrorHandlingSpec int

const (
	PropagateErrors ErrorHandlingSpec = 1 + iota
	CatchErrors
)

// TODO: This function should be private (not intended to be exposed to actors).
// (merging runtime and interpreter packages should solve this)
// TODO: this should not use the MessageReceipt return type, even though it needs the same triple
// of values. This method cannot compute the total gas cost and the returned receipt will never
// go on chain.
func (rt *VMContext) SendToplevelFromInterpreter(input InvocInput) (MessageReceipt, st.StateTree) {

	rt._running = true
	ret := rt._sendInternal(input, CatchErrors)
	rt._running = false
	return ret, rt._globalStatePending
}

func _catchRuntimeErrors(f func() InvocOutput) (output InvocOutput, exitCode exitcode.ExitCode) {
	defer func() {
		if r := recover(); r != nil {
			switch r.(type) {
			case *RuntimeError:
				output = vmr.InvocOutput_Make(nil)
				exitCode = (r.(*RuntimeError).ExitCode)
			default:
				panic(r)
			}
		}
	}()

	output = f()
	exitCode = exitcode.OK()
	return
}

func _invokeMethodInternal(
	rt *VMContext,
	actorCode vmr.ActorCode,
	method abi.MethodNum,
	params abi.MethodParams) (
	ret InvocOutput, exitCode exitcode.ExitCode, internalCallSeqNumFinal actstate.CallSeqNum) {

	if method == builtin.MethodSend {
		ret = vmr.InvocOutput_Make(nil)
		return
	}

	rt._running = true
	ret, exitCode = _catchRuntimeErrors(func() InvocOutput {
		IMPL_TODO("dispatch to actor code")
		var methodOutput vmr.InvocOutput // actorCode.InvokeMethod(rt, method, params)
		if rt._actorSubstateUpdated {
			rt._rtAllocGas(gascost.UpdateActorSubstate)
		}
		rt._checkActorStateNotAcquired()
		rt._checkNumValidateCalls(1)
		return methodOutput
	})
	rt._running = false

	internalCallSeqNumFinal = rt._internalCallSeqNum

	return
}

func (rtOuter *VMContext) _sendInternal(input InvocInput, errSpec ErrorHandlingSpec) MessageReceipt {
	rtOuter._checkRunning()
	rtOuter._checkActorStateNotAcquired()

	initGasRemaining := rtOuter._gasRemaining

	rtOuter._rtAllocGas(gascost.InvokeMethod(input.Value, input.Method))

	receiver, receiverAddr := rtOuter._resolveReceiver(input.To)
	receiverCode, err := loadActorCode(receiver.CodeID())
	if err != nil {
		rtOuter._throwError(exitcode.ActorCodeNotFound)
	}

	err = rtOuter._transferFunds(rtOuter._actorAddress, receiverAddr, input.Value)
	if err != nil {
		rtOuter._throwError(exitcode.InsufficientFunds_System)
	}

	rtInner := VMContext_Make(
		rtOuter._store,
		rtOuter._chain,
		rtOuter._toplevelSender,
		rtOuter._toplevelBlockWinner,
		rtOuter._toplevelMsgCallSeqNum,
		rtOuter._internalCallSeqNum+1,
		rtOuter._globalStatePending,
		receiverAddr,
		input.Value,
		rtOuter._gasRemaining,
	)

	invocOutput, exitCode, internalCallSeqNumFinal := _invokeMethodInternal(
		rtInner,
		receiverCode,
		input.Method,
		input.Params,
	)

	_gasAmountAssertValid(rtOuter._gasRemaining.Subtract(rtInner._gasRemaining))
	rtOuter._gasRemaining = rtInner._gasRemaining
	gasUsed := initGasRemaining.Subtract(rtOuter._gasRemaining)
	_gasAmountAssertValid(gasUsed)

	rtOuter._internalCallSeqNum = internalCallSeqNumFinal

	if exitCode == exitcode.OutOfGas {
		// OutOfGas error cannot be caught
		rtOuter._throwError(exitCode)
	}

	if errSpec == PropagateErrors && exitCode.IsError() {
		rtOuter._throwError(exitcode.MethodSubcallError)
	}

	if exitCode.AllowsStateUpdate() {
		rtOuter._globalStatePending = rtInner._globalStatePending
	}

	return MessageReceipt_Make(invocOutput, exitCode, gasUsed)
}

// Loads a receiving actor state from the state tree, resolving non-ID addresses through the InitActor state.
// If it doesn't exist, and the message is a simple value send to a pubkey-style address,
// creates the receiver as an account actor in the returned state.
// Aborts otherwise.
func (rt *VMContext) _resolveReceiver(targetRaw addr.Address) (actstate.ActorState, addr.Address) {
	// Resolve the target address via the InitActor, and attempt to load state.
	initSubState := rt._loadInitActorState()
	targetIdAddr := initSubState.ResolveAddress(targetRaw)
	act, found := rt._globalStatePending.GetActor(targetIdAddr)
	if found {
		return act, targetIdAddr
	}

	if targetRaw.Protocol() != addr.SECP256K1 && targetRaw.Protocol() != addr.BLS {
		// Don't implicitly create an account actor for an address without an associated key.
		rt._throwError(exitcode.ActorNotFound)
	}

	// Allocate an ID address from the init actor and map the pubkey To address to it.
	newIdAddr := initSubState.MapAddressToNewID(targetRaw)
	rt._saveInitActorState(initSubState)

	// Create new account actor (charges gas).
	rt._createActor(builtin.AccountActorCodeID, newIdAddr)

	// Initialize account actor substate with it's pubkey address.
	substate := &acctact.AccountActorState{
		Address: targetRaw,
	}
	rt._saveAccountActorState(newIdAddr, *substate)
	act, _ = rt._globalStatePending.GetActor(newIdAddr)
	return act, newIdAddr
}

func (rt *VMContext) _loadInitActorState() initact.InitActorState {
	initState, ok := rt._globalStatePending.GetActor(builtin.InitActorAddr)
	util.Assert(ok)
	var initSubState initact.InitActorState
	ok = rt.IpldGet(cid.Cid(initState.State()), &initSubState)
	util.Assert(ok)
	return initSubState
}

func (rt *VMContext) _saveInitActorState(state initact.InitActorState) {
	// Gas is charged here separately from _actorSubstateUpdated because this is a different actor
	// than the receiver.
	rt._rtAllocGas(gascost.UpdateActorSubstate)
	rt._updateActorSubstateInternal(builtin.InitActorAddr, actor.ActorSubstateCID(rt.IpldPut(&state)))
}

func (rt *VMContext) _saveAccountActorState(address addr.Address, state acctact.AccountActorState) {
	// Gas is charged here separately from _actorSubstateUpdated because this is a different actor
	// than the receiver.
	rt._rtAllocGas(gascost.UpdateActorSubstate)
	rt._updateActorSubstateInternal(address, actor.ActorSubstateCID(rt.IpldPut(state)))
}

func (rt *VMContext) _sendInternalOutputs(input InvocInput, errSpec ErrorHandlingSpec) (InvocOutput, exitcode.ExitCode) {
	ret := rt._sendInternal(input, errSpec)
	return vmr.InvocOutput_Make(ret.ReturnValue), ret.ExitCode
}

func (rt *VMContext) Send(
	toAddr addr.Address, methodNum abi.MethodNum, params abi.MethodParams, value abi.TokenAmount) InvocOutput {

	return rt.SendPropagatingErrors(vmr.InvocInput_Make(toAddr, methodNum, params, value))
}

func (rt *VMContext) SendQuery(toAddr addr.Address, methodNum abi.MethodNum, params abi.MethodParams) util.Serialization {
	invocOutput := rt.Send(toAddr, methodNum, params, abi.TokenAmount(0))
	ret := invocOutput.ReturnValue
	Assert(ret != nil)
	return ret
}

func (rt *VMContext) SendFunds(toAddr addr.Address, value abi.TokenAmount) {
	rt.Send(toAddr, builtin.MethodSend, nil, value)
}

func (rt *VMContext) SendPropagatingErrors(input InvocInput) InvocOutput {
	ret, _ := rt._sendInternalOutputs(input, PropagateErrors)
	return ret
}

func (rt *VMContext) SendCatchingErrors(input InvocInput) (InvocOutput, exitcode.ExitCode) {
	rt.ValidateImmediateCallerIs(builtin.CronActorAddr)
	return rt._sendInternalOutputs(input, CatchErrors)
}

func (rt *VMContext) CurrentBalance() abi.TokenAmount {
	IMPL_FINISH()
	panic("")
}

func (rt *VMContext) ValueReceived() abi.TokenAmount {
	return rt._valueReceived
}

func (rt *VMContext) GetRandomness(epoch abi.ChainEpoch) abi.RandomnessSeed {
	return rt._chain.RandomnessSeedAtEpoch(epoch)
}

func (rt *VMContext) NewActorAddress() addr.Address {
	addrBuf := new(bytes.Buffer)

	senderState, ok := rt._globalStatePending.GetActor(rt._toplevelSender)
	util.Assert(ok)
	var aast acctact.AccountActorState
	ok = rt.IpldGet(cid.Cid(senderState.State()), &aast)
	util.Assert(ok)
	err := aast.Address.MarshalCBOR(addrBuf)
	util.Assert(err == nil)
	err = binary.Write(addrBuf, binary.BigEndian, rt._toplevelMsgCallSeqNum)
	util.Assert(err != nil)
	err = binary.Write(addrBuf, binary.BigEndian, rt._internalCallSeqNum)
	util.Assert(err != nil)

	newAddr, err := addr.NewActorAddress(addrBuf.Bytes())
	util.Assert(err == nil)
	return newAddr
}

func (rt *VMContext) IpldPut(x ipld.Object) cid.Cid {
	IMPL_FINISH() // Serialization
	serialized := []byte{}
	cid := rt._store.Put(serialized)
	rt._rtAllocGas(gascost.IpldPut(len(serialized)))
	return cid
}

func (rt *VMContext) IpldGet(c cid.Cid, o ipld.Object) bool {
	serialized, ok := rt._store.Get(c)
	if ok {
		rt._rtAllocGas(gascost.IpldGet(len(serialized)))
	}
	IMPL_FINISH() // Deserialization into o
	return ok
}

func (rt *VMContext) CurrEpoch() abi.ChainEpoch {
	IMPL_FINISH()
	panic("")
}

func (rt *VMContext) CurrIndices() indices.Indices {
	// TODO: compute from state tree (rt._globalStatePending), using individual actor
	// state helper functions when possible
	TODO()
	panic("")
}

func (rt *VMContext) AcquireState() ActorStateHandle {
	rt._checkRunning()
	rt._checkActorStateNotAcquired()
	rt._actorStateAcquired = true

	state, ok := rt._globalStatePending.GetActor(rt._actorAddress)
	util.Assert(ok)

	stateRef := state.State().Ref()
	return &ActorStateHandle_I{
		_initValue: &stateRef,
		_rt:        rt,
	}
}

func (rt *VMContext) Compute(f ComputeFunctionID, args []util.Any) Any {
	def, found := _computeFunctionDefs[f]
	if !found {
		rt.AbortAPI("Function definition in rt.Compute() not found")
	}
	gasCost := def.GasCostFn(args)
	rt._rtAllocGas(gasCost)
	return def.Body(args)
}

Code Loading #

package impl

import (
	abi "github.com/filecoin-project/specs-actors/actors/abi"
	vmr "github.com/filecoin-project/specs-actors/actors/runtime"
)

func loadActorCode(codeID abi.ActorCodeID) (vmr.ActorCode, error) {

	panic("TODO")
	// TODO: resolve circular dependency

	// // load the code from StateTree.
	// // TODO: this is going to be enabled in the future.
	// // code, err := loadCodeFromStateTree(input.InTree, codeCID)
	// return staticActorCodeRegistry.LoadActor(codeCID)
}

Exit codes #

package exitcode

// Common error codes that may be shared by different actors.
// Actors may also define their own codes, including redefining these values.

const (
	// Indicates a method parameter is invalid.
	ErrIllegalArgument = FirstActorErrorCode + iota
	// Indicates a requested resource does not exist.
	ErrNotFound
	// Indicates an action is disallowed.
	ErrForbidden
	// Indicates a balance of funds is insufficient.
	ErrInsufficientFunds
	// Indicates an actor's internal state is invalid.
	ErrIllegalState
	// Indicates de/serialization failure within actor code.
	ErrSerialization

	// Common error codes stop here.  If you define a common error code above
	// this value it will have conflicting interpretations
	FirstActorSpecificExitCode = ExitCode(32)

	// An error code intended to be replaced by different code structure or a more descriptive error.
	ErrPlaceholder = ExitCode(1000)
)