vm

package
v1.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: Apache-2.0, MIT Imports: 47 Imported by: 0

Documentation

Overview

Package vm implements a minimal, pure-Go execution shell sufficient for Curio's read-only and gas-estimation calls against Lantern.

Scope and non-scope

Filecoin's reference FVM (ref-fvm, Rust) executes WASM-compiled actor code with a full gas-metered runtime, syscalls, and IPLD store. Lantern cannot import ref-fvm (CGo) and there is no pure-Go FVM in the ecosystem today. So this package is deliberately **not** a full VM.

What this package DOES:

  • Decode and dispatch a Filecoin Message against the built-in actor method tables shipped in go-state-types (v17/v18).
  • Execute Account → Account `Send` (method 0) end-to-end: balance check, value transfer, gas accounting. This is the only on-chain write path we model with full fidelity.
  • For every other built-in actor method, run "gas accounting only": return a synthetic `MessageReceipt` with `ExitCode=0`, `Return=nil`, and a `GasUsed` value derived from the message size, method parameters and the canonical Filecoin gas schedule. **State is not mutated.** This matches what Lotus' `GasEstimateGasLimit` actually needs: it binary-searches for a gas value that succeeds without caring about the receipt body.
  • For user-deployed actors (EVM, native FVM actors v3+) and any unknown method, return `ExitCode=SysErrUnsupportedMethod`.

What this package DOES NOT do:

  • It does not execute actor logic (no PreCommit/ProveCommit math, no PoSt verification, no FIL+ allocation accounting).
  • It does not produce a new state root. `ApplyMessage` returns the input state root unchanged (modulo a synthetic per-message receipt CID we accumulate in an AMT for block-template assembly).
  • It does not run cron, vesting, reward issuance, or any of the per-block "implicit" messages a full Filecoin block would.

Why this is enough for Phase 7:

  • StateCall: callers only need ExitCode + GasUsed + ReturnValue for gas estimation and dry-run validation. For Curio's writes (PreCommit, ProveCommit, PoSt, etc.) the network's full nodes execute the message for real; Lantern only has to produce a plausible gas envelope so Curio can sign+broadcast.
  • GasEstimateMessageGas: binary search for the gas limit becomes trivial because our VM never charges more than a fixed per-method ceiling. We compose this with the real on-chain `BaseFee` and a mempool-derived premium percentile.
  • MinerCreateBlock (Phase 7 Part C): in dry-run mode the block's `ParentStateRoot` field is taken verbatim from the parent tipset (i.e. "no change"). We document this clearly: Lantern cannot produce a *publishable* block, but it can produce a syntactically valid `*types.BlockMsg` Curio's WinPoSt task can hand to its normal signing pipeline.

Provenance

  • Gas constants and per-message costs come from go-state-types/builtin/v18 (in particular the price-list lifted from Lotus 1.36's `chain/vm/gas_v15.go`).
  • Method dispatch uses the public `Methods` map shipped in `go-state-types/builtin/v{N}/<actor>/methods.go`.
  • No code is lifted from `lotus/chain/vm` directly.

All limitations are documented in PHASE7-BLOCKERS.md.

Index

Constants

View Source
const MaxBlockGas int64 = 10_000_000_000

MaxBlockGas is the per-block gas budget (10 billion units).

This matches `build.BlockGasLimit` in Lotus 1.36.

Variables

View Source
var ZeroSenderAddr addr.Address

ZeroSenderAddr is convenience for tests; not used internally.

Functions

func EpochAt

func EpochAt(s *hstore.Store, ep abi.ChainEpoch) (int64, error)

EpochAt computes how recent a tipset is relative to head. Returns -1 if epoch is in the future or store is empty.

func MaxGasCost

func MaxGasCost(msg *types.Message) big.Int

MaxGasCost returns the worst-case fee a sender will pay for a fully burnt gas budget: `msg.GasLimit × msg.GasFeeCap`.

Types

type ApplyOptions

type ApplyOptions struct {
	// NetworkVersion is the current Filecoin network version (e.g. nv26
	// for v18 actors).
	NetworkVersion network.Version

	// PriceList is the gas schedule. If zero-value, V15PriceList is used.
	PriceList PriceList

	// BaseFee is the parent tipset's base fee in attoFIL.
	BaseFee big.Int

	// DryRun = true means do not commit state. Always true in Phase 7
	// (we have no place to commit it anyway).
	DryRun bool
}

ApplyOptions controls execution behaviour.

type ApplyResult

type ApplyResult struct {
	Receipt    types.MessageReceipt
	MethodInfo *MethodInfo // nil for Send / unknown
	GasCost    GasCost
	Error      string
	// ParamsDecoded is the human-readable representation of the params,
	// when we can decode them. Useful for tracing.
	ParamsDecoded interface{}
}

ApplyResult is the structured return from Apply.

func Apply

func Apply(ctx context.Context, acc *accessor.Accessor, msg *types.Message, opts ApplyOptions) (*ApplyResult, error)

Apply executes a single message against the accessor's view of state.

`acc` is a read-only state accessor bound to the tipset's parent state root. We use it to (a) resolve `msg.From` and `msg.To` to ID addresses, (b) read the receiver actor's code CID for method dispatch, (c) check the sender's balance.

The returned ApplyResult.Receipt has:

  • ExitCode: 0 on success, SysErrUnsupportedMethod for unknown actors, SysErrInsufficientFunds when the sender can't cover gas+value.
  • GasUsed: estimated gas consumed.
  • Return: empty bytes (we don't synthesize return-typed payloads).

type BaseFeeSource

type BaseFeeSource interface {
	// CurrentBaseFee returns the latest known ParentBaseFee.
	CurrentBaseFee(ctx context.Context) (big.Int, error)
}

BaseFeeSource is the minimum chain-history surface the gas estimator needs.

type CallResult

type CallResult struct {
	Receipt    types.MessageReceipt
	MethodInfo *MethodInfo
	GasCost    GasCost
	Duration   time.Duration
	Error      string
}

CallResult mirrors the parts of api.InvocResult that callers care about.

func StateCall

func StateCall(ctx context.Context, acc *accessor.Accessor, msg *types.Message, opts ApplyOptions) (*CallResult, error)

StateCall runs `msg` in dry-run mode against `acc`'s state.

`acc` must already be bound to the desired tipset's parent state root. Phase 7 does not support specifying an arbitrary tipset key — callers must build the right accessor up-front.

type FilBigInt

type FilBigInt = big.Int

FilBigInt aliases the FIL-token bigint type used by gas math.

type GasCost

type GasCost struct {
	GasUsed            int64
	BaseFeeBurn        big.Int
	OverEstimationBurn big.Int
	MinerTip           big.Int
	MinerPenalty       big.Int
	Refund             big.Int
	TotalCost          big.Int
}

GasCost mirrors api.MessageGasCost.

type GasEstimator

type GasEstimator struct {
	Acc       *accessor.Accessor
	BaseFee   BaseFeeSource
	Premium   PremiumSource
	PriceList PriceList
}

GasEstimator computes Lotus-compatible gas estimates without executing the message.

func (*GasEstimator) EstimateFeeCap

func (e *GasEstimator) EstimateFeeCap(ctx context.Context, premium big.Int, _ int64) (big.Int, error)

EstimateFeeCap returns BaseFee * 1.5 + premium, with a floor of MinimumBaseFee. `maxqueueblocks` follows Lotus' convention but is currently ignored: we always assume "include within the next few blocks".

func (*GasEstimator) EstimateGasLimit

func (e *GasEstimator) EstimateGasLimit(ctx context.Context, msg *types.Message) (int64, error)

EstimateGasLimit returns a conservative gas-limit ceiling for `msg`.

We compute the union of:

  • The fixed-shape overhead (OnChainMessage + OnInvoke).
  • A per-method ceiling: 75M for known Curio-write methods (PreCommit, ProveCommit, PoSt, PublishStorageDeals), 30M for simple builtin sends, 10M for Send (method 0).
  • Plus a 25% safety margin.

Numbers are deliberately conservative: better to over-estimate than have Curio's message run out of gas mid-execution.

func (*GasEstimator) EstimateGasPremium

func (e *GasEstimator) EstimateGasPremium(ctx context.Context, nblocksincl uint64) (big.Int, error)

EstimateGasPremium returns the 60th percentile premium from the last `nblocksincl` epochs. Falls back to 100k attoFIL if no sample is available.

func (*GasEstimator) EstimateMessageGas

func (e *GasEstimator) EstimateMessageGas(ctx context.Context, msg *types.Message, maxFee big.Int) (*types.Message, error)

EstimateMessageGas fills in `msg.GasLimit`, `msg.GasFeeCap`, and `msg.GasPremium` and returns the modified message. If any of those fields are already non-zero, they are preserved (matching Lotus's "don't clobber explicit user input" rule).

type HeaderStoreFeeSource

type HeaderStoreFeeSource struct {
	Store *hstore.Store
}

HeaderStoreFeeSource implements BaseFeeSource against a persistent header store. CurrentBaseFee reads the latest tipset's ParentBaseFee.

func (*HeaderStoreFeeSource) CurrentBaseFee

func (h *HeaderStoreFeeSource) CurrentBaseFee(_ context.Context) (big.Int, error)

CurrentBaseFee returns the head tipset's ParentBaseFee. If the store is empty, returns build.MinimumBaseFee.

type MempoolPremiumSource

type MempoolPremiumSource struct {
	Pending func() []*types.SignedMessage
}

MempoolPremiumSource implements PremiumSource against an in-memory gossipsub mempool. Lantern's `net/mpool` package exposes a Pending() list of locally observed SignedMessages; we sample their GasPremium values directly.

func (*MempoolPremiumSource) RecentPremiums

func (m *MempoolPremiumSource) RecentPremiums(_ context.Context, _ int64, samples int) ([]big.Int, error)

RecentPremiums returns the GasPremium values of all currently-pending messages, up to `samples`. `lookback` is unused — gossipsub already constrains the freshness window.

type MethodInfo

type MethodInfo struct {
	Kind    actors.Kind
	Version int
	Method  abi.MethodNum
	Meta    builtin.MethodMeta
}

MethodInfo is the resolved metadata for one built-in method.

func ResolveMethod

func ResolveMethod(_ context.Context, kind actors.Kind, version int, m abi.MethodNum) (*MethodInfo, error)

ResolveMethod looks up the (kind, version, method) -> MethodMeta.

Returns an error if either the actor version is unsupported, or the method number is not registered for that actor.

func (*MethodInfo) String

func (mi *MethodInfo) String() string

String returns a human-readable description: "<kind>.<MethodName>".

type PremiumSource

type PremiumSource interface {
	// RecentPremiums returns up to `samples` recent GasPremium values
	// from messages included in the last `lookback` epochs. Order is
	// not specified.
	RecentPremiums(ctx context.Context, lookback int64, samples int) ([]big.Int, error)
}

PremiumSource exposes a rolling sample of recently included messages' premiums (attoFIL/gas) over the last `n` tipsets.

type PriceList

type PriceList struct {
	// OnChainMessageComputeBase is the constant component of per-message
	// gas. Lotus v15: 38863.
	OnChainMessageComputeBase int64

	// OnChainMessageStorageBase is the per-message base storage cost.
	// Lotus v15: 36 gas per byte over the encoded message length.
	OnChainMessageStoragePerByte int64

	// OnChainReturnValuePerByte is charged per byte returned to the caller.
	// Lotus v15: 1.
	OnChainReturnValuePerByte int64

	// OnMethodInvocation is the per-call base cost. Lotus v15: 75000.
	OnMethodInvocation int64

	// OnMethodInvocationValue is added when the call transfers value
	// (i.e. msg.Value > 0). Lotus v15: 39750.
	OnMethodInvocationValue int64

	// OnIpldGet / OnIpldPut are per-block costs charged inside actor
	// methods. We use the average cost of a HAMT load.
	OnIpldGetBase int64
	OnIpldPutBase int64

	// OnHashingBase is added once per cryptographic hashing operation.
	OnHashingBase int64

	// OnVerifySignatureBLS / Secp / Delegated.
	OnVerifySignatureBLS       int64
	OnVerifySignatureSecp256k1 int64
	OnVerifySignatureDelegated int64
}

PriceList is the gas schedule applied to a single message.

func V15PriceList

func V15PriceList() PriceList

V15PriceList returns the network-version-≥17 gas schedule.

Numbers transcribed from `github.com/filecoin-project/lotus/chain/vm/gas_v15.go` at commit a0ecb8687 (Lotus 1.36). They are deliberately constants here, not imported, to keep this package CGo-free and Lotus-import-free.

func (PriceList) OnChainMessage

func (p PriceList) OnChainMessage(encodedLen int) int64

OnChainMessage computes the per-message base cost (compute + storage). `encodedLen` is the CBOR-encoded length of the SignedMessage.

func (PriceList) OnChainReturnValue

func (p PriceList) OnChainReturnValue(retLen int) int64

OnChainReturnValue charges per byte of the return blob.

func (PriceList) OnInvoke

func (p PriceList) OnInvoke(valueXfer bool) int64

OnInvoke returns the per-call base cost. `valueXfer` indicates whether the call transfers FIL.

func (PriceList) OnSignature

func (p PriceList) OnSignature(sig gscrypto.SigType) int64

OnSignature returns the per-signature verification cost for the given signature type.

Directories

Path Synopsis
Package evm is a minimal, read-only EVM interpreter for executing view/pure contract calls against locally-verified Filecoin FEVM state (lantern#43 Part B, Stage 3).
Package evm is a minimal, read-only EVM interpreter for executing view/pure contract calls against locally-verified Filecoin FEVM state (lantern#43 Part B, Stage 3).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL