EVM 控制流程,区块,Hash, 账户,交易, Log 与 Gas 相关的指令集说明
在以太坊虚拟机(EVM)中,控制流程指令用于管理程序的执行流程,包括函数调用、返回、条件分支等操作。主要的控制流程指令包括 JUMP、JUMPI、PC、JUMPDEST、CALL、CALLCODE、DELEGATECALL、STATICCALL、RETURN、REVERT、SELFDESTRUCT 等。以下是这些指令的详细介绍:
操作码: 0x56
功能: 无条件跳转到指定的程序计数器(PC)位置。
Gas 消耗: 8 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract JUMPExample {
function jump(uint256 destination) public pure returns (uint256) {
assembly {
jump(destination)
destination := 42 // This line will not be reached
jumpdest
}
return destination;
}
}
操作码: 0x57
功能: 根据条件跳转到指定的程序计数器(PC)位置。
Gas 消耗: 10 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract JUMPIExample {
function conditionalJump(uint256 destination, bool condition) public pure returns (uint256) {
uint256 result = 0;
assembly {
let cond := condition
jumpi(destination, cond)
result := 42 // This line will be reached if condition is false
jumpdest
result := 1 // This line will be reached if condition is true
}
return result;
}
}
操作码: 0x58
功能: 返回当前的程序计数器(PC)位置,并将其推送到堆栈顶端。
Gas 消耗: 2 gas
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract PCExample {
function getCurrentPC() public pure returns (uint256) {
uint256 pc;
assembly {
pc := pc()
}
return pc;
}
}
操作码: 0x5B
功能: 标记一个有效的跳转目标位置。
Gas 消耗: 1 gas
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract JUMPDESTExample {
function markJumpDest() public pure returns (uint256) {
uint256 result = 0;
assembly {
jumpdest
result := 42
}
return result;
}
}
操作码: 0xF1
功能: 调用另一个合约的方法,并传递以太币。
Gas 消耗: 700 gas 基础费用 + 调用费用
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CALLExample {
function callAnotherContract(address target, bytes memory data) public returns (bytes memory) {
(bool success, bytes memory result) = target.call(data);
require(success, "Call failed");
return result;
}
}
操作码: 0xFA
功能: 调用另一个合约的方法,不允许修改状态。
Gas 消耗: 700 gas 基础费用 + 调用费用
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract STATICCALLExample {
function staticCallAnotherContract(address target, bytes memory data) public view returns (bytes memory) {
(bool success, bytes memory result) = target.staticcall(data);
require(success, "Static call failed");
return result;
}
}
操作码: 0xF3
功能: 结束当前执行,并返回指定的输出数据。
Gas 消耗: 0 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract RETURNExample {
function returnData() public pure returns (bytes memory) {
bytes memory data = new bytes(32);
assembly {
mstore(add(data, 32), 42)
return(add(data, 32), 32)
}
}
}
操作码: 0xFD
功能: 终止执行,并回退所有状态更改。
Gas 消耗: 0 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract REVERTExample {
function revertWithData() public pure {
bytes memory data = new bytes(32);
assembly {
mstore(add(data, 32), 42)
revert(add(data, 32), 32)
}
}
}
操作码: 0xFF
功能: 销毁当前合约,并将其剩余的以太币发送到指定地址。
Gas 消耗: 0 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SELFDESTRUCTExample {
function destroy(address payable beneficiary) public {
selfdestruct(beneficiary);
}
}
在以太坊虚拟机(EVM)中,区块相关的指令用于获取区块链状态信息,这些信息在智能合约的执行过程中可能是必需的。主要的区块相关指令包括 BLOCKHASH、COINBASE、TIMESTAMP、NUMBER、DIFFICULTY、GASLIMIT、CHAINID 和 BASEFEE。以下是这些指令的详细介绍:
操作码: 0x40
功能: 获取指定区块号的区块哈希。
Gas 消耗: 20 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BlockHashExample {
function getBlockHash(uint256 blockNumber) public view returns (bytes32) {
bytes32 blockHash;
assembly {
blockHash := blockhash(blockNumber)
}
return blockHash;
}
}
操作码: 0x41
功能: 获取当前区块的矿工地址(coinbase)。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CoinbaseExample {
function getCoinbase() public view returns (address) {
address coinbase;
assembly {
coinbase := coinbase()
}
return coinbase;
}
}
操作码: 0x42
功能: 获取当前区块的时间戳。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TimestampExample {
function getTimestamp() public view returns (uint256) {
uint256 timestamp;
assembly {
timestamp := timestamp()
}
return timestamp;
}
}
操作码: 0x43
功能: 获取当前区块的区块号。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BlockNumberExample {
function getBlockNumber() public view returns (uint256) {
uint256 blockNumber;
assembly {
blockNumber := number()
}
return blockNumber;
}
}
操作码: 0x44
功能: 获取当前区块的难度。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DifficultyExample {
function getDifficulty() public view returns (uint256) {
uint256 difficulty;
assembly {
difficulty := difficulty()
}
return difficulty;
}
}
操作码: 0x45
功能: 获取当前区块的气体限制。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasLimitExample {
function getGasLimit() public view returns (uint256) {
uint256 gasLimit;
assembly {
gasLimit := gaslimit()
}
return gasLimit;
}
}
操作码: 0x46
功能: 获取当前链的链ID。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ChainIDExample {
function getChainID() public view returns (uint256) {
uint256 chainID;
assembly {
chainID := chainid()
}
return chainID;
}
}
操作码: 0x48
功能: 获取当前区块的基础费用。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BaseFeeExample {
function getBaseFee() public view returns (uint256) {
uint256 baseFee;
assembly {
baseFee := basefee()
}
return baseFee;
}
}
在以太坊虚拟机(EVM)中,Hash 指令主要用于计算各种哈希值。这些指令在智能合约中非常重要,因为它们可以用于数据的验证和保护。主要的 Hash 指令包括 SHA3。以下是这些指令的详细介绍:
操作码: 0x20
功能: 计算给定内存区域的 Keccak-256 哈希值。
Gas 消耗: 30 gas 基础费用 + 每个字节 6 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SHA3Example {
function computeHash(bytes memory data) public pure returns (bytes32) {
bytes32 hash;
assembly {
// Load the start position of the data in memory
let dataPtr := add(data, 32)
// Load the length of the data
let dataLen := mload(data)
// Compute the hash using SHA3
hash := keccak256(dataPtr, dataLen)
}
return hash;
}
}
示例解释
Keccak-256 是一种加密哈希函数,它是 SHA-3 的一种变体。EVM 使用 Keccak-256 而不是标准的 SHA-3。计算哈希时,EVM 需要消耗一定的 Gas 费用,费用与数据的大小成正比。
用途:
操作步骤示例:
完整的 Solidity 合约示例
以下是一个完整的 Solidity 合约示例,演示了如何使用 SHA3 指令计算哈希值:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract HashExample {
// 计算字符串的 Keccak-256 哈希值
function hashString(string memory input) public pure returns (bytes32) {
bytes32 result;
assembly {
// 加载字符串数据的起始位置
let inputPtr := add(input, 32)
// 加载字符串的长度
let inputLen := mload(input)
// 使用 keccak256 计算哈希
result := keccak256(inputPtr, inputLen)
}
return result;
}
// 计算两个整数的 Keccak-256 哈希值
function hashIntegers(uint256 a, uint256 b) public pure returns (bytes32) {
bytes32 result;
assembly {
// 分配 64 字节的内存区域
let memPtr := mload(0x40)
// 将整数 a 和 b 分别存储在内存中
mstore(memPtr, a)
mstore(add(memPtr, 32), b)
// 使用 keccak256 计算哈希
result := keccak256(memPtr, 64)
}
return result;
}
}
在这个示例中,我们定义了两个函数:
通过这些函数,可以看到如何在 Solidity 合约中使用 SHA3 指令来计算哈希值,并理解其在智能合约中的应用。
在以太坊虚拟机(EVM)中,账户指令用于与账户相关的信息和操作。这些指令主要涉及获取账户的余额、代码、代码大小、代码哈希、地址等。主要的账户指令包括 BALANCE、EXTCODESIZE、EXTCODECOPY、EXTCODEHASH 和 SELFDESTRUCT。以下是这些指令的详细介绍:
操作码: 0x31
功能: 获取指定账户的余额。
Gas 消耗: 700 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BalanceExample {
function getBalance(address account) public view returns (uint256) {
uint256 balance;
assembly {
balance := balance(account)
}
return balance;
}
}
操作码: 0x3B
功能: 获取指定账户的代码大小。
Gas 消耗: 700 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExtCodeSizeExample {
function getCodeSize(address account) public view returns (uint256) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size;
}
}
操作码: 0x3C
功能: 复制指定账户的代码到内存中。
Gas 消耗: 700 gas + 每字节 3 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExtCodeCopyExample {
function getCode(address account) public view returns (bytes memory) {
uint256 size;
assembly {
size := extcodesize(account)
}
bytes memory code = new bytes(size);
assembly {
extcodecopy(account, add(code, 32), 0, size)
}
return code;
}
}
操作码: 0x3F
功能: 获取指定账户代码的 Keccak-256 哈希。
Gas 消耗: 400 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExtCodeHashExample {
function getCodeHash(address account) public view returns (bytes32) {
bytes32 codehash;
assembly {
codehash := extcodehash(account)
}
return codehash;
}
}
在以太坊虚拟机(EVM)中,交易指令主要用于获取当前交易的信息以及执行发送以太币等操作。这些指令包括 CALL, CALLCODE, DELEGATECALL, STATICCALL, CREATE, CREATE2, CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY, CODESIZE, CODECOPY, 和 GASPRICE。以下是这些指令的详细介绍:
这些指令用于调用其他合约或执行代理调用。
操作码: 0xF1
功能: 调用另一个合约,支持发送以太币。
Gas 消耗: 调用基本费用 + 所需 gas + 调用的子合约执行所需的 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CallExample {
function callAnotherContract(address target, uint256 value, bytes calldata data) public returns (bytes memory) {
(bool success, bytes memory result) = target.call{value: value}(data);
require(success, "Call failed");
return result;
}
}
5.1.2.CALLCODE
操作码: 0xF2
功能: 调用另一个合约的代码,但保持当前合约的存储。
Gas 消耗: 类似 CALL
操作步骤:
5.1.3.DELEGATECALL 5.1.3.委托调用
操作码: 0xF4
功能: 类似 CALLCODE,但继承调用者的 msg.sender 和 msg.value。
Gas 消耗: 类似 CALL
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract DelegateCallExample {
function delegateCallAnotherContract(address target, bytes calldata data) public returns (bytes memory) {
(bool success, bytes memory result) = target.delegatecall(data);
require(success, "Delegate call failed");
return result;
}
}
5.1.4. STATICCALL
操作码: 0xFA
功能: 以静态方式调用另一个合约,不允许状态更改。
Gas 消耗: 类似 CALL
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract StaticCallExample {
function staticCallAnotherContract(address target, bytes calldata data) public view returns (bytes memory) {
(bool success, bytes memory result) = target.staticcall(data);
require(success, "Static call failed");
return result;
}
}
5.2.1.CREATE
操作码: 0xF0
功能: 创建一个新的合约。
Gas 消耗: 32000 gas + 合约创建所需的 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CreateExample {
function createNewContract(bytes memory bytecode) public returns (address) {
address newContract;
assembly {
newContract := create(0, add(bytecode, 32), mload(bytecode))
}
require(newContract != address(0), "Contract creation failed");
return newContract;
}
}
5.2.2.CREATE2
操作码: 0xF5
功能: 使用指定的 salt 创建一个新的合约,确保合约地址的可预测性。
Gas 消耗: 类似 CREATE
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Create2Example {
function createNewContractWithSalt(bytes memory bytecode, bytes32 salt) public returns (address) {
address newContract;
assembly {
newContract := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
require(newContract != address(0), "Contract creation failed");
return newContract;
}
}
3.1.CALLVALUE 调用值
操作码: 0x34
功能: 获取当前调用中发送的以太币数量。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CallValueExample {
function getCallValue() public payable returns (uint256) {
uint256 value;
assembly {
value := callvalue()
}
return value;
}
}
5.3.2.CALLDATALOAD 5.3.2.调用数据加载
操作码: 0x35
功能: 从调用数据中加载一个 32 字节的值。
Gas 消耗: 3 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CallDataLoadExample {
function loadCallData(uint256 position) public pure returns (bytes32) {
bytes32 data;
assembly {
data := calldataload(position)
}
return data;
}
}
5.3.3.CALLDATASIZE
操作码: 0x36
功能: 获取调用数据的大小。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CallDataSizeExample {
function getCallDataSize() public pure returns (uint256) {
uint256 size;
assembly {
size := calldatasize()
}
return size;
}
}
5.3.4.CALLDATACOPY
操作码: 0x37
功能: 将调用数据复制到内存中。
Gas 消耗: 3 gas + 每字节 3 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CallDataCopyExample {
function copyCallData(uint256 start, uint256 length) public pure returns (bytes memory) {
bytes memory data = new bytes(length);
assembly {
calldatacopy(add(data, 32), start, length)
}
return data;
}
}
5.4.1.CODESIZE
操作码: 0x38
功能: 获取当前合约的代码大小。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CodeSizeExample {
function getCodeSize() public view returns (uint256) {
uint256 size;
assembly {
size := codesize()
}
return size;
}
}
5.4.2.CODECOPY
操作码: 0x39
功能: 将当前合约的代码复制到内存中。
Gas 消耗: 3 gas + 每字节 3 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CodeCopyExample {
function copyCode(uint256 start, uint256 length) public view returns (bytes memory) {
bytes memory code = new bytes(length);
assembly {
codecopy(add(code, 32), start, length)
}
return code;
}
}
操作码: 0x3A
功能: 获取当前交易的 gas 价格。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasPriceExample {
function getGasPrice() public view returns (uint256) {
uint256 gasPrice;
assembly {
gasPrice := gasprice()
}
return gasPrice;
}
}
以下是一个综合使用上述指令的完整示例合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract EVMSample {
function callExample(address target, uint256 value, bytes calldata data) public returns (bytes memory) {
(bool success, bytes memory result) = target.call{value: value}(data);
require(success, "Call failed");
return result;
}
function delegateCallExample(address target, bytes calldata data) public returns (bytes memory) {
(bool success, bytes memory result) = target.delegatecall(data);
require(success, "Delegate call failed");
return result;
}
function staticCallExample(address target, bytes calldata data) public view returns (bytes memory) {
(bool success, bytes memory result) = target.staticcall(data);
require(success, "Static call failed");
return result;
}
function createExample(bytes memory bytecode) public returns (address) {
address newContract;
assembly {
newContract := create(0, add(bytecode, 32), mload(bytecode))
}
require(newContract != address(0), "Contract creation failed");
return newContract;
}
function create2Example(bytes memory bytecode, bytes32 salt) public returns (address) {
address newContract;
assembly {
newContract := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
require(newContract != address(0), "Contract creation failed");
return newContract;
}
function getCallValue() public payable returns (uint256) {
uint256 value;
assembly {
value := callvalue()
}
return value;
}
function loadCallData(uint256 position) public pure returns (bytes32) {
bytes32 data;
assembly {
data := calldataload(position)
}
return data;
}
function getCallDataSize() public pure returns (uint256) {
uint256 size;
assembly {
size := calldatasize()
}
return size;
}
function copyCallData(uint256 start, uint256 length) public pure returns (bytes memory) {
bytes memory data = new bytes(length);
assembly {
calldatacopy(add(data, 32), start, length)
}
return data;
}
function getCodeSize() public view returns (uint256) {
uint256 size;
assembly {
size := codesize()
}
return size;
}
function copyCode(uint256 start, uint256 length) public view returns (bytes memory) {
bytes memory code = new bytes(length);
assembly {
codecopy(add(code, 32), start, length)
}
return code;
}
function getGasPrice() public view returns (uint256) {
uint256 gasPrice;
assembly {
gasPrice := gasprice()
}
return gasPrice;
}
}
这个合约演示了如何使用各种 EVM 交易指令来进行合约调用、创建、数据加载和复制、获取交易信息等操作。每个函数对应一个具体的指令,展示了其实际应用和返回结果。
在以太坊虚拟机(EVM)中,Log 指令用于向区块链中写入日志数据。日志数据可以在以太坊客户端(如以太坊节点或区块浏览器)中查看,并且在 Solidity 合约中使用事件(event)来定义和触发日志记录。以下是与 Log 相关的 EVM 指令及其详细介绍:
功能: 将日志数据写入区块链。
操作码:
Gas 消耗: 375 gas + 8 gas/byte
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract LogExample {
event LogEvent(address indexed sender, uint256 indexed value);
function logData(uint256 value) public {
emit LogEvent(msg.sender, value);
}
}
在以太坊虚拟机(EVM)中,Gas 指令主要用于管理和获取当前交易的 gas 相关信息。以下是与 gas 相关的主要指令及其详细介绍:
操作码: 0x5A
功能: 返回剩余的 gas。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasExample {
function getRemainingGas() public view returns (uint256) {
uint256 gasRemaining;
assembly {
gasRemaining := gas()
}
return gasRemaining;
}
}
操作码: 0x45
功能: 获取当前区块的 gas 限制。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasLimitExample {
function getGasLimit() public view returns (uint256) {
uint256 gasLimit;
assembly {
gasLimit := gaslimit()
}
return gasLimit;
}
}
操作码: 0x3A
功能: 获取当前交易的 gas 价格。
Gas 消耗: 2 gas
操作步骤:
示例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasPriceExample {
function getGasPrice() public view returns (uint256) {
uint256 gasPrice;
assembly {
gasPrice := gasprice()
}
return gasPrice;
}
}
在智能合约中,开发者可以使用 gas() 指令来获取当前剩余的 gas,并使用 gaslimit() 和 gasprice() 指令来获取当前区块的 gas 限制和交易的 gas 价格。这些信息对于优化合约性能和管理 gas 成本非常重要。
以下是一个综合使用 gas 指令的合约示例,展示了如何在合约中获取并显示当前交易和区块的 gas 信息:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GasInfo {
function getGasInfo() public view returns (uint256, uint256, uint256) {
uint256 gasRemaining;
uint256 gasLimit;
uint256 gasPrice;
assembly {
gasRemaining := gas()
gasLimit := gaslimit()
gasPrice := gasprice()
}
return (gasRemaining, gasLimit, gasPrice);
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!