深入区块链以太坊源码之交易、智能合约和虚拟机

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Grit_ICPC/article/details/83419408

交易的大致流程:

//交易执行的大体流程
// state_processor.go
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
	var (
		receipts types.Receipts //接收交易返回的
		usedGas  = new(uint64)	//交易花费的gas
		header   = block.Header() 
		allLogs  []*types.Log  //日志信息数组
		gp       = new(GasPool).AddGas(block.GasLimit()) //说明当前交易还有多少Gas可以使用
	)

	// Mutate the block and state according to any hard-fork specs
	//判断当前区块是否出现硬分叉
	if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
		misc.ApplyDAOHardFork(statedb)//将硬分叉后合约gas转到系统固定dao账户上
	}
	// Iterate over and process the individual transactions
	//遍历区块上的所有交易
	for i, tx := range block.Transactions() {
		statedb.Prepare(tx.Hash(), block.Hash(), i)
		//执行交易,并且获取receipt对象,receipt中还包含了log数组其记录了Tx中一小步的操作
		receipt, _ , err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
		if err != nil {
			return nil, nil, 0, err
		}
		receipts = append(receipts, receipt)
		allLogs = append(allLogs, receipt.Logs...)
	}
	// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
	p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts)

	return receipts, allLogs, *usedGas, nil
}

// state_processor.go
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) {
	//MakeSigner根据区块配置和区块头编号进行签名,签名过程会根据以太坊预计的四个阶段来分别签名
	//AsMessage主要是封装当前的交易信息,并且在通过内部的Sender函数返回发送者地址,之后该地址通过
	//r,s,v,恢复签名->公钥->地址
	//而对交易进行签名是通过signTx函数,将tx信息进行hash,之后和私钥一起secp256k1.sign(hash, privKey)进行签名
	msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
	if err != nil {
		return nil, 0, err
	}
	//NewEVMContext(msg, header, bc, author)创建EVM的上下文环境
	context := NewEVMContext(msg, header, bc, author)
	
	// Create a new environment which holds all relevant information
	// about the transaction and calling mechanisms.
	//vm.NewEVM(context, statedb, config, cfg)创建EVM对象,并在内部创建一个evm.interpreter(虚拟机解析器)
	vmenv := vm.NewEVM(context, statedb, config, cfg)
	// Apply the transaction to the current state (included in the env)

	//通过EVM对象来执行Message(msg) 其中包括解释器解释执行智能合约
	_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
	if err != nil {
		return nil, 0, err
	}
	// Update the state with pending changes
	var root []byte
	//判断是否分叉情况( `config.IsByzantium(header.Number)` )
	if config.IsByzantium(header.Number) {
		statedb.Finalise(true)
	} else {//如果不是,获取当前的statedb的状态树根哈希
		root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
	}
	*usedGas += gas

	// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
	// based on the eip phase, we're passing whether the root touch-delete accounts.
	//Receipt 中有一个Log类型的数组,其中每一个Log对象记录了Tx中一小步的操作。
	//所以,每一个tx的执行结果,由一个Receipt对象来表示;更详细的内容,由一组Log对象来记录。
	//这个Log数组很重要,比如在不同Ethereum节点(Node)的相互同步过程中,
	//待同步区块的Log数组有助于验证同步中收到的block是否正确和完整,所以会被单独同步(传输)。

	receipt := types.NewReceipt(root, failed, *usedGas)
	receipt.TxHash = tx.Hash()
	receipt.GasUsed = gas
	// if the transaction created a contract, store the creation address in the receipt.
	if msg.To() == nil {
		receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
	}
	// Set the receipt logs and create a bloom for filtering
	receipt.Logs = statedb.GetLogs(tx.Hash())
	receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

	return receipt, gas, err
}



type StateProcessor struct {
	config *params.ChainConfig // Chain configuration options
	bc     *BlockChain         // Canonical block chain
	engine consensus.Engine    // Consensus engine used for block rewards
}

type ChainConfig struct {
	ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection
	// 链id标识了当前链,主键唯一id,也用于replay protection重发保护(用来防止replay attack重发攻击:恶意重复或拖延正确数据传输的一种网络攻击手段)
	//所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。为了抵御重放攻击,现在的身份认证一般采用挑战因答方式。

	HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead)
	//代表以太坊版本
	DAOForkBlock   *big.Int `json:"daoForkBlock,omitempty"`   // TheDAO hard-fork switch block (nil = no fork)
	//TheDAO硬分叉切换
	DAOForkSupport bool     `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork
	//结点是否支持或者反对DAO硬分叉

	// EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
	EIP150Block *big.Int    `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork)
	EIP150Hash  common.Hash `json:"eip150Hash,omitempty"`  // EIP150 HF hash (needed for header only clients as only gas pricing changed)

	EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block
	EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block

	ByzantiumBlock      *big.Int `json:"byzantiumBlock,omitempty"`      // Byzantium switch block (nil = no fork, 0 = already on byzantium)
	ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
	EWASMBlock          *big.Int `json:"ewasmBlock,omitempty"`          // EWASM switch block (nil = no fork, 0 = already activated)

	// Various consensus engines
	Ethash *EthashConfig `json:"ethash,omitempty"`
	Clique *CliqueConfig `json:"clique,omitempty"`
}

//通过反射获取变量类型后面的json值


package main
import (
	"fmt"
	"math/big"
	"reflect"
)
type Process struct {
	chainId *big.Int `json:"chainId" num:"2"`
}

func reflectContent(p *Process) map[string]string {
	t := reflect.TypeOf(p).Elem()
	//i := reflect.Type(p)
	cont := make(map[string]string)
	for i:= 0 ; i < t.NumField() ; i++ {
		cont[t.Field(i).Tag.Get("json")] = t.Field(i).Tag.Get("num")
	}
	return cont
}
func main(){
	p := &Process{big.NewInt(111)}
	cont := reflectContent(p)
	fmt.Printf("chainId is %s\n",cont["chainId"])
}

深入EVM创建合约和执行的过程
 

/*智能合约执行流程
合约创建流程具体内容
创建合约编写智能合约
编译智能合约代码编译成字节码
部署把字节码部署到区块链网络中*/

代码结构
.
├── analysis.go            //跳转目标判定
├── common.go
├── contract.go            //合约数据结构
├── contracts.go           //预编译好的合约
├── errors.go
├── evm.go                 //执行器 对外提供一些外部接口   
├── gas.go                 //call gas花费计算 一级指令耗费gas级别
├── gas_table.go           //指令耗费计算函数表
├── gen_structlog.go       
├── instructions.go        //指令操作
├── interface.go           
├── interpreter.go         //解释器 调用核心
├── intpool.go             //int值池
├── int_pool_verifier_empty.go
├── int_pool_verifier.go
├── jump_table.go           //指令和指令操作(操作,花费,验证)对应表
├── logger.go               //状态日志
├── memory.go               //EVM 内存
├── memory_table.go         //EVM 内存操作表 主要衡量操作所需内存大小
├── noop.go
├── opcodes.go              //Op指令 以及一些对应关系     
├── runtime
│   ├── env.go              //执行环境 
│   ├── fuzz.go
│   └── runtime.go          //运行接口 测试使用
├── stack.go                //栈
└── stack_table.go          //栈验证


func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) {
	return NewStateTransition(evm, msg, gp).TransitionDb()
}

type StateTransition struct {
	gp         *GasPool
	msg        Message
	gas        uint64
	gasPrice   *big.Int
	initialGas uint64
	value      *big.Int
	data       []byte
	state      vm.StateDB
	evm        *vm.EVM
}



// TransitionDb will transition the state by applying the current message and
// returning the result including the used gas. It returns an error if failed.
// An error indicates a consensus issue.
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
	//验证nonce不能比当前小,目的是为了双花。每一个账户从同一个节点发起交易时,
	//这个nonce值从0开始计数,发送一笔nonce对应加1。当前面的nonce处理完成之后
	//才会处理后面的nonce。注意这里的前提条件是相同的地址在相同的节点发送交易。
	//如果传入的nonce值过大,在进入txpool中检查到它之前的nonce并没有使用过,
	//那么此笔交易不会发送到pending中,而且放置在queued中
	if err = st.preCheck(); err != nil {
		return
	}
	msg := st.msg
	sender := vm.AccountRef(msg.From())
	homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
	contractCreation := msg.To() == nil

	// Pay intrinsic gas
	//来计算此次交易需消耗的gas
	gas, err := IntrinsicGas(st.data, contractCreation, homestead)
	if err != nil {
		return nil, 0, false, err
	}
	if err = st.useGas(gas); err != nil {
		return nil, 0, false, err
	}

	var (
		evm = st.evm
		// vm errors do not effect consensus and are therefor
		// not assigned to err, except for insufficient balance
		// error.
		vmerr error
	)
	//如果交易的(转帐)转入方地址(tx.data.Recipient)为空,调用EVM的Create()函数;否则,调用Call()函数
	//合约创建交易,调用evm.Create(sender, st.data, st.gas, st.value)来执行message
	if contractCreation {
		ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
	} else {
		// Increment the nonce for the next transaction
		st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
		ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
	}
	if vmerr != nil {
		log.Debug("VM returned with error", "err", vmerr)
		// The only possible consensus-error would be if there wasn't
		// sufficient balance to make the transfer happen. The first
		// balance transfer may never fail.
		if vmerr == vm.ErrInsufficientBalance {
			return nil, 0, false, vmerr
		}
	}
	st.refundGas()
	st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))

	return ret, st.gasUsed(), vmerr != nil, err
}


// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
	//通过转出方地址和其Nonce值生成合约账户地址
	contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
	return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
}

// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
	// Depth check execution. Fail if we're trying to execute above the
	// limit.
	//不可以超过evm栈深度的预设值
	if evm.depth > int(params.CallCreateDepth) {
		return nil, common.Address{}, gas, ErrDepth
	}
	//检查发出账户是否有足够的钱
	if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
		return nil, common.Address{}, gas, ErrInsufficientBalance
	}
	nonce := evm.StateDB.GetNonce(caller.Address())
	evm.StateDB.SetNonce(caller.Address(), nonce+1)

	// Ensure there's no existing contract already at the designated address
	contractHash := evm.StateDB.GetCodeHash(address)
	if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
		return nil, common.Address{}, 0, ErrContractAddressCollision
	}
	// Create a new account on the state
	//保存当前状态,如果出错,就回滚到这个状态,nextRevisionId主要记录版本号,而具体内容在journal中
	snapshot := evm.StateDB.Snapshot()
	//CreateAccount里面主要是下面的createObject方法
	evm.StateDB.CreateAccount(address)
	if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
		evm.StateDB.SetNonce(address, 1)
	}
	//转账功能由Transfer()函数实现,在Context初始化时赋值即可。
	evm.Transfer(evm.StateDB, caller.Address(), address, value)

	// initialise a new contract and set the code that is to be used by the
	// EVM. The contract is a scoped environment for this execution context
	// only.
	contract := NewContract(caller, AccountRef(address), value, gas)
	//设置合约hash
	contract.SetCodeOptionalHash(&address, codeAndHash)

	if evm.vmConfig.NoRecursion && evm.depth > 0 {
		return nil, address, gas, nil
	}

	if evm.vmConfig.Debug && evm.depth == 0 {
		evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
	}
	start := time.Now()
	
	//运行合约
	/*如果待执行的Contract对象恰好属于一组预编译的合约集合-此时以指令地址CodeAddr为匹配项-那么
	它可以直接运行;没有经过预编译的Contract,才会由Interpreter解释执行。这里的"预编译",
	可理解为不需要编译(解释)指令(Code)*/
	ret, err := run(evm, contract, nil, false)

	// check whether the max code size has been exceeded
	maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
	// if the contract creation ran successfully and no errors were returned
	// calculate the gas required to store the code. If the code could not
	// be stored due to not enough gas set an error and let it be handled
	// by the error checking condition below.
	if err == nil && !maxCodeSizeExceeded {
		createDataGas := uint64(len(ret)) * params.CreateDataGas
		if contract.UseGas(createDataGas) {
			evm.StateDB.SetCode(address, ret)
		} else {
			err = ErrCodeStoreOutOfGas
		}
	}

	// When an error was returned by the EVM or when setting the creation code
	// above we revert to the snapshot and consume any gas remaining. Additionally
	// when we're in homestead this also counts for code storage gas errors.
	if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
		evm.StateDB.RevertToSnapshot(snapshot)
		if err != errExecutionReverted {
			contract.UseGas(contract.Gas)
		}
	}
	// Assign err if contract code size exceeds the max while the err is still empty.
	if maxCodeSizeExceeded && err == nil {
		err = errMaxCodeSizeExceeded
	}
	if evm.vmConfig.Debug && evm.depth == 0 {
		evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
	}
	return ret, address, contract.Gas, err

}

//在新创建合约账号的时候,会将前一个版本留下来
func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
	prev = self.getStateObject(addr)
	newobj = newObject(self, addr, Account{})
	newobj.setNonce(0) // sets the object to dirty
	if prev == nil {
		self.journal.append(createObjectChange{account: &addr})
	} else {
		self.journal.append(resetObjectChange{prev: prev})
	}
	self.setStateObject(newobj)
	return newobj, prev
}

/*1.若已经有预编译:Ethereuem 代码中已经加入了多个预编译合约,功能覆盖了包括椭圆曲线密钥恢复,
SHA-3(256bits)哈希算法,RIPEMD-160加密算法等等。相信基于自身业务的需求,
二次开发者完全可以加入自己的预编译合约*/
/*2.没有预编译则通过解释器Interpreter进行编译解释*/
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
	if contract.CodeAddr != nil {
		precompiles := PrecompiledContractsHomestead
		if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
			precompiles = PrecompiledContractsByzantium
		}
		if p := precompiles[*contract.CodeAddr]; p != nil {
			return RunPrecompiledContract(p, input, contract)
		}
	}
	for _, interpreter := range evm.interpreters {
		if interpreter.CanRun(contract.Code) {
			if evm.interpreter != interpreter {
				// Ensure that the interpreter pointer is set back
				// to its current value upon return.
				defer func(i Interpreter) {
					evm.interpreter = i
				}(evm.interpreter)
				evm.interpreter = interpreter
			}
			return interpreter.Run(contract, input, readOnly)
		}
	}
	return nil, ErrNoCompatibleInterpreter
}

猜你喜欢

转载自blog.csdn.net/Grit_ICPC/article/details/83419408
今日推荐