fvm

package
v0.12.6 Latest Latest
Warning

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

Go to latest
Published: Dec 9, 2020 License: AGPL-3.0 Imports: 24 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()

txProc := fvm.Transaction(tx)

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

i := fvm.Transaction(tx)

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 AccountKeyWeightThreshold = 1000
View Source
const TopShotContractAddress = "0b2a3299cc857e29"

Variables

View Source
var ErrAccountNotFound = errors.New("account not found")
View Source
var ErrInvalidHashAlgorithm = errors.New("invalid hash algorithm")

Functions

func FlowTokenAddress

func FlowTokenAddress(chain flow.Chain) flow.Address

func FungibleTokenAddress

func FungibleTokenAddress(chain flow.Chain) flow.Address

func StringToHashingAlgorithm

func StringToHashingAlgorithm(s string) hash.HashingAlgorithm

StringToHashingAlgorithm converts a string to a HashingAlgorithm.

func StringToSigningAlgorithm

func StringToSigningAlgorithm(s string) crypto.SigningAlgorithm

StringToSigningAlgorithm converts a string to a SigningAlgorithm.

func SystemChunkTransaction

func SystemChunkTransaction(serviceAddress flow.Address) *flow.TransactionBody

SystemChunkTransaction creates and returns the transaction corresponding to the system chunk at the specified service address.

Types

type ASTCache

type ASTCache interface {
	GetProgram(ast.Location) (*ast.Program, error)
	SetProgram(ast.Location, *ast.Program) error
}

ASTCache is an interface to a cache for parsed program ASTs.

type Blocks

type Blocks interface {
	// ByHeight returns the block at the given height. It is only available
	// for finalized blocks.
	ByHeight(height uint64) (*flow.Block, error)
}

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(
	servicePublicKey flow.AccountPublicKey,
	initialTokenSupply cadence.UFix64,
) *BootstrapProcedure

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

func (*BootstrapProcedure) Run

func (b *BootstrapProcedure) Run(vm *VirtualMachine, ctx Context, ledger state.Ledger) error

type Context

type Context struct {
	Chain                            flow.Chain
	ASTCache                         ASTCache
	Blocks                           Blocks
	Metrics                          *MetricsCollector
	GasLimit                         uint64
	BlockHeader                      *flow.Header
	ServiceAccountEnabled            bool
	RestrictedAccountCreationEnabled bool
	RestrictedDeploymentEnabled      bool
	SetValueHandler                  SetValueHandler
	SignatureVerifier                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 DefaultSignatureVerifier

type DefaultSignatureVerifier struct{}

func NewDefaultSignatureVerifier

func NewDefaultSignatureVerifier() DefaultSignatureVerifier

func (DefaultSignatureVerifier) Verify

func (DefaultSignatureVerifier) Verify(
	signature []byte,
	tag []byte,
	message []byte,
	publicKey crypto.PublicKey,
	hashAlgo hash.HashingAlgorithm,
) (bool, error)

type Error

type Error interface {
	Code() uint32
	Error() string
}

An Error represents a non-fatal error that is expected during normal operation of the virtual machine.

VM errors are distinct from fatal errors, which indicate an unexpected failure in the VM (i.e. storage, stack overflow).

Each VM error is identified by a unique error code that is returned to the user.

type ExecutionError

type ExecutionError struct {
	Err runtime.Error
}

func (*ExecutionError) Code

func (e *ExecutionError) Code() uint32

func (*ExecutionError) Error

func (e *ExecutionError) Error() string

type InvalidHashAlgorithmError

type InvalidHashAlgorithmError struct {
	Address  flow.Address
	KeyIndex uint64
	HashAlgo hash.HashingAlgorithm
}

An InvalidHashAlgorithmError indicates that a given key has an invalid hash algorithm.

func (*InvalidHashAlgorithmError) Code

func (*InvalidHashAlgorithmError) Error

func (e *InvalidHashAlgorithmError) Error() string

type InvalidProposalKeyMissingSignatureError

type InvalidProposalKeyMissingSignatureError struct {
	Address  flow.Address
	KeyIndex uint64
}

A InvalidProposalKeyMissingSignatureError indicates that a proposal key does not have a valid signature.

func (*InvalidProposalKeyMissingSignatureError) Code

func (*InvalidProposalKeyMissingSignatureError) Error

type InvalidProposalKeyPublicKeyDoesNotExistError

type InvalidProposalKeyPublicKeyDoesNotExistError struct {
	Address  flow.Address
	KeyIndex uint64
}

A InvalidProposalKeyPublicKeyDoesNotExistError indicates that proposal key specifies a nonexistent public key.

func (*InvalidProposalKeyPublicKeyDoesNotExistError) Code

func (*InvalidProposalKeyPublicKeyDoesNotExistError) Error

type InvalidProposalKeyPublicKeyRevokedError

type InvalidProposalKeyPublicKeyRevokedError struct {
	Address  flow.Address
	KeyIndex uint64
}

An InvalidProposalKeyPublicKeyRevokedError indicates that proposal key sequence number does not match the on-chain value.

func (*InvalidProposalKeyPublicKeyRevokedError) Code

func (*InvalidProposalKeyPublicKeyRevokedError) Error

type InvalidProposalKeySequenceNumberError

type InvalidProposalKeySequenceNumberError struct {
	Address           flow.Address
	KeyIndex          uint64
	CurrentSeqNumber  uint64
	ProvidedSeqNumber uint64
}

An InvalidProposalKeySequenceNumberError indicates that proposal key sequence number does not match the on-chain value.

func (*InvalidProposalKeySequenceNumberError) Code

func (*InvalidProposalKeySequenceNumberError) Error

type InvalidSignaturePublicKeyDoesNotExistError

type InvalidSignaturePublicKeyDoesNotExistError struct {
	Address  flow.Address
	KeyIndex uint64
}

An InvalidSignaturePublicKeyDoesNotExistError indicates that a signature specifies a public key that does not exist.

func (*InvalidSignaturePublicKeyDoesNotExistError) Code

func (*InvalidSignaturePublicKeyDoesNotExistError) Error

type InvalidSignaturePublicKeyRevokedError

type InvalidSignaturePublicKeyRevokedError struct {
	Address  flow.Address
	KeyIndex uint64
}

An InvalidSignaturePublicKeyRevokedError indicates that a signature specifies a public key that has been revoked.

func (*InvalidSignaturePublicKeyRevokedError) Code

func (*InvalidSignaturePublicKeyRevokedError) Error

type InvalidSignatureVerificationError

type InvalidSignatureVerificationError struct {
	Address  flow.Address
	KeyIndex uint64
}

An InvalidSignatureVerificationError indicates that a signature could not be verified using its specified public key.

func (*InvalidSignatureVerificationError) Code

func (*InvalidSignatureVerificationError) Error

type LRUASTCache

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

LRUASTCache implements a program AST cache with a LRU cache.

func NewLRUASTCache

func NewLRUASTCache(size int) (*LRUASTCache, error)

NewLRUASTCache creates a new LRU cache that implements the ASTCache interface.

func (*LRUASTCache) GetProgram

func (cache *LRUASTCache) GetProgram(location ast.Location) (*ast.Program, error)

GetProgram retrieves a program AST from the LRU cache.

func (*LRUASTCache) SetProgram

func (cache *LRUASTCache) SetProgram(location ast.Location, program *ast.Program) error

SetProgram adds a program AST to the LRU cache.

type MetricsCollector

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

A MetricsCollector accumulates performance metrics reported by the Cadence runtime.

A single collector instance will sum all reported values. For example, the "parsed" field will be incremented each time a program is parsed.

func NewMetricsCollector

func NewMetricsCollector() *MetricsCollector

NewMetricsCollectors returns a new runtime metrics collector.

func (*MetricsCollector) Checked

func (m *MetricsCollector) Checked() time.Duration

func (*MetricsCollector) Interpreted

func (m *MetricsCollector) Interpreted() time.Duration

func (*MetricsCollector) Parsed

func (m *MetricsCollector) Parsed() time.Duration

type MissingPayerError

type MissingPayerError struct{}

A MissingPayerError indicates that a transaction is missing a payer.

func (*MissingPayerError) Code

func (e *MissingPayerError) Code() uint32

func (*MissingPayerError) Error

func (e *MissingPayerError) Error() string

type MissingSignatureError

type MissingSignatureError struct {
	Address flow.Address
}

A MissingSignatureError indicates that a transaction is missing a required signature.

func (*MissingSignatureError) Code

func (e *MissingSignatureError) Code() uint32

func (*MissingSignatureError) Error

func (e *MissingSignatureError) Error() string

type Option

type Option func(ctx Context) Context

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

func WithASTCache

func WithASTCache(cache ASTCache) Option

WithASTCache sets the AST cache for a virtual machine context.

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 WithChain

func WithChain(chain flow.Chain) Option

WithChain sets the chain parameters for a virtual machine context.

func WithGasLimit

func WithGasLimit(limit uint64) Option

WithGasLimit sets the gas limit for a virtual machine context.

func WithMetricsCollector

func WithMetricsCollector(mc *MetricsCollector) Option

WithMetricsCollector sets the metrics collector for a virtual machine context.

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

func WithRestrictedAccountCreation

func WithRestrictedAccountCreation(enabled bool) Option

WithRestrictedAccountCreation enables or disables restricted account creation for a virtual machine context

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 WithSetValueHandler

func WithSetValueHandler(handler SetValueHandler) Option

WithSetValueHandler sets a handler that is called when a value is written by the Cadence runtime.

func WithTransactionProcessors

func WithTransactionProcessors(processors ...TransactionProcessor) Option

WithTransactionSignatureVerifier sets the transaction processors for a virtual machine context.

type Procedure

type Procedure interface {
	Run(vm *VirtualMachine, ctx Context, ledger state.Ledger) 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,
	ledger state.Ledger,
) error

type ScriptProcedure

type ScriptProcedure struct {
	ID        flow.Identifier
	Script    []byte
	Arguments [][]byte
	Value     cadence.Value
	Logs      []string
	Events    []cadence.Event
	// TODO: report gas consumption: https://github.com/dapperlabs/flow-go/issues/4139
	GasUsed uint64
	Err     Error
}

func Script

func Script(code []byte) *ScriptProcedure

func (*ScriptProcedure) Run

func (proc *ScriptProcedure) Run(vm *VirtualMachine, ctx Context, ledger state.Ledger) error

func (*ScriptProcedure) WithArguments

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

type ScriptProcessor

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

type SetValueHandler

type SetValueHandler func(owner flow.Address, key string, value cadence.Value) error

SetValueHandler receives a value written by the Cadence runtime.

type SignatureVerifier

type SignatureVerifier interface {
	Verify(
		signature []byte,
		tag []byte,
		message []byte,
		publicKey crypto.PublicKey,
		hashAlgo hash.HashingAlgorithm,
	) (bool, error)
}

type TransactionFeeDeductor

type TransactionFeeDeductor struct{}

func NewTransactionFeeDeductor

func NewTransactionFeeDeductor() *TransactionFeeDeductor

func (*TransactionFeeDeductor) Process

func (d *TransactionFeeDeductor) Process(
	vm *VirtualMachine,
	ctx Context,
	proc *TransactionProcedure,
	ledger state.Ledger,
) 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,
	ledger state.Ledger,
) error

type TransactionProcedure

type TransactionProcedure struct {
	ID          flow.Identifier
	Transaction *flow.TransactionBody
	Logs        []string
	Events      []cadence.Event
	// TODO: report gas consumption: https://github.com/dapperlabs/flow-go/issues/4139
	GasUsed uint64
	Err     Error
}

func (*TransactionProcedure) ConvertEvents

func (proc *TransactionProcedure) ConvertEvents(txIndex uint32) ([]flow.Event, error)

func (*TransactionProcedure) Run

func (proc *TransactionProcedure) Run(vm *VirtualMachine, ctx Context, ledger state.Ledger) error

type TransactionProcessor

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

type TransactionSequenceNumberChecker

type TransactionSequenceNumberChecker struct{}

func NewTransactionSequenceNumberChecker

func NewTransactionSequenceNumberChecker() *TransactionSequenceNumberChecker

func (*TransactionSequenceNumberChecker) Process

type TransactionSignatureVerifier

type TransactionSignatureVerifier struct {
	SignatureVerifier  SignatureVerifier
	KeyWeightThreshold int
}

func NewTransactionSignatureVerifier

func NewTransactionSignatureVerifier(keyWeightThreshold int) *TransactionSignatureVerifier

func (*TransactionSignatureVerifier) Process

type UUIDGenerator

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

func NewUUIDGenerator

func NewUUIDGenerator(uuids *state.UUIDs) *UUIDGenerator

func (*UUIDGenerator) GenerateUUID

func (u *UUIDGenerator) GenerateUUID() (uint64, error)

type VirtualMachine

type VirtualMachine struct {
	Runtime runtime.Runtime
}

A VirtualMachine augments the Cadence runtime with Flow host functionality.

func New

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

func (*VirtualMachine) GetAccount

func (vm *VirtualMachine) GetAccount(ctx Context, address flow.Address, ledger state.Ledger) (*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, ledger state.Ledger) 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