Filecoin Spec 翻译 ——【2.3】 VM - 虚拟机(上)

1,762 阅读16分钟

原文链接:filecoin-project.github.io/specs/#syst…

本文由星际联盟Boni Liu翻译,转载请注明出处。

星际联盟提供对Filecoin Spec 的全文翻译,便于Filecoin项目广大中国参与者理解Filecoin的深层原理。文章将定期更新章节,请持续关注"IPFS星际联盟"&"星际联盟Filecoin"公众号。

2.3 VM - 虚拟机

import msg "github.com/filecoin-project/specs/systems/filecoin_vm/message"
import st "github.com/filecoin-project/specs/systems/filecoin_vm/state_tree"

// VM is the object that controls execution.
// It is a stateless, pure function. It uses no local storage.
//
// TODO: make it just a function: VMExec(...) ?
type VM struct {
    // Execute computes and returns outTree, a new StateTree which is the
    // application of msgs to inTree.
    //
    // *Important:* Execute is intended to be a pure function, with no side-effects.
    // however, storage of the new parts of the computed outTree may exist in
    // local storage.
    //
    // *TODO:* define whether this should take 0, 1, or 2 IpldStores:
    // - (): storage of IPLD datastructures is assumed implicit
    // - (store): get and put to same IpldStore
    // - (inStore, outStore): get from inStore, put new structures into outStore
    //
    // This decision impacts callers, and potentially impacts how we reason about
    // local storage, and intermediate storage. It is definitely the case that
    // implementations may want to operate on this differently, depending on
    // how their IpldStores work.
    Execute(inTree st.StateTree, msgs [msg.UnsignedMessage]) union {outTree st.StateTree, err error}
}

2.3.1 VM Actor 接口

// This contains actor things that are _outside_ of VM exection.
// The VM uses this to execute abi.

import abi "github.com/filecoin-project/specs-actors/actors/abi"
import actor "github.com/filecoin-project/specs-actors/actors"
import cid "github.com/ipfs/go-cid"

// CallSeqNum is an invocation (Call) sequence (Seq) number (Num).
// This is a value used for securing against replay attacks:
// each AccountActor (user) invocation must have a unique CallSeqNum
// value. The sequenctiality of the numbers is used to make it
// easy to verify, and to order messages.
//
// Q&A
// - > Does it have to be sequential?
//   No, a random nonce could work against replay attacks, but
//   making it sequential makes it much easier to verify.
// - > Can it be used to order events?
//   Yes, a user may submit N separate messages with increasing
//   sequence number, causing them to execute in order.
//
type CallSeqNum int64

// Actor is a base computation object in the Filecoin VM. Similar
// to Actors in the Actor Model (programming), or Objects in Object-
// Oriented Programming, or Ethereum Contracts in the EVM.
//
// ActorState represents the on-chain storage all actors keep.
type ActorState struct {
    // Identifies the code this actor executes.
    CodeID      abi.ActorCodeID
    // CID of the root of optional actor-specific sub-state.
    State       actor.ActorSubstateCID
    // Balance of tokens held by this actor.
    Balance     abi.TokenAmount
    // Expected sequence number of the next message sent by this actor.
    // Initially zero, incremented when an account actor originates a top-level message.
    // Always zero for other abi.
    CallSeqNum
}

type ActorSystemStateCID cid.Cid

// ActorState represents the on-chain storage actors keep. This type is a
// union of concrete types, for each of the Actors:
// - InitActor
// - CronActor
// - AccountActor
// - PaymentChannelActor
// - StoragePowerActor
// - StorageMinerActor
// - StroageMarketActor
//
// TODO: move this into a directory inside the VM that patches in all
// the actors from across the system. this will be where we declare/mount
// all actors in the VM.
// type ActorState union {
//     Init struct {
//         AddressMap  {addr.Address: ActorID}
//         NextID      ActorID
//     }
// }
package actor

import (
	util "github.com/filecoin-project/specs/util"
	cid "github.com/ipfs/go-cid"
)

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

type Serialization = util.Serialization

func (st *ActorState_I) CID() cid.Cid {
	panic("TODO")
}

2.3.2 状态树

状态树是在Filecoin区块链上应用操作产生的输出。

import abi "github.com/filecoin-project/specs-actors/actors/abi"
import addr "github.com/filecoin-project/go-address"
import actor "github.com/filecoin-project/specs/systems/filecoin_vm/actor"
import cid "github.com/ipfs/go-cid"

// The on-chain state data structure is a map (HAMT) of addresses to actor states.
// Only ID addresses are expected as keys.
type StateTree struct {
    ActorStates  {addr.Address: actor.ActorState}  // HAMT

    // Returns the CID of the root node of the HAMT.
    RootCID()    cid.Cid

    // Looks up an actor state by address.
    GetActor(a addr.Address) (state actor.ActorState, ok bool)

    // Looks up an abi.ActorCodeID by address.
    GetActorCodeID_Assert(a addr.Address) abi.ActorCodeID
}

TODO

  • 添加ConvenienceAPI状态,以提供更加用户友好的视图。

2.3.3 宏观经济指标

Indices是通过状态树(State Tree) 计算得到的一组全球经济指标,以及一些基于用户状态/动作来计算策略输出的纯函数Indices用于计算和实现系统的经济机制和政策。Indices中不存在持久性状态,也不能引入任何状态突变。 请注意,指标应该存放在哪里是一个设计上的决策。 一旦确定了所有经济机制,就有可能将Indices分解为多个文件或将其放置到不同的Actor中。 暂时来说,Indices是所有潜在宏观经济指标的临时保留文件,这些指标需要被系统留意。

package indices

import (
	"math/big"

	abi "github.com/filecoin-project/specs-actors/actors/abi"
	actor_util "github.com/filecoin-project/specs-actors/actors/util"
)

var PARAM_FINISH = actor_util.PARAM_FINISH

// Data in Indices are populated at instantiation with data from the state tree
// Indices itself has no state tree or access to the runtime
// it is a passive data structure that allows for convenience access to network indices
// and pure functions in implementing economic policies given states
type Indices interface {
	Epoch() abi.ChainEpoch
	NetworkKPI() big.Int
	TotalNetworkSectorWeight() abi.SectorWeight
	TotalPledgeCollateral() abi.TokenAmount
	TotalNetworkEffectivePower() abi.StoragePower // power above minimum miner size
	TotalNetworkPower() abi.StoragePower          // total network power irrespective of meeting minimum miner size

	TotalMinedFIL() abi.TokenAmount
	TotalUnminedFIL() abi.TokenAmount
	TotalBurnedFIL() abi.TokenAmount
	LastEpochReward() abi.TokenAmount

	StorageDeal_DurationBounds(
		pieceSize abi.PieceSize,
		startEpoch abi.ChainEpoch,
	) (minDuration abi.ChainEpoch, maxDuration abi.ChainEpoch)
	StorageDeal_StoragePricePerEpochBounds(
		pieceSize abi.PieceSize,
		startEpoch abi.ChainEpoch,
		endEpoch abi.ChainEpoch,
	) (minPrice abi.TokenAmount, maxPrice abi.TokenAmount)
	StorageDeal_ProviderCollateralBounds(
		pieceSize abi.PieceSize,
		startEpoch abi.ChainEpoch,
		endEpoch abi.ChainEpoch,
	) (minProviderCollateral abi.TokenAmount, maxProviderCollateral abi.TokenAmount)
	StorageDeal_ClientCollateralBounds(
		pieceSize abi.PieceSize,
		startEpoch abi.ChainEpoch,
		endEpoch abi.ChainEpoch,
	) (minClientCollateral abi.TokenAmount, maxClientCollateral abi.TokenAmount)
	SectorWeight(
		sectorSize abi.SectorSize,
		startEpoch abi.ChainEpoch,
		endEpoch abi.ChainEpoch,
		dealWeight abi.DealWeight,
	) abi.SectorWeight
	PledgeCollateralReq(minerNominalPower abi.StoragePower) abi.TokenAmount
	SectorWeightProportion(minerActiveSectorWeight abi.SectorWeight) big.Int
	PledgeCollateralProportion(minerPledgeCollateral abi.TokenAmount) big.Int
	StoragePower(
		minerActiveSectorWeight abi.SectorWeight,
		minerInactiveSectorWeight abi.SectorWeight,
		minerPledgeCollateral abi.TokenAmount,
	) abi.StoragePower
	StoragePowerProportion(
		minerStoragePower abi.StoragePower,
	) big.Int
	CurrEpochBlockReward() abi.TokenAmount
	GetCurrBlockRewardRewardForMiner(
		minerStoragePower abi.StoragePower,
		minerPledgeCollateral abi.TokenAmount,
		// TODO extend or eliminate
	) abi.TokenAmount
	StoragePower_PledgeSlashForSectorTermination(
		storageWeightDesc actor_util.SectorStorageWeightDesc,
		terminationType actor_util.SectorTermination,
	) abi.TokenAmount
	StoragePower_PledgeSlashForSurprisePoStFailure(
		minerClaimedPower abi.StoragePower,
		numConsecutiveFailures int64,
	) abi.TokenAmount
	StorageMining_PreCommitDeposit(
		sectorSize abi.SectorSize,
		expirationEpoch abi.ChainEpoch,
	) abi.TokenAmount
	StorageMining_TemporaryFaultFee(
		storageWeightDescs []actor_util.SectorStorageWeightDesc,
		duration abi.ChainEpoch,
	) abi.TokenAmount
	NetworkTransactionFee(
		toActorCodeID abi.ActorCodeID,
		methodNum abi.MethodNum,
	) abi.TokenAmount
	GetCurrBlockRewardForMiner(
		minerStoragePower abi.StoragePower,
		minerPledgeCollateral abi.TokenAmount,
	) abi.TokenAmount
}

type IndicesImpl struct {
	// these fields are computed from StateTree upon construction
	// they are treated as globally available states
	Epoch                      abi.ChainEpoch
	NetworkKPI                 big.Int
	TotalNetworkSectorWeight   abi.SectorWeight
	TotalPledgeCollateral      abi.TokenAmount
	TotalNetworkEffectivePower abi.StoragePower // power above minimum miner size
	TotalNetworkPower          abi.StoragePower // total network power irrespective of meeting minimum miner size

	TotalMinedFIL   abi.TokenAmount
	TotalUnminedFIL abi.TokenAmount
	TotalBurnedFIL  abi.TokenAmount
	LastEpochReward abi.TokenAmount
}

func (inds *IndicesImpl) StorageDeal_DurationBounds(
	pieceSize abi.PieceSize,
	startEpoch abi.ChainEpoch,
) (minDuration abi.ChainEpoch, maxDuration abi.ChainEpoch) {

	// placeholder
	PARAM_FINISH()
	minDuration = abi.ChainEpoch(0)
	maxDuration = abi.ChainEpoch(1 << 20)
	return
}

func (inds *IndicesImpl) StorageDeal_StoragePricePerEpochBounds(
	pieceSize abi.PieceSize,
	startEpoch abi.ChainEpoch,
	endEpoch abi.ChainEpoch,
) (minPrice abi.TokenAmount, maxPrice abi.TokenAmount) {

	// placeholder
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) StorageDeal_ProviderCollateralBounds(
	pieceSize abi.PieceSize,
	startEpoch abi.ChainEpoch,
	endEpoch abi.ChainEpoch,
) (minProviderCollateral abi.TokenAmount, maxProviderCollateral abi.TokenAmount) {

	// placeholder
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) StorageDeal_ClientCollateralBounds(
	pieceSize abi.PieceSize,
	startEpoch abi.ChainEpoch,
	endEpoch abi.ChainEpoch,
) (minClientCollateral abi.TokenAmount, maxClientCollateral abi.TokenAmount) {

	// placeholder
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) SectorWeight(
	sectorSize abi.SectorSize,
	startEpoch abi.ChainEpoch,
	endEpoch abi.ChainEpoch,
	dealWeight abi.DealWeight,
) abi.SectorWeight {
	// for every sector, given its size, start, end, and deals within the sector
	// assign sector power for the duration of its lifetime
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) PledgeCollateralReq(minerNominalPower abi.StoragePower) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) SectorWeightProportion(minerActiveSectorWeight abi.SectorWeight) big.Int {
	// return proportion of SectorWeight for miner
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) PledgeCollateralProportion(minerPledgeCollateral abi.TokenAmount) big.Int {
	// return proportion of Pledge Collateral for miner
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) StoragePower(
	minerActiveSectorWeight abi.SectorWeight,
	minerInactiveSectorWeight abi.SectorWeight,
	minerPledgeCollateral abi.TokenAmount,
) abi.StoragePower {
	// return StoragePower based on inputs
	// StoragePower for miner = func(ActiveSectorWeight for miner, PledgeCollateral for miner, global indices)
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) StoragePowerProportion(
	minerStoragePower abi.StoragePower,
) big.Int {
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) CurrEpochBlockReward() abi.TokenAmount {
	// total block reward allocated for CurrEpoch
	// each expected winner get an equal share of this reward
	// computed as a function of NetworkKPI, LastEpochReward, TotalUnmminedFIL, etc
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) GetCurrBlockRewardRewardForMiner(
	minerStoragePower abi.StoragePower,
	minerPledgeCollateral abi.TokenAmount,
	// TODO extend or eliminate
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

// TerminationFault
func (inds *IndicesImpl) StoragePower_PledgeSlashForSectorTermination(
	storageWeightDesc actor_util.SectorStorageWeightDesc,
	terminationType actor_util.SectorTermination,
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

// DetectedFault
func (inds *IndicesImpl) StoragePower_PledgeSlashForSurprisePoStFailure(
	minerClaimedPower abi.StoragePower,
	numConsecutiveFailures int64,
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) StorageMining_PreCommitDeposit(
	sectorSize abi.SectorSize,
	expirationEpoch abi.ChainEpoch,
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) StorageMining_TemporaryFaultFee(
	storageWeightDescs []actor_util.SectorStorageWeightDesc,
	duration abi.ChainEpoch,
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) NetworkTransactionFee(
	toActorCodeID abi.ActorCodeID,
	methodNum abi.MethodNum,
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

func (inds *IndicesImpl) GetCurrBlockRewardForMiner(
	minerStoragePower abi.StoragePower,
	minerPledgeCollateral abi.TokenAmount,
) abi.TokenAmount {
	PARAM_FINISH()
	panic("")
}

func ConsensusPowerForStorageWeight(
	storageWeightDesc actor_util.SectorStorageWeightDesc,
) abi.StoragePower {
	PARAM_FINISH()
	panic("")
}

func StorageDeal_ProviderInitTimedOutSlashAmount(providerCollateral abi.TokenAmount) abi.TokenAmount {
	// placeholder
	PARAM_FINISH()
	return providerCollateral
}

func StoragePower_ConsensusMinMinerPower() abi.StoragePower {
	PARAM_FINISH()
	panic("")
}

func StorageMining_PoStNoChallengePeriod() abi.ChainEpoch {
	PARAM_FINISH()
	panic("")
}

func StorageMining_SurprisePoStProvingPeriod() abi.ChainEpoch {
	PARAM_FINISH()
	panic("")
}

func StoragePower_SurprisePoStMaxConsecutiveFailures() int64 {
	PARAM_FINISH()
	panic("")
}

func StorageMining_DeclaredFaultEffectiveDelay() abi.ChainEpoch {
	PARAM_FINISH()
	panic("")
}

2.3.4 虚拟机消息 - Actor方法调用

消息是两个Actor之间进行通信的单位,因此是状态变化的根本原因。一条消息包含:

  • 从发送方转移到接收方的代币数量
  • 在接收方被调用的,带有参数的方法(可选)

Actor代码使得Actor可以在处理收到的消息的同时,向其他Actor发送额外的消息。 消息会被同步执行:Actor在恢复控制之前会等待发送的消息执行完毕。

消息的处理的过程消耗以gas计价的计算单位和存储。消息的gas limit(gas限额) 为其计算量提供了上限。消息的发送者按照自身决定的gas price(单价) 支付其执行消息(包括所有嵌套的消息)所消耗的gas。 区块出产者选择将哪些消息打包到区块中,并根据每条消息的gas price(单价) 和消耗量获得奖励,从而形成市场。

2.3.4.1 消息语法验证

语法错误的消息一定不能传输,不能保留在消息池中,也不能包含在区块

一条语法正确的UnsignedMessage(未签名消息):

  • 有一个格式正确的非空的To(接收) 地址
  • 有一个格式正确的非空的From(发送) 地址
  • 有一个非负的CallSeqNum
  • 有不小于0且不大于代币总供应量(2e9 * 1e18) 的Value(值)
  • 有一个非负的MethodNum
  • 仅当MethodNum为0时才具有非空的Params
  • 有非负的GasPrice
  • GasLimit大于等于gas的消耗量,而gas的消耗量与消息序列化后的字节大小有关
  • GasLimit的值不大于网络中的区块的gas limit参数

当消息被单独传输时(包含在区块中之前),无论使用哪种签名方案,都会被打包为SignedMessage(签名消息) 。一条有效的签名消息:

  • 其序列化后的总大小不大于message.MessageMaxSize

2.3.4.2 消息语义验证

语义验证是指需要消息本身之外的信息的验证。

一条语义正确的SignedMessage必须带有一个签名,该签名可以验证From地址所标识的账户actor的公钥是否已对payload进行签名。

注意:

  • From地址是一个ID-address时,必须在区块所标识的父状态中,发送帐户actor的状态下查找公钥。
  • 发送actor必须存在于包含本消息的区块所标识的父state中。 这意味着,对于一个区块而言,既包含某条创建“新帐户actor”的消息,又包含同一actor发出的消息的行为是不合法的。该actor发出的第一条消息必须等待紧随其后的下一个epoch(时期)。 消息池可能会排除尚未出现在链状态下的actor的消息。

消息没有进一步的语义验证,可能导致包含该消息的区块无效。 每个语义有效且签名正确的消息都可以包含在一个区块中,并会从执行中产生一个receipt(回执)。 但是,消息可能无法执行到完成,这种情况下就不会对期望的状态改变带来影响。

使用这种“无消息语义验证”策略的原因是,在消息作为tipset的一部分执行之前,无法知道消息将被应用的状态。区块出产者不知道在tipset中是否有另一个区块会抢先执行此消息,从而修改该区块消息被应用到的,来自公开声明的父state的状态。

import filcrypto "github.com/filecoin-project/specs/algorithms/crypto"
import addr "github.com/filecoin-project/go-address"
import actor "github.com/filecoin-project/specs/systems/filecoin_vm/actor"
import abi "github.com/filecoin-project/specs-actors/actors/abi"

// GasAmount is a quantity of gas.
type GasAmount struct {
    value                BigInt

    Add(GasAmount)       GasAmount
    Subtract(GasAmount)  GasAmount
    SubtractIfNonnegative(GasAmount) (ret GasAmount, ok bool)
    LessThan(GasAmount) bool
    Equals(GasAmount) bool
    Scale(int) GasAmount
}

type UnsignedMessage struct {
    // Address of the receiving actor.
    To          addr.Address
    // Address of the sending actor.
    From        addr.Address
    // Expected CallSeqNum of the sending actor (only for top-level messages).
    CallSeqNum  actor.CallSeqNum

    // Amount of value to transfer from sender's to receiver's balance.
    Value       abi.TokenAmount

    // GasPrice is a Gas-to-FIL cost
    GasPrice    abi.TokenAmount
    GasLimit    GasAmount

    // Optional method to invoke on receiver, zero for a plain value send.
    Method      abi.MethodNum
    /// Serialized parameters to the method (if method is non-zero).
    Params      abi.MethodParams
}  // representation tuple

type SignedMessage struct {
    Message    UnsignedMessage
    Signature  filcrypto.Signature
}  // representation tuple
package message

import (
	filcrypto "github.com/filecoin-project/specs/algorithms/crypto"
	util "github.com/filecoin-project/specs/util"
)

var IMPL_FINISH = util.IMPL_FINISH

type Serialization = util.Serialization

// The maximum serialized size of a SignedMessage.
const MessageMaxSize = 32 * 1024

func SignedMessage_Make(message UnsignedMessage, signature filcrypto.Signature) SignedMessage {
	return &SignedMessage_I{
		Message_:   message,
		Signature_: signature,
	}
}

func Sign(message UnsignedMessage, keyPair filcrypto.SigKeyPair) (SignedMessage, error) {
	sig, err := filcrypto.Sign(keyPair, util.Bytes(Serialize_UnsignedMessage(message)))
	if err != nil {
		return nil, err
	}
	return SignedMessage_Make(message, sig), nil
}

func SignatureVerificationError() error {
	IMPL_FINISH()
	panic("")
}

func Verify(message SignedMessage, publicKey filcrypto.PublicKey) (UnsignedMessage, error) {
	m := util.Bytes(Serialize_UnsignedMessage(message.Message()))
	sigValid, err := filcrypto.Verify(publicKey, message.Signature(), m)
	if err != nil {
		return nil, err
	}
	if !sigValid {
		return nil, SignatureVerificationError()
	}
	return message.Message(), nil
}

func (x *GasAmount_I) Add(y GasAmount) GasAmount {
	IMPL_FINISH()
	panic("")
}

func (x *GasAmount_I) Subtract(y GasAmount) GasAmount {
	IMPL_FINISH()
	panic("")
}

func (x *GasAmount_I) SubtractIfNonnegative(y GasAmount) (ret GasAmount, ok bool) {
	ret = x.Subtract(y)
	ok = true
	if ret.LessThan(GasAmount_Zero()) {
		ret = x
		ok = false
	}
	return
}

func (x *GasAmount_I) LessThan(y GasAmount) bool {
	IMPL_FINISH()
	panic("")
}

func (x *GasAmount_I) Equals(y GasAmount) bool {
	IMPL_FINISH()
	panic("")
}

func (x *GasAmount_I) Scale(count int) GasAmount {
	IMPL_FINISH()
	panic("")
}

func GasAmount_Affine(b GasAmount, x int, m GasAmount) GasAmount {
	return b.Add(m.Scale(x))
}

func GasAmount_Zero() GasAmount {
	return GasAmount_FromInt(0)
}

func GasAmount_FromInt(x int) GasAmount {
	IMPL_FINISH()
	panic("")
}

func GasAmount_SentinelUnlimited() GasAmount {
	// Amount of gas larger than any feasible execution; meant to indicated unlimited gas
	// (e.g., for builtin system method invocations).
	return GasAmount_FromInt(1).Scale(1e9).Scale(1e9) // 10^18
}

2.3.5 VM运行环境(在VM内部)

2.3.5.1 回执

MessageReceipt包含顶级消息执行的结果。 语法正确的回执具有:

  • 非负的ExitCode
  • 仅当退出代码为零时,才返回非空的ReturnValue
  • 非负的GasUsed

2.3.5.2 vm/runtime 接口

package runtime

import actor "github.com/filecoin-project/specs-actors/actors"
import abi "github.com/filecoin-project/specs-actors/actors/abi"
import crypto "github.com/filecoin-project/specs-actors/actors/crypto"
import exitcode "github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
import addr "github.com/filecoin-project/go-address"
import indices "github.com/filecoin-project/specs-actors/actors/runtime/indices"
import cid "github.com/ipfs/go-cid"

// Runtime is the VM's internal runtime object.
// this is everything that is accessible to actors, beyond parameters.
type Runtime interface {
	CurrEpoch() abi.ChainEpoch

	// Randomness returns a (pseudo)random string for the given epoch and tag.
	GetRandomness(epoch abi.ChainEpoch) abi.RandomnessSeed

	// The address of the immediate calling actor.
	// Not necessarily the actor in the From field of the initial on-chain Message.
	// Always an ID-address.
	ImmediateCaller() addr.Address
	ValidateImmediateCallerIs(caller addr.Address)
	ValidateImmediateCallerInSet(callers []addr.Address)
	ValidateImmediateCallerAcceptAnyOfType(type_ abi.ActorCodeID)
	ValidateImmediateCallerAcceptAnyOfTypes(types []abi.ActorCodeID)
	ValidateImmediateCallerAcceptAny()
	ValidateImmediateCallerMatches(CallerPattern)

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

	// The actor who mined the block in which the initial on-chain message appears.
	// Always an ID-address.
	ToplevelBlockWinner() addr.Address

	AcquireState() ActorStateHandle

	SuccessReturn() InvocOutput
	ValueReturn([]byte) InvocOutput

	// Throw an error indicating a failure condition has occurred, from which the given actor
	// code is unable to recover.
	Abort(errExitCode exitcode.ExitCode, msg string)

	// Calls Abort with InvalidArguments_User.
	AbortArgMsg(msg string)
	AbortArg()

	// Calls Abort with InconsistentState_User.
	AbortStateMsg(msg string)
	AbortState()

	// Calls Abort with InsufficientFunds_User.
	AbortFundsMsg(msg string)
	AbortFunds()

	// Calls Abort with RuntimeAPIError.
	// For internal use only (not in actor code).
	AbortAPI(msg string)

	// Check that the given condition is true (and call Abort if not).
	Assert(bool)

	CurrentBalance() abi.TokenAmount
	ValueReceived() abi.TokenAmount

	// Look up the current values of several system-wide economic indices.
	CurrIndices() indices.Indices

	// Look up the code ID of a given actor address.
	GetActorCodeID(addr addr.Address) (ret abi.ActorCodeID, ok bool)

	// Run a (pure function) computation, consuming the gas cost associated with that function.
	// This mechanism is intended to capture the notion of an ABI between the VM and native
	// functions, and should be used for any function whose computation is expensive.
	Compute(ComputeFunctionID, args []interface{}) interface{}

	// Sends a message to another actor.
	// If the invoked method does not return successfully, this caller will be aborted too.
	SendPropagatingErrors(input InvocInput) InvocOutput
	Send(
		toAddr addr.Address,
		methodNum abi.MethodNum,
		params abi.MethodParams,
		value abi.TokenAmount,
	) InvocOutput
	SendQuery(
		toAddr addr.Address,
		methodNum abi.MethodNum,
		params abi.MethodParams,
	) []byte
	SendFunds(toAddr addr.Address, value abi.TokenAmount)

	// Sends a message to another actor, trapping an unsuccessful execution.
	// This may only be invoked by the singleton Cron actor.
	SendCatchingErrors(input InvocInput) (output InvocOutput, exitCode exitcode.ExitCode)

	// 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 in the state tree, with empty state. May only be called by InitActor.
	CreateActor(
		// The new actor's code identifier.
		codeId abi.ActorCodeID,
		// Address under which the new actor's state will be stored. Must be an ID-address.
		address addr.Address,
	)

	// Deletes an actor in the state tree. May only be called by the actor itself,
	// or by StoragePowerActor in the case of StorageMinerActors.
	DeleteActor(address addr.Address)

	// Retrieves and deserializes an object from the store into o. Returns whether successful.
	IpldGet(c cid.Cid, o interface{}) bool
	// Serializes and stores an object, returning its CID.
	IpldPut(x interface{}) cid.Cid

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

type Syscalls interface {
	// Verifies that a signature is valid for an address and plaintext.
	VerifySignature(
		signature crypto.Signature,
		signer addr.Address,
		plaintext []byte,
	) bool
	// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes.
	ComputeUnsealedSectorCID(sectorSize abi.SectorSize, pieces []abi.PieceInfo) (abi.UnsealedSectorCID, error)
	// Verifies a sector seal proof.
	VerifySeal(sectorSize abi.SectorSize, vi abi.SealVerifyInfo) bool
	// Verifies a proof of spacetime.
	VerifyPoSt(sectorSize abi.SectorSize, vi abi.PoStVerifyInfo) bool
}

type InvocInput struct {
	To     addr.Address
	Method abi.MethodNum
	Params abi.MethodParams
	Value  abi.TokenAmount
}

type InvocOutput struct {
	ReturnValue []byte
}

type ActorStateHandle interface {
	UpdateRelease(newStateCID actor.ActorSubstateCID)
	Release(checkStateCID actor.ActorSubstateCID)
	Take() actor.ActorSubstateCID
}

type ComputeFunctionID int64

const (
	Compute_VerifySignature = ComputeFunctionID(1)
)

2.3.5.3 vm/runtime 实现

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.RandomnessAtEpoch(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)
}

2.3.5.4 代码加载

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)
}

2.3.5.5 退出码(Exit codes)

package exitcode

type ExitCode int64

const (
	// TODO: remove once canonical error codes are finalized
	SystemErrorCode_Placeholder      = ExitCode(-(1 << 30))
	UserDefinedErrorCode_Placeholder = ExitCode(-(1 << 30))
)

const Ok = ExitCode(0)

// TODO: assign all of these.
const (
	// ActorNotFound represents a failure to find an actor.
	ActorNotFound = SystemErrorCode_Placeholder + iota

	// ActorCodeNotFound represents a failure to find the code for a
	// particular actor in the VM registry.
	ActorCodeNotFound

	// InvalidMethod represents a failure to find a method in
	// an actor
	InvalidMethod

	// InvalidArgumentsSystem indicates that a method was called with the incorrect
	// number of arguments, or that its arguments did not satisfy its
	// preconditions
	InvalidArguments_System

	// InsufficientFunds represents a failure to apply a message, as
	// it did not carry sufficient funds for its application.
	InsufficientFunds_System

	// InvalidCallSeqNum represents a message invocation out of sequence.
	// This happens when message.CallSeqNum is not exactly actor.CallSeqNum + 1
	InvalidCallSeqNum

	// OutOfGas is returned when the execution of an actor method
	// (including its subcalls) uses more gas than initially allocated.
	OutOfGas

	// RuntimeAPIError is returned when an actor method invocation makes a call
	// to the runtime that does not satisfy its preconditions.
	RuntimeAPIError

	// RuntimeAssertFailure is returned when an actor method invocation calls
	// rt.Assert with a false condition.
	RuntimeAssertFailure

	// MethodSubcallError is returned when an actor method's Send call has
	// returned with a failure error code (and the Send call did not specify
	// to ignore errors).
	MethodSubcallError
)

const (
	InsufficientFunds_User = UserDefinedErrorCode_Placeholder + iota
	InvalidArguments_User
	InconsistentState_User

	InvalidSectorPacking
	SealVerificationFailed
	PoStVerificationFailed
	DeadlineExceeded
	InsufficientPledgeCollateral
)

func (x ExitCode) IsSuccess() bool {
	return x == Ok
}

func (x ExitCode) IsError() bool {
	return !x.IsSuccess()
}

func (x ExitCode) AllowsStateUpdate() bool {
	return x.IsSuccess()
}

func OK() ExitCode {
	return Ok
}

func EnsureErrorCode(x ExitCode) ExitCode {
	if !x.IsError() {
		// Throwing an error with a non-error exit code is itself an error
		x = (RuntimeAPIError)
	}
	return x
}