evm

package module
v0.0.0-...-bf5c25a Latest Latest
Warning

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

Go to latest
Published: Jul 1, 2020 License: LGPL-3.0-or-later Imports: 14 Imported by: 0

README

文档

evm实现了以太坊黄皮书中设计的虚拟机,可用于运行solidity编写的智能合约。

1. 项目结构

|
|- abi          //实现了外部调用智能合约的格式转换工具
|- core         //实现了一些接口
|- crypto       //密码学相关函数实现
|- db           //数据库实现
|- errors       //错误码定义
|- example      //示例,可参考example/README.md
|- gas          //汇编代码消耗的gas定义
|- precompile   //本地合约,golang实现
|- rlp          //编解码算法
|- tests        //测试
|- util         //公共函数
|- cache.go     //缓存,加速数据库操作
|- context.go   //evm运行上下文
|- evm.go       //汇编实现
|- interface.go //接口定义
|- opcodes.go   //汇编表
|- memory.go    //evm存储实现
|- stack.go     //evm存储实现

2. 要实现的几类接口

2.1. Account
type Account interface {
// Getter of account address / code / balance
// Setter of account code / balance
GetAddress() Address
GetBalance() uint64
AddBalance(balance uint64) error
SubBalance(balance uint64) error
GetCode() []byte
SetCode(code []byte)
// GetCodeHash return the hash of account code, please return [32]byte, and // // return [32]byte{0, ..., 0} if code is empty
GetCodeHash() []byte
GetNonce() uint64
SetNonce(nonce uint64)
// Suicide will suicide an account
Suicide()
HasSuicide() bool
}

要注意的有以下几点。

  • Nonce: Nonce需要是自增的(有些案例中交易为了随机性会有一个交易Nonce,这两者不一定要等价,虽然以太坊中是等价的)。
  • GetCodeHash:允许用户自己实现code的哈希函数,如果用户返回nil则会调用Keccak256函数,这一点和以太坊保持一致。
  • 对Balance的相关操作要注意溢出的错误处理。
2.2. Address
type Address interface {
    Bytes() []byte
}

用户需要为自己所实现的地址定义Bytes接口以转换为bytes供EVM所使用。

  • 如果序列化长度为32,则使用时不进行处理。
  • 如果序列化长度小于32,则在左边补0至32位。
  • 如果序列化长度大于32,则忽视左侧的部分并缩短至32位处理。

但是,需要注意的是,在EVM中如果地址长度超过20位,运行时将可能并不能得到预期的结果,所以请不要使用有效信息超过20位的地址

下面会详细阐释一下原因。

虽然在以太坊中,栈、内存等都是32位的机器,看起来能够支撑32位以内的地址,但是以下面的代码为例。

function info() public view returns (address, uint) {
    return (msg.sender, balance);
}

其生成的汇编代码并不会老老实实的将传进来的sender地址返回,有可能会通过一个PUSH20指令将地址截断放到栈中并使用(可能是为了gas消耗角度考虑),这样一来地址的有效信息就被截断了,所以会导致信息丢失。

2.3. DB & WriteBatch

底层数据库存储每个账户及其拥有的kv storage。当DB作为交易执行的cache使用时,交易完成后需要把更新及删除的账户状态写入底层数据库,这个过程使用batch执行来加速。

2.3.1. DB
// Exist return if the account exist
// Note: if account is suicided, return true
Exist(address Address) bool
// GetStorage return a default account if unexist
GetAccount(address Address) Account
// Note: GetStorage return nil if key is not exist
GetStorage(address Address, key []byte) (value []byte)
// if db is used as cache, updated and removed account need to be synced to
// database by writeBatch once execution finished
NewWriteBatch() WriteBatch

如果一个账户在交易执行过程中执行了selfdestruct汇编指令,会被标记为suicided,这说明该账户曾经存在过,在底层数据库中有相应的kv存储。由于Exist通常(ethereum里)在创建新账户的时候被调用,由于底层数据库和cache中还没有清除该账户的信息,创建新账户不需要过多操作,可以不用收取多余的gas。

2.3.2. WriteBatch
SetStorage(address Address, key []byte, value []byte)
// Note: db should delete all storages if an account suicide
UpdateAccount(account Account) error
AddLog(log *Log)
2.4. Blockchain
GetBlockHash(num uint64) []byte
// CreateAddress will be called by CREATE Opcode
CreateAddress(caller Address, nonce uint64) Address
// Create2Address will be called by CREATE2 Opcode
Create2Address(caller Address, salt, code []byte) Address
// Note: NewAccount will create a default account in Blockchain service, but please do not append the account into db here
NewAccount(address Address) Account
// BytesToAddress provide a way convert bytes(normally [32]byte) to Address
BytesToAddress(bytes []byte) Address
  • GetBlockHash:返回高度为num的区块哈希,注意num < block height 且 > blockheight - 257。
  • CreateAddress:用户自定义的创建地址函数(对应CREATE指令),如果不想实现可以直接返回nil,EVM执行时会采取与以太坊相同的方式处理。
  • Create2Address:用户自定义的创建地址函数(对应CREATE2指令),如果不想实现可以直接返回nil,EVM执行时会采取与以太坊相同的方式处理。
  • NewAccount:根据一个地址返回默认的账户(请不要在DB里面也插入该账户,需要的时候EVM会调用DB的相关函数去插入)。
  • BytesToAddress:将byte数组(长度一般为32位)解析为用户定义的Address。

Documentation

Index

Constants

View Source
const (
	DefaultStackCapacity    uint64 = 1024
	DefaultMaxStackCapacity uint64 = 32 * 1024
	MaxCodeSize             int    = 24576
)

Here defines some default stack capacity variables

View Source
const (
	SHA3 = 0x20
)

20s: SHA3

Variables

This section is empty.

Functions

func SetDebug

func SetDebug(isDebug bool)

SetDebug set debug and logrus log level

Types

type Account

type Account interface {
	GetAddress() Address
	GetBalance() uint64
	AddBalance(balance uint64) error
	SubBalance(balance uint64) error
	GetCode() []byte
	SetCode(code []byte)
	// GetCodeHash return the hash of account code, please return [32]byte,
	// and return [32]byte{0, ..., 0} if code is empty
	GetCodeHash() []byte
	GetNonce() uint64
	SetNonce(nonce uint64)
	// Suicide will suicide an account
	Suicide()
	HasSuicide() bool

	Copy() Account
}

Account describe what function that account should provide

type Address

type Address interface {
	// It would be better if length = 32
	// 1. Add zero in left if length < 32
	// 2. Remove left byte if length > 32(however, this may be harm)
	Bytes() []byte
}

Address describe what functions that an Address implementation should provide

type Blockchain

type Blockchain interface {
	// GetBlockHash return ZeroWord256 if num > 256 or num > max block height
	GetBlockHash(num uint64) []byte
	// CreateAddress will be called by CREATE Opcode
	CreateAddress(caller Address, nonce uint64) Address
	// Create2Address will be called by CREATE2 Opcode
	Create2Address(caller Address, salt, code []byte) Address
	// Note: NewAccount will create a default account in Blockchain service,
	// but please do not append the account into db here
	NewAccount(address Address) Account
	// BytesToAddress provide a way convert bytes(normally [32]byte) to Address
	BytesToAddress(bytes []byte) Address
}

Blockchain describe what function that blockchain system shoudld provide to support the evm

type Cache

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

Cache cache on a DB. It will simulate operate on a db, and sync to db if necessary. Note: It's not thread safety because now it will only be used in one thread.

func NewCache

func NewCache(db DB) *Cache

NewCache is the constructor of Cache

func (*Cache) AddLog

func (cache *Cache) AddLog(log *Log)

AddLog add log

func (*Cache) Exist

func (cache *Cache) Exist(addr Address) bool

Exist return if an account exist

func (*Cache) GetAccount

func (cache *Cache) GetAccount(addr Address) Account

GetAccount return the account of address

func (*Cache) GetNonce

func (cache *Cache) GetNonce(address Address) uint64

GetNonce return the nonce of account

func (*Cache) GetStorage

func (cache *Cache) GetStorage(address Address, key core.Word256) []byte

GetStorage returns the key of an address if exist, else returns an error

func (*Cache) HasSuicide

func (cache *Cache) HasSuicide(addr Address) bool

HasSuicide return if an account has suicide

func (*Cache) SetStorage

func (cache *Cache) SetStorage(address Address, key core.Word256, value []byte)

SetStorage set the storage of address NOTE: Set value to zero to remove. How should i understand this? TODO: Review this

func (*Cache) Suicide

func (cache *Cache) Suicide(address Address) error

Suicide remove an account

func (*Cache) Sync

func (cache *Cache) Sync()

Sync will sync change to db

func (*Cache) UpdateAccount

func (cache *Cache) UpdateAccount(account Account) error

UpdateAccount set account

type Context

type Context struct {
	Input []byte
	Value uint64
	Gas   *uint64

	BlockHeight uint64
	BlockTime   int64
	Difficulty  uint64
	GasLimit    uint64
	GasPrice    uint64
	CoinBase    []byte
}

Context defines some context

type DB

type DB interface {
	// Exist return if the account exist
	// Note: if account is suicided, return true
	Exist(address Address) bool
	// GetStorage return a default account if unexist
	GetAccount(address Address) Account
	// Note: GetStorage return nil if key is not exist
	GetStorage(address Address, key []byte) (value []byte)
	NewWriteBatch() WriteBatch
}

DB describe what function that db should provide to support the evm

type EVM

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

EVM is the evm

func New

func New(bc Blockchain, db DB, ctx *Context) *EVM

New is the constructor of EVM

func (*EVM) Call

func (evm *EVM) Call(caller, callee Address, code []byte) ([]byte, error)

Call run code on evm, and it will sync change to db if error is nil

func (*EVM) CallWithoutTransfer

func (evm *EVM) CallWithoutTransfer(caller, callee Address, code []byte) (output []byte, err error)

CallWithoutTransfer is call without transfer, and it will sync change to db if error is nil

func (*EVM) Create

func (evm *EVM) Create(caller Address) ([]byte, Address, error)

Create create a contract account, and return an error if there exist a contract on the address

func (*EVM) GetRefund

func (evm *EVM) GetRefund() uint64

GetRefund return the refund

type Log

type Log struct {
	// Consensus field
	Address Address `json:"address"`
	// list of topics provided by the contract.
	Topics []core.Word256 `json:"topics"`
	// supplied by the contract, usually ABI-encoded
	Data []byte `json:"data"`

	// Derived fields, so the database should record the context to support these fields
	BlockNumber uint64 `json:"blockNumber"`
	// hash of the transaction
	TxHash []byte `json:"transactionHash"`
	// index of the transaction in the block
	TxIndex uint `json:"transactionIndex"`
	// hash of the block in which the transaction was included
	BlockHash []byte `json:"blockHash"`
	// index of the log in the block
	Index uint `json:"logIndex"`
}

Log is the log of evm

func (*Log) String

func (l *Log) String() string

String return string of log Note: This should be used only for testing. TODO: A better String or remove it.

type Memory

type Memory interface {
	// Read a value from the memory store starting at offset
	// (index of first byte will equal offset). The value will be returned as a
	// length-bytes byte slice. Returns an error if the memory cannot be read or
	// is not allocated.
	//
	// The value returned should be copy of any underlying memory, not a reference
	// to the underlying store.
	Read(offset, length *big.Int) (value []byte, gasCost uint64)
	// Write a value to the memory starting at offset (the index of the first byte
	// written will equal offset). The value is provided as bytes to be written
	// consecutively to the memory store. Return an error if the memory cannot be
	// written or allocated.
	Write(offset *big.Int, value []byte) (gasCost uint64)
	// Returns the current capacity of the memory. For dynamically allocating
	// memory this capacity can be used as a write offset that is guaranteed to be
	// unused. Solidity in particular makes this assumption when using MSIZE to
	// get the current allocated memory.
	Capacity() *big.Int
	Len() uint64
	CalMemGas(offset, length uint64) (uint64, error)
}

Memory is the interface for a bounded linear memory indexed by a single *big.Int parameter for each byte in the memory.

func DefaultDynamicMemoryProvider

func DefaultDynamicMemoryProvider(errSink errors.Sink) Memory

DefaultDynamicMemoryProvider return to default DynamicMemory

func NewDynamicMemory

func NewDynamicMemory(initialCapacity, maximumCapacity uint64, errSink errors.Sink) Memory

NewDynamicMemory is the constrcutor of DynamicMemory (note that although we take a maximumCapacity of uint64 we currently limit the maximum to int32 at runtime because we are using a single slice which we cannot guarantee to be indexable above int32 or all validators

type OpCode

type OpCode byte

OpCode is the type of operation code

const (
	STOP OpCode = iota
	ADD
	MUL
	SUB
	DIV
	SDIV
	MOD
	SMOD
	ADDMOD
	MULMOD
	EXP
	SIGNEXTEND
)

0s: Stop and Arithmetic Operations

const (
	LT OpCode = iota + 0x10
	GT
	SLT
	SGT
	EQ
	ISZERO
	AND
	OR
	XOR
	NOT
	BYTE
	SHL
	SHR
	SAR
)

10s: Comparison & Bitwise Logic Opreations

const (
	ADDRESS OpCode = 0x30 + iota
	BALANCE
	ORIGIN
	CALLER
	CALLVALUE
	CALLDATALOAD
	CALLDATASIZE
	CALLDATACOPY
	CODESIZE
	CODECOPY
	GASPRICE
	EXTCODESIZE
	EXTCODECOPY
	RETURNDATASIZE
	RETURNDATACOPY
	EXTCODEHASH
)

30s: Environmental Information

const (
	BLOCKHASH OpCode = 0x40 + iota
	COINBASE
	TIMESTAMP
	NUMBER
	DIFFICULTY
	GASLIMIT
	CHAINID
	SELFBALANCE
)

40s: Block Information

const (
	POP OpCode = 0x50 + iota
	MLOAD
	MSTORE
	MSTORE8
	SLOAD
	SSTORE
	JUMP
	JUMPI
	PC
	MSIZE
	GAS
	JUMPDEST
)

50s: Stack, Memory, Storage and Flow Operations

const (
	PUSH1 OpCode = 0x60 + iota
	PUSH2
	PUSH3
	PUSH4
	PUSH5
	PUSH6
	PUSH7
	PUSH8
	PUSH9
	PUSH10
	PUSH11
	PUSH12
	PUSH13
	PUSH14
	PUSH15
	PUSH16
	PUSH17
	PUSH18
	PUSH19
	PUSH20
	PUSH21
	PUSH22
	PUSH23
	PUSH24
	PUSH25
	PUSH26
	PUSH27
	PUSH28
	PUSH29
	PUSH30
	PUSH31
	PUSH32
)

60s & 70s: Push Operations

const (
	DUP1 OpCode = 0x80 + iota
	DUP2
	DUP3
	DUP4
	DUP5
	DUP6
	DUP7
	DUP8
	DUP9
	DUP10
	DUP11
	DUP12
	DUP13
	DUP14
	DUP15
	DUP16
)

80s: Duplication Operations

const (
	SWAP1 OpCode = 0x90 + iota
	SWAP2
	SWAP3
	SWAP4
	SWAP5
	SWAP6
	SWAP7
	SWAP8
	SWAP9
	SWAP10
	SWAP11
	SWAP12
	SWAP13
	SWAP14
	SWAP15
	SWAP16
)

90s: Exchange Operations

const (
	LOG0 OpCode = 0xa0 + iota
	LOG1
	LOG2
	LOG3
	LOG4
)

a0s: Logging Operations

const (
	CREATE OpCode = 0xf0 + iota
	CALL
	CALLCODE
	RETURN
	DELEGATECALL
	CREATE2
	STATICCALL OpCode = 0xfa

	REVERT       OpCode = 0xfd
	INVALID      OpCode = 0xfe
	SELFDESTRUCT OpCode = 0xff
)

f0s: System Operations

func (OpCode) String

func (i OpCode) String() string

type Stack

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

Stack is the stack that support the running of evm Note: The stack is not thread safety

func NewStack

func NewStack(initialCapacity uint64, maxCapacity uint64, gas *uint64, errSink errors.Sink, toAddressFunc func(bytes []byte) Address) *Stack

NewStack is the constructor of Stack

func (*Stack) Dup

func (st *Stack) Dup(n int)

Dup duplicate stack

func (*Stack) Len

func (st *Stack) Len() int

Len return length of stack

func (*Stack) Peek

func (st *Stack) Peek() core.Word256

Peek peek the stack element

func (*Stack) PeekBigInt

func (st *Stack) PeekBigInt() *big.Int

PeekBigInt peek big int from stack

func (*Stack) Pop

func (st *Stack) Pop() core.Word256

Pop pos a core.Word256 from the stak

func (*Stack) PopAddress

func (st *Stack) PopAddress() Address

PopAddress pop address from stack

func (*Stack) PopBigInt

func (st *Stack) PopBigInt() *big.Int

PopBigInt pop big int from stack

func (*Stack) PopBytes

func (st *Stack) PopBytes() []byte

PopBytes pop bytes from stack

func (*Stack) PopUint64

func (st *Stack) PopUint64() uint64

PopUint64 pop uint64 from stack

func (*Stack) Print

func (st *Stack) Print(n int)

Print print stack status

func (*Stack) Push

func (st *Stack) Push(word core.Word256)

Push push core.Word256 into stack

func (*Stack) PushAddress

func (st *Stack) PushAddress(address Address)

PushAddress push address into stack

func (*Stack) PushBigInt

func (st *Stack) PushBigInt(bigInt *big.Int)

PushBigInt push the bigInt as a core.Word256 encoding negative values in 32-byte twos complement and returns the encoded result

func (*Stack) PushBytes

func (st *Stack) PushBytes(bz []byte)

PushBytes push bytes into stack, bytes length would fixed to 32

func (*Stack) PushUint64

func (st *Stack) PushUint64(i uint64)

PushUint64 push uint64 into stack

func (*Stack) Swap

func (st *Stack) Swap(n int)

Swap swap stack

type WriteBatch

type WriteBatch interface {
	SetStorage(address Address, key []byte, value []byte)
	// Note: db should delete all storages if an account suicide
	UpdateAccount(account Account) error
	AddLog(log *Log)
}

WriteBatch define a batch which support some write operations

Directories

Path Synopsis
Package abi implements the Ethereum ABI (Application Binary Interface).
Package abi implements the Ethereum ABI (Application Binary Interface).
blake2b
Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693 and the extendable output function (XOF) BLAKE2Xb.
Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693 and the extendable output function (XOF) BLAKE2Xb.
bn256
Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
bn256/cloudflare
Package bn256 implements a particular bilinear group at the 128-bit security level.
Package bn256 implements a particular bilinear group at the 128-bit security level.
bn256/google
Package bn256 implements a particular bilinear group.
Package bn256 implements a particular bilinear group.
secp256k1
Package secp256k1 wraps the bitcoin secp256k1 C library.
Package secp256k1 wraps the bitcoin secp256k1 C library.
Package rlp implements the RLP serialization format.
Package rlp implements the RLP serialization format.

Jump to

Keyboard shortcuts

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