fvm

package
v0.17.6 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2021 License: AGPL-3.0 Imports: 33 Imported by: 24

README

Flow Virtual Machine (FVM)

The Flow Virtual Machine (FVM) augments the Cadence runtime with the domain-specific functionality required by the Flow protocol.

Usage

Quickstart
import (
    "github.com/onflow/cadence/runtime"
    "github.com/onflow/flow-go/fvm"
    "github.com/onflow/flow-go/fvm/state"
    "github.com/onflow/flow-go/model/flow"
)

vm := fvm.New(runtime.NewInterpreterRuntime())

tx := flow.NewTransactionBody().
    SetScript([]byte(`transaction { execute { log("Hello, World!") } }`))

ctx := fvm.NewContext()
ledger := state.NewMapLedger()

txIndex := uint32(0)
txProc := fvm.Transaction(tx, txIndex)

err := vm.Run(ctx, txProc, ledger)
if err != nil {
  panic("fatal error during transaction procedure!")
}

fmt.Println(txProc.Logs[0]) // prints "Hello, World!"
Procedure

The FVM interacts with the Flow execution state by running atomic procedures against a shared ledger. A Procedure is an operation that can be applied to a Ledger.

Procedure Types
Transactions (Read/write)

A TransactionProcedure is an operation that mutates the ledger state.

A transaction procedure can be created from a flow.TransactionBody:

var tx flow.TransactionBody
var txIndex uint32

i := fvm.Transaction(tx, txIndex)

A transaction procedure has the following steps:

  1. Verify all transaction signatures against the current ledger state
  2. Validate and increment the proposal key sequence number
  3. Deduct transaction fee from the payer account
  4. Invoke transaction script with provided arguments and authorizers
Scripts (Read-only)

A ScriptProcedure is an operation that reads from the ledger state.

foo := fvm.Script([]byte(`fun main(): Int { return 42 }`))

A script can have optionally bound arguments:

script := fvm.Script([]byte(`fun main(x: Int): Int { x * 2 }`))

foo := script.WithArguments(argA)
bar := script.WithArguments(argB)
Contexts

The VM runs procedures inside of an execution context that defines the host functionality provided to the Cadence runtime. A Context instance is an immutable object, and any changes to a context must be made by spawning a new child context.

vm := fvm.New(runtime.NewInterpreterRuntime())

globalCtx := fvm.NewContext()

// create separate contexts for different blocks
block1Ctx := fvm.NewContextFromParent(globalCtx, fvm.WithBlockHeader(block1))
block2Ctx := fvm.NewContextFromParent(globalCtx, fvm.WithBlockHeader(block2))

// contexts can be safely used in parallel
go executeBlock(vm, block1Ctx)
go executeBlock(vm, block2Ctx)
Context Options

TODO: document context options

Design

The structure of the FVM package is intended to promote the following:

  • Determinism: the VM should be consistent and predictable, producing identical results given the same inputs.
  • Performance: the VM should expose functionality that allows the caller to exploit parallelism and pre-computation.
  • Flexibility: the VM should support a variety of use cases including not only execution and verification, but also developer tooling and network emulation.
Context

A Context is a reusable component that simply defines the parameters of an execution environment. For example, all transactions within the same block would share a common Context configured with the relevant block data.

Contexts can be arbitrarily nested. A child context inherits the parameters of its parent, but is free to override any parameters that it chooses.

HostEnvironment

A HostEnvironment is a short-lived component that is consumed by a procedure. A HostEnvironment is the interface through which the VM communicates with the Cadence runtime. The HostEnvironment provides information about the current Context and also collects logs, events and metrics emitted from the runtime.

Procedure Lifecycle

The diagram below illustrates the relationship between contexts, host environments and procedures. Multiple procedures can reuse the same context, each of which is supplied with a unique HostEnvironment instance.

fvm

TODO

  • Improve error handling
  • Create list of all missing test cases
  • Implement missing test cases
  • Documentation
    • Transaction validation (signatures, sequence numbers)
    • Fee payments
    • Address generation
    • Account management
    • Bootstrapping
    • Ledger usage

Open Questions

  • Currently the caller is responsible for providing the correct Ledger instance when running a procedure. When performing atomic operations in batches (i.e blocks or chunks), multiple ledgers are spawned in a hierarchy similar to that of multiple Context instances. Does it make sense for the fvm package to manage ledgers as well?

Documentation

Index

Constants

View Source
const (
	DefaultGasLimit                     = 100_000 // 100K
	DefaultEventCollectionByteSizeLimit = 256_000 // 256KB
	DefaultMaxNumOfTxRetries            = 3
)
View Source
const AccountKeyWeightThreshold = 1000

Variables

View Source
var DefaultAccountCreationFee = func() cadence.UFix64 {
	value, err := cadence.NewUFix64("0.00100000")
	if err != nil {
		panic(fmt.Errorf("invalid default account creation fee: %w", err))
	}
	return value
}()
View Source
var DefaultMinimumStorageReservation = func() cadence.UFix64 {
	value, err := cadence.NewUFix64("0.00100000")
	if err != nil {
		panic(fmt.Errorf("invalid default minimum storage reservation: %w", err))
	}
	return value
}()
View Source
var DefaultStorageMBPerFLOW = func() cadence.UFix64 {
	value, err := cadence.NewUFix64("10.00000000")
	if err != nil {
		panic(fmt.Errorf("invalid default minimum storage reservation: %w", err))
	}
	return value
}()
View Source
var DefaultTransactionFees = func() cadence.UFix64 {
	value, err := cadence.NewUFix64("0.0001")
	if err != nil {
		panic(fmt.Errorf("invalid default transaction fees: %w", err))
	}
	return value
}()

DefaultTransactionFees are the default transaction fees if transaction fees are on. If they are off (which is the default behaviour) that means the transaction fees are 0.0.

Functions

func FlowFeesAddress added in v0.14.0

func FlowFeesAddress(chain flow.Chain) flow.Address

func FlowTokenAddress

func FlowTokenAddress(chain flow.Chain) flow.Address

func FungibleTokenAddress

func FungibleTokenAddress(chain flow.Chain) flow.Address

func NewInterpreterRuntime added in v0.15.1

func NewInterpreterRuntime() runtime.Runtime

Types

type Blocks

type Blocks interface {
	// ByHeight returns the block at the given height in the chain ending in `header` (or finalized
	// if `header` is nil). This enables querying un-finalized blocks by height with respect to the
	// chain defined by the block we are executing.
	ByHeightFrom(height uint64, header *flow.Header) (*flow.Header, error)
}

func NewBlockFinder added in v0.13.0

func NewBlockFinder(storage storage.Headers) Blocks

NewBlockFinder constructs a new block finder

type BlocksFinder added in v0.13.0

type BlocksFinder struct {
	// contains filtered or unexported fields
}

BlocksFinder finds blocks and return block headers

func (*BlocksFinder) ByHeightFrom added in v0.13.0

func (b *BlocksFinder) ByHeightFrom(height uint64, header *flow.Header) (*flow.Header, error)

ByHeightFrom returns the block header by height.

type BootstrapProcedure

type BootstrapProcedure struct {
	// contains filtered or unexported fields
}

A BootstrapProcedure is an invokable that can be used to bootstrap the ledger state with the default accounts and contracts required by the Flow virtual machine.

func Bootstrap

func Bootstrap(
	serviceAccountPublicKey flow.AccountPublicKey,
	opts ...BootstrapProcedureOption,
) *BootstrapProcedure

Bootstrap returns a new BootstrapProcedure instance configured with the provided genesis parameters.

func (*BootstrapProcedure) Run

type BootstrapProcedureOption added in v0.14.0

type BootstrapProcedureOption func(*BootstrapProcedure) *BootstrapProcedure

func WithAccountCreationFee added in v0.14.0

func WithAccountCreationFee(fee cadence.UFix64) BootstrapProcedureOption

func WithInitialTokenSupply added in v0.14.0

func WithInitialTokenSupply(supply cadence.UFix64) BootstrapProcedureOption

func WithMinimumStorageReservation added in v0.14.0

func WithMinimumStorageReservation(reservation cadence.UFix64) BootstrapProcedureOption

func WithRestrictedAccountCreationEnabled added in v0.17.6

func WithRestrictedAccountCreationEnabled(enabled cadence.Bool) BootstrapProcedureOption

func WithStorageMBPerFLOW added in v0.16.1

func WithStorageMBPerFLOW(ratio cadence.UFix64) BootstrapProcedureOption

func WithTransactionFee added in v0.14.0

func WithTransactionFee(fee cadence.UFix64) BootstrapProcedureOption

type Context

type Context struct {
	Chain                         flow.Chain
	Blocks                        Blocks
	Metrics                       handler.MetricsReporter
	Tracer                        module.Tracer
	GasLimit                      uint64
	MaxStateKeySize               uint64
	MaxStateValueSize             uint64
	MaxStateInteractionSize       uint64
	EventCollectionByteSizeLimit  uint64
	MaxNumOfTxRetries             uint8
	BlockHeader                   *flow.Header
	ServiceAccountEnabled         bool
	RestrictedDeploymentEnabled   bool
	LimitAccountStorage           bool
	TransactionFeesEnabled        bool
	CadenceLoggingEnabled         bool
	EventCollectionEnabled        bool
	ServiceEventCollectionEnabled bool
	AccountFreezeAvailable        bool
	ExtensiveTracing              bool
	SignatureVerifier             crypto.SignatureVerifier
	TransactionProcessors         []TransactionProcessor
	ScriptProcessors              []ScriptProcessor
	Logger                        zerolog.Logger
}

A Context defines a set of execution parameters used by the virtual machine.

func NewContext

func NewContext(logger zerolog.Logger, opts ...Option) Context

NewContext initializes a new execution context with the provided options.

func NewContextFromParent

func NewContextFromParent(parent Context, opts ...Option) Context

NewContextFromParent spawns a child execution context with the provided options.

type Option

type Option func(ctx Context) Context

An Option sets a configuration parameter for a virtual machine context.

func WithAccountFreezeAvailable added in v0.15.1

func WithAccountFreezeAvailable(accountFreezeAvailable bool) Option

WithAccountFreezeAvailable sets availability of account freeze function for a virtual machine context.

With this option set to true, a setAccountFreeze function will be enabled for transactions processed by the VM

func WithAccountStorageLimit added in v0.14.0

func WithAccountStorageLimit(enabled bool) Option

WithAccountStorageLimit enables or disables checking if account storage used is over its storage capacity

func WithBlockHeader

func WithBlockHeader(header *flow.Header) Option

WithBlockHeader sets the block header for a virtual machine context.

The VM uses the header to provide current block information to the Cadence runtime, as well as to seed the pseudorandom number generator.

func WithBlocks

func WithBlocks(blocks Blocks) Option

WithBlocks sets the block storage provider for a virtual machine context.

The VM uses the block storage provider to provide historical block information to the Cadence runtime.

func WithCadenceLogging added in v0.13.1

func WithCadenceLogging(enabled bool) Option

WithCadenceLogging enables or disables Cadence logging for a virtual machine context.

func WithChain

func WithChain(chain flow.Chain) Option

WithChain sets the chain parameters for a virtual machine context.

func WithEventCollectionSizeLimit added in v0.13.1

func WithEventCollectionSizeLimit(limit uint64) Option

WithEventCollectionSizeLimit sets the event collection byte size limit for a virtual machine context.

func WithExtensiveTracing added in v0.16.0

func WithExtensiveTracing() Option

WithExtensiveTracing sets the extensive tracing

func WithGasLimit

func WithGasLimit(limit uint64) Option

WithGasLimit sets the gas limit for a virtual machine context.

func WithMaxStateInteractionSize added in v0.14.0

func WithMaxStateInteractionSize(limit uint64) Option

WithMaxStateInteractionSize sets the byte size limit for total interaction with ledger. this prevents attacks such as reading all large registers

func WithMaxStateKeySize added in v0.14.0

func WithMaxStateKeySize(limit uint64) Option

WithMaxStateKeySize sets the byte size limit for ledger keys

func WithMaxStateValueSize added in v0.14.0

func WithMaxStateValueSize(limit uint64) Option

WithMaxStateValueSize sets the byte size limit for ledger values

func WithMetricsReporter added in v0.17.6

func WithMetricsReporter(mr handler.MetricsReporter) Option

WithMetricsReporter sets the metrics collector for a virtual machine context.

A metrics collector is used to gather metrics reported by the Cadence runtime.

func WithRestrictedDeployment

func WithRestrictedDeployment(enabled bool) Option

WithRestrictedDeployment enables or disables restricted contract deployment for a virtual machine context.

func WithServiceAccount

func WithServiceAccount(enabled bool) Option

WithServiceAccount enables or disables calls to the Flow service account.

func WithServiceEventCollectionEnabled added in v0.17.1

func WithServiceEventCollectionEnabled() Option

WithServiceEventCollectionEnabled enables service event collection

func WithTracer added in v0.14.5

func WithTracer(tr module.Tracer) Option

WithTracer sets the tracer for a virtual machine context.

func WithTransactionFeesEnabled added in v0.17.1

func WithTransactionFeesEnabled(enabled bool) Option

WithTransactionFeesEnabled enables or disables deduction of transaction fees

func WithTransactionProcessors

func WithTransactionProcessors(processors ...TransactionProcessor) Option

WithTransactionProcessors sets the transaction processors for a virtual machine context.

type Procedure

type Procedure interface {
	Run(vm *VirtualMachine, ctx Context, sth *state.StateHolder, programs *programs.Programs) error
}

An Procedure is an operation (or set of operations) that reads or writes ledger state.

type ScriptInvocator

type ScriptInvocator struct{}

func NewScriptInvocator

func NewScriptInvocator() ScriptInvocator

func (ScriptInvocator) Process

func (i ScriptInvocator) Process(
	vm *VirtualMachine,
	ctx Context,
	proc *ScriptProcedure,
	sth *state.StateHolder,
	programs *programs.Programs,
) error

type ScriptProcedure

type ScriptProcedure struct {
	ID        flow.Identifier
	Script    []byte
	Arguments [][]byte
	Value     cadence.Value
	Logs      []string
	Events    []flow.Event
	GasUsed   uint64
	Err       errors.Error
}

func Script

func Script(code []byte) *ScriptProcedure

func (*ScriptProcedure) Run

func (proc *ScriptProcedure) Run(vm *VirtualMachine, ctx Context, sth *state.StateHolder, programs *programs.Programs) error

func (*ScriptProcedure) WithArguments

func (proc *ScriptProcedure) WithArguments(args ...[]byte) *ScriptProcedure

type ScriptProcessor

type ScriptProcessor interface {
	Process(*VirtualMachine, Context, *ScriptProcedure, *state.StateHolder, *programs.Programs) error
}

type TransactionAccountFrozenChecker added in v0.15.1

type TransactionAccountFrozenChecker struct{}

func NewTransactionAccountFrozenChecker added in v0.15.1

func NewTransactionAccountFrozenChecker() *TransactionAccountFrozenChecker

func (*TransactionAccountFrozenChecker) Process added in v0.15.1

type TransactionAccountFrozenEnabler added in v0.15.1

type TransactionAccountFrozenEnabler struct{}

func NewTransactionAccountFrozenEnabler added in v0.15.1

func NewTransactionAccountFrozenEnabler() *TransactionAccountFrozenEnabler

func (*TransactionAccountFrozenEnabler) Process added in v0.15.1

type TransactionContractFunctionInvocator added in v0.17.1

type TransactionContractFunctionInvocator struct {
	// contains filtered or unexported fields
}

func NewTransactionContractFunctionInvocator added in v0.17.1

func NewTransactionContractFunctionInvocator(
	contractLocation common.AddressLocation,
	functionName string,
	arguments []interpreter.Value,
	argumentTypes []sema.Type,
	logger zerolog.Logger) *TransactionContractFunctionInvocator

func (*TransactionContractFunctionInvocator) Invoke added in v0.17.1

func (i *TransactionContractFunctionInvocator) Invoke(env *hostEnv, parentTraceSpan opentracing.Span) (cadence.Value, error)

type TransactionInvocator

type TransactionInvocator struct {
	// contains filtered or unexported fields
}

func NewTransactionInvocator

func NewTransactionInvocator(logger zerolog.Logger) *TransactionInvocator

func (*TransactionInvocator) Process

func (i *TransactionInvocator) Process(
	vm *VirtualMachine,
	ctx *Context,
	proc *TransactionProcedure,
	sth *state.StateHolder,
	programs *programs.Programs,
) (processErr error)

type TransactionProcedure

type TransactionProcedure struct {
	ID            flow.Identifier
	Transaction   *flow.TransactionBody
	TxIndex       uint32
	Logs          []string
	Events        []flow.Event
	ServiceEvents []flow.Event
	GasUsed       uint64
	Err           errors.Error
	Retried       int
	TraceSpan     opentracing.Span
}

func Transaction

func Transaction(tx *flow.TransactionBody, txIndex uint32) *TransactionProcedure

func (*TransactionProcedure) Run

func (proc *TransactionProcedure) Run(vm *VirtualMachine, ctx Context, st *state.StateHolder, programs *programs.Programs) error

func (*TransactionProcedure) SetTraceSpan added in v0.14.5

func (proc *TransactionProcedure) SetTraceSpan(traceSpan opentracing.Span)

type TransactionProcessor

type TransactionProcessor interface {
	Process(*VirtualMachine, *Context, *TransactionProcedure, *state.StateHolder, *programs.Programs) error
}

type TransactionSequenceNumberChecker

type TransactionSequenceNumberChecker struct{}

func NewTransactionSequenceNumberChecker

func NewTransactionSequenceNumberChecker() *TransactionSequenceNumberChecker

func (*TransactionSequenceNumberChecker) Process

type TransactionSignatureVerifier

type TransactionSignatureVerifier struct {
	SignatureVerifier  crypto.SignatureVerifier
	KeyWeightThreshold int
}

func NewTransactionSignatureVerifier

func NewTransactionSignatureVerifier(keyWeightThreshold int) *TransactionSignatureVerifier

func (*TransactionSignatureVerifier) Process

type TransactionStorageLimiter added in v0.14.0

type TransactionStorageLimiter struct {
	// A function to create a function to get storage capacity from an address. This is to make this easily testable.
	GetStorageCapacityFuncFactory func(
		vm *VirtualMachine,
		ctx Context,
		tp *TransactionProcedure,
		sth *state.StateHolder,
		programs *programs.Programs,
	) (func(address common.Address) (value uint64, err error), error)
}

func NewTransactionStorageLimiter added in v0.14.0

func NewTransactionStorageLimiter() *TransactionStorageLimiter

func (*TransactionStorageLimiter) Process added in v0.14.0

type VirtualMachine

type VirtualMachine struct {
	Runtime runtime.Runtime
}

A VirtualMachine augments the Cadence runtime with Flow host functionality.

func NewVirtualMachine added in v0.15.1

func NewVirtualMachine(rt runtime.Runtime) *VirtualMachine

NewVirtualMachine creates a new virtual machine instance with the provided runtime.

func (*VirtualMachine) GetAccount

func (vm *VirtualMachine) GetAccount(ctx Context, address flow.Address, v state.View, programs *programs.Programs) (*flow.Account, error)

GetAccount returns an account by address or an error if none exists.

func (*VirtualMachine) Run

func (vm *VirtualMachine) Run(ctx Context, proc Procedure, v state.View, programs *programs.Programs) (err error)

Run runs a procedure against a ledger in the given context.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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