以太坊 EVM内交易执行分析(一)

版权声明:1、本BLOG的目的、形式及内容。   此BLOG为个人维护BLOG,内容均来自原创及互连网转载。最终目的为收集整理自己需要的文章技术等内容,不涉及商业用途。 2、有关原创文章的版权   本BLOG上原创文章未经本人许可,不得用于商业用途及传统媒体。网络媒体转载请注明出处,否则属于侵权行为。 3、有关本站侵权   本BLOG所转载的内容,均是本人未发现有对文章版权声明的文章且无来自传统媒体。如 https://blog.csdn.net/xq723310/article/details/82915664

     以太坊上交易最终都会由EVM进行解析存入数据库,今天就来探讨一下,一笔交易是如何别EVM执行的。我们可以把交易分为三种。(注意,和交易相关的模块很多,交易的生命周期存在于整个以太坊中,我们这次只是分析和EVM相关的部分。)

     1、以太币转移,两个账户之间只发生了以太币的转移。

     2、合约创建,用户创建智能合约的交易。这类交易的to地址都是空着的。

     3、调用合约,用户去调用智能合约中的某个函数,这类交易中data信息包含了需要调用的函数hash值的前4个字节,以及参数。例如基于ERC20的Token转账就是这种类型,前四个字节是0xa9059cbb表示transfer(address _to, uint256 _value),后边跟了两个参数,to地址和交易数量。

     最好结合代码理解一下。

https://github.com/ethereum/go-ethereum/blob/461291882edce0ac4a28f64c4e8725b7f57cbeae/core/vm/evm.go

    以太币转移

    先看一下以太币转移类型的交易,这类交易比较简单,主要函数是由context中的两个函数完成的,CanTransfer CanTransferFunc。

type Context struct {
	// 账户余额够不够
	CanTransfer CanTransferFunc
	// 把eth转移给另外一个账户
	Transfer TransferFunc
	// 对入参n返回一个hash
	GetHash GetHashFunc

	// Message information
	Origin   common.Address // Provides information for ORIGIN
	GasPrice *big.Int       // Provides information for GASPRICE

	// Block information
	Coinbase    common.Address // Provides information for COINBASE
	GasLimit    uint64         // Provides information for GASLIMIT
	BlockNumber *big.Int       // Provides information for NUMBER
	Time        *big.Int       // Provides information for TIME
	Difficulty  *big.Int       // Provides information for DIFFICULTY
}

下边具体分析一下怎么实现的。


func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
	return db.GetBalance(addr).Cmp(amount) >= 0
}

func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
	db.SubBalance(sender, amount)
	db.AddBalance(recipient, amount)
}

    怎么样是不是so easy,一眼就看出来了;就是调用数据接口,实现数据库的数据的修改就可以了,对账户的余额进行增加、减少和查询;当然实际情况并非这么简单,之所以看着这么简单,就是我们常说的解耦;后续我会继续分析state和数据库等模块,那会儿我们就会有更清晰的认识。

扫描二维码关注公众号,回复: 3448812 查看本文章

合约创建\调用合约

合约创建,evm.Create的逻辑和Call方法比较类似的。

evm.call evm.create
检查栈的深度,刚开始的执行深度为0;超过params.CallCreateDepth(即1024)则返回错误;检查账户余额是否超过要转账的数额。

检查栈的深度是否超过params.CallCreateDepth(即1024);检查账户余额是否超过要转账的数额。

保存statedb快照;如果to账户不存在statedb中,则执行precompiles预编译,与编译结果为nil时出错返回;无错误则在statedb中创建to账户;

创建者的nonce值加1;创建合约地址并获取hash;保存statedb快照,然后根据合约地址创建账户;

转账Transfer,这一步就是以太币转账的过程。 转账Transfer,这一步就是以太币转账的过程。
根据from、to、value、gas创建并初始化合约;获取to地址的code,调用SetCallCode方法,设置合约地址关联的code; 根据from、to、value、gas创建并初始化合约;获取入参code,调用SetCallCode方法,设置合约地址关联的code;
debug模式下,栈深度为0,记录evm执行交易的信息。 检查栈的深度是否超过params.CallCreateDepth(即1024);debug模式下,栈深度为0,开始记录evm执行交易的信息。
运行之前创建的合约 运行之前创建的合约
出现任何错误,statedb回滚到之前快照,消耗剩余gas。 检测run结果以及合约大小是否超出限制(out of gas ),出现问题,statedb回滚到之前的快照。
  debug模式下,结束记录evm执行交易的信息。

call和create几点不同:

1)由于智能合约的创建是没有to地址的,所以SetCallCode方法的参数是从直接有用户传进去的的;而call则不一样,例如你转账erc20代币的时候,to地址是合约地址,所以SetCallCode方法的入参则是从to地址的关联的code中获取的。

2)智能合约创建是一个从无到有的过程,所以statedb中没有合约的地址和账户,所以创建合约地址和账户是必然的过程。

3)call()的input类型为[]byte,和create()的code类型同样为[]byte;这两者从交易的角度是一样的;但是当系统解析到to地址时,才有了分化;to地址不为空,则tx.data被当作contract的input,to地址为空的时候,则tx.data被当作contract的code。

     还有后续继续分析一下,交易是如何被解释器具体分析执行的。

猜你喜欢

转载自blog.csdn.net/xq723310/article/details/82915664
今日推荐