以太坊 EVM内交易执行分析(一)
以太坊上交易最终都会由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和数据库等模块,那会儿我们就会有更清晰的认识。
合约创建\调用合约
合约创建,evm.Create的逻辑和Call方法比较类似的。
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。
还有后续继续分析一下,交易是如何被解释器具体分析执行的。