解析Solidity预编译合约的实现

更新时间:2023-07-28 06:44:55 阅读: 评论:0

解析Solidity预编译合约的实现
在Solidity中存在很多预先编译好的合约(或者说是⽅法)可供调⽤,例如sha256、keccak256等,本⽂简单分析下其实现逻辑。
例如有如下测试合约,在测试合约内调⽤sha256:
pragma solidity ^0.4.24;
contract Sha256Test {
uint256 time = 123;
event hashResult(bytes32);
function calcSha256(string input) public {
bytes32 id = sha256(input, time);
emit hashResult(id);
}
}
此合约源码需要经过solidity编译器编译,编译器解析到sha256关键字就会插⼊⼀段合约调⽤的逻辑,编译器源码如下:
⽂件:
ca FunctionType::Kind::ECRecover:
ca FunctionType::Kind::SHA256:
白金婚戒
ca FunctionType::Kind::RIPEMD160:
{
厦门馅饼_pression().accept(*this);
static map<FunctionType::Kind, u256> const contractAddress{
{FunctionType::Kind::ECRecover, 1},
{FunctionType::Kind::SHA256, 2},
{FunctionType::Kind::RIPEMD160, 3}
};
m_context << contractAddress.at(function.kind());
for (unsigned i = function.sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
appendExternalFunctionCall(function, arguments);
break;
}
所以,当运⾏到此处时,对于sha256会调⽤地址是2的合约的sha256⽅法。那么在地址是2的地⽅的合约是什么时候部署进以太坊⽹络的呢?
我们知道,通常智能合约的开发流程是⽤solidlity编写逻辑代码,再通过编译器编译元数据,最后再发布到以太坊上。以太坊底层通过EVM 模块⽀持合约的执⾏与调⽤,调⽤时根据合约地址获取到代码,
⽣成环境后载⼊到EVM中运⾏。
执⾏⼊⼝定义在中,功能就是组装执⾏环境(代码,执⾏⼈关系,参数等)。
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司法救助
}
// 合约调⽤深度检查
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// 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) {
//创建调⽤对象的stateObject
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
precompiles = PrecompiledContractsByzantium
英文读
}
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
return nil, gas, nil
}
evm.StateDB.CreateAccount(addr)
}
//调⽤别⼈合约可能需要花钱
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
婴儿奶粉排行榜10强//创建合约环境
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
start := time.Now()
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, fal, input, gas, value)
defer func() { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}()
}
//执⾏操作
ret, err = run(evm, contract, input)
// When an error was returned by the EVM or when tting 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 {
西藏梗contract.UGas(contract.Gas)
}
}
return ret, contract.Gas, err
}
嗓子痒老是咳嗽有白痰
类似的函数有四个。
Call A->B A,B的环境独⽴
CallCode、 和Call类似 区别在于storage位置不⼀样
增大音量DelegateCall、 和CallCode类似,区别在于msg.nd不⼀样
StaticCall 和call相似 只是不能修改状态
Contract和参数构造完成后调⽤执⾏函数,执⾏函数会检查调⽤的是否会之前编译好的原⽣合约,如果是原⽣合约则调⽤原⽣合约,否则调⽤解释器执⾏函数运算合约。
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte) ([]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)
}
}
return evm.interpreter.Run(contract, input)
}
这⾥所说的原⽣合约就是指native Go写的预编译的合约,在go-ethereum中有定义,如下:
// PrecompiledContractsByzantium contains the default t of pre-compiled Ethereum
/
/ contracts ud in the Byzantium relea.
var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256Add{},
common.BytesToAddress([]byte{7}): &bn256ScalarMul{},
common.BytesToAddress([]byte{8}): &bn256Pairing{},
}
这⾥我们看到了地址是2的sha256,Go的实现如下:
// SHA256 implemented as a native contract.
type sha256hash struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
//
// This method does not require any overflow checking as the input size gas costs
// required for anything significant is so high it's impossible to pay for.
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaGas
}
func (c *sha256hash) Run(input []byte) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
可以看到实际就是使⽤的Go的sha256实现的。
参考:

本文发布于:2023-07-28 06:44:55,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1121328.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:IC基础知识
标签:合约   编译   编译器   状态
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图