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);
    }
} 
                如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!