兄弟连区块链入门教程eth源码分析core-vm源码分析(二)

栏目: IOS · 发布时间: 6年前

内容简介:区块链入门教程eth源码分析core-vm源码分析(二),合约创建 Create 会创建一个新的合约。// Create creates a new contract using code as deployment code.func (evm

区块链入门教程eth源码分析core-vm源码分析(二),合约创建 Create 会创建一个新的合约。

// 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) {

// Depth check execution. Fail if we're trying to execute above the
// limit.
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
}
// Ensure there's no existing contract already at the designated address
// 确保特定的地址没有合约存在
nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1)
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
contractHash := evm.StateDB.GetCodeHash(contractAddr)
if evm.StateDB.GetNonce(contractAddr) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { //如果已经存在
    return nil, common.Address{}, 0, ErrContractAddressCollision
}
// Create a new account on the state
snapshot := evm.StateDB.Snapshot() //创建一个StateDB的快照,以便回滚
evm.StateDB.CreateAccount(contractAddr) //创建账户
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
    evm.StateDB.SetNonce(contractAddr, 1) //设置nonce
}
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value) //转账
// initialise a new contract and set the code that is to be used by the
// E The contract is a scoped evmironment for this execution context
// only.
contract := NewContract(caller, AccountRef(contractAddr), value, gas)
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
if evm.vmConfig.NoRecursion && evm.depth > 0 {
    return nil, contractAddr, gas, nil
}
ret, err = run(evm, snapshot, contract, nil) //执行合约的初始化代码
// 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.
//如果合同创建成功并且没有错误返回,则计算存储代码所需的GAS。 如果由于没有足够的GAS而导致代码不能被存储设置错误,并通过下面的错误检查条件来处理。
if err == nil && !maxCodeSizeExceeded {
    createDataGas := uint64(len(ret)) * params.CreateDataGas
    if contract.UseGas(createDataGas) {
        evm.StateDB.SetCode(contractAddr, 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
}
return ret, contractAddr, contract.Gas, err

}

Call方法, 无论我们转账或者是执行合约代码都会调用到这里, 同时合约里面的call指令也会执行到这里。

// Call executes the contract associated with the addr with the given input as

// parameters. It also handles any necessary value transfer required and takes

// the necessary steps to create accounts and reverses the state in case of an

// execution error or failed value transfer.

// Call 执行与给定的input作为参数与addr相关联的合约。

// 它还处理所需的任何必要的转账操作,并采取必要的步骤来创建帐户

// 并在任意错误的情况下回滚所做的操作。

func (evm EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value big.Int) (ret []byte, leftOverGas uint64, err error) {

if evm.vmConfig.NoRecursion && evm.depth > 0 {
    return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
// 调用深度最多1024
if evm.depth > int(params.CallCreateDepth) {
    return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
// 查看我们的账户是否有足够的金钱。
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
    return nil, gas, ErrInsufficientBalance
}
var (
    to = AccountRef(addr)
    snapshot = evm.StateDB.Snapshot()
)
if !evm.StateDB.Exist(addr) { // 查看指定地址是否存在
    // 如果地址不存在,查看是否是 native go的合约, native go的合约在
    // contracts.go 文件里面
    precompiles := PrecompiledContractsHomestead
    if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
        precompiles = PrecompiledContractsByzantium
    }
    if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
        // 如果不是指定的合约地址, 并且value的值为0那么返回正常,而且这次调用没有消耗Gas
        return nil, gas, nil
    }
    // 负责在本地状态创建addr
    evm.StateDB.CreateAccount(addr)
}
// 执行转账
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
// initialise a new contract and set the code that is to be used by the
// E The contract is a scoped environment for this execution context
// only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, snapshot, contract, input)
// 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 err != nil {
    evm.StateDB.RevertToSnapshot(snapshot)
    if err != errExecutionReverted {
        // 如果是由revert指令触发的错误,因为ICO一般设置了人数限制或者资金限制
        // 在大家抢购的时候很可能会触发这些限制条件,导致被抽走不少钱。这个时候
        // 又不能设置比较低的GasPrice和GasLimit。因为要速度快。
        // 那么不会使用剩下的全部Gas,而是只会使用代码执行的Gas
        // 不然会被抽走 GasLimit *GasPrice的钱,那可不少。
        contract.UseGas(contract.Gas)
    }
}
return ret, contract.Gas, err

}

剩下的三个函数 CallCode, DelegateCall, 和 StaticCall,这三个函数不能由外部调用,只能由Opcode触发。

CallCode

// CallCode differs from Call in the sense that it executes the given address'

// code with the caller as context.

// CallCode与Call不同的地方在于它使用caller的context来执行给定地址的代码。

func (evm EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value big.Int) (ret []byte, leftOverGas uint64, err error) {

if evm.vmConfig.NoRecursion && evm.depth > 0 {
    return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
    return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
    return nil, gas, ErrInsufficientBalance
}
var (
    snapshot = evm.StateDB.Snapshot()
    to = AccountRef(caller.Address()) //这里是最不同的地方 to的地址被修改为caller的地址了 而且没有转账的行为
)
// initialise a new contract and set the code that is to be used by the
// E The contract is a scoped evmironment for this execution context
// only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, snapshot, contract, input)
if err != nil {
    evm.StateDB.RevertToSnapshot(snapshot)
    if err != errExecutionReverted {
        contract.UseGas(contract.Gas)
    }
}
return ret, contract.Gas, err

}

DelegateCall

// DelegateCall differs from CallCode in the sense that it executes the given address'

// code with the caller as context and the caller is set to the caller of the caller.

// DelegateCall 和 CallCode不同的地方在于 caller被设置为 caller的caller

func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {

if evm.vmConfig.NoRecursion && evm.depth > 0 {
    return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
    return nil, gas, ErrDepth
}
var (
    snapshot = evm.StateDB.Snapshot()
    to = AccountRef(caller.Address())
)
// Initialise a new contract and make initialise the delegate values
// 标识为AsDelete()
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, snapshot, contract, input)
if err != nil {
    evm.StateDB.RevertToSnapshot(snapshot)
    if err != errExecutionReverted {
        contract.UseGas(contract.Gas)
    }
}
return ret, contract.Gas, err

}

// StaticCall executes the contract associated with the addr with the given input

// as parameters while disallowing any modifications to the state during the call.

// Opcodes that attempt to perform such modifications will result in exceptions

// instead of performing the modifications.

// StaticCall不允许执行任何修改状态的操作,

func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {

if evm.vmConfig.NoRecursion && evm.depth > 0 {
    return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
    return nil, gas, ErrDepth
}
// Make sure the readonly is only set if we aren't in readonly yet
// this makes also sure that the readonly flag isn't removed for
// child calls.
if !evm.interpreter.readOnly {
    evm.interpreter.readOnly = true
    defer func() { evm.interpreter.readOnly = false }()
}
var (
    to = AccountRef(addr)
    snapshot = evm.StateDB.Snapshot()
)
// 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, to, new(big.Int), gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
// 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.
ret, err = run(evm, snapshot, contract, input)
if err != nil {
    evm.StateDB.RevertToSnapshot(snapshot)
    if err != errExecutionReverted {
        contract.UseGas(contract.Gas)
    }
}
return ret, contract.Gas, err

}


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Development Recipes

Web Development Recipes

Brian P. Hogan、Chris Warren、Mike Weber、Chris Johnson、Aaron Godin / Pragmatic Bookshelf / 2012-1-22 / USD 35.00

You'll see a full spectrum of cutting-edge web development techniques, from UI and eye candy recipes to solutions for data analysis, testing, and web hosting. Make buttons and content stand out with s......一起来看看 《Web Development Recipes》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具