在这里插入图片描述

欢迎来到《Solidity面试修炼之道》专栏💎。

专栏核心理念:

核心 Slogan💸💸:从面试题到实战精通,你的 Web3 开发进阶指南。

一句话介绍🔬🔬: 150+ 道面试题 × 103 篇深度解析 = 你的 Solidity 修炼秘籍。

  1. ✅ 名称有深度和系统性
  2. ✅ "修炼"体现进阶过程
  3. ✅ 适合中文技术社区
  4. ✅ 记忆度高,易于传播
  5. ✅ 全场景适用

Q12: 以太坊主要使用什么哈希函数?

简答:
以太坊主要使用 Keccak-256 哈希函数,这是 SHA-3 的一个变体。在 Solidity 中通过 keccak256() 函数使用。

详细分析:
Keccak-256 是以太坊的核心哈希函数,用于多个关键场景:

主要用途

  1. 地址生成:从公钥生成以太坊地址
  2. 数据完整性:验证数据未被篡改
  3. Merkle 树:构建状态树、交易树、收据树
  4. 签名:ECDSA 签名前对消息进行哈希
  5. 存储槽计算:计算 mapping 和动态数组的存储位置
  6. 函数选择器:计算函数签名的前 4 字节

Keccak-256 特性

  • 输出长度:256 位(32 字节)
  • 单向函数:不可逆
  • 抗碰撞:找到两个产生相同哈希的输入在计算上不可行
  • 雪崩效应:输入的微小变化导致输出完全不同

注意:以太坊使用的是 Keccak-256,而不是最终标准化的 SHA3-256。它们的输出不同,这是一个历史遗留问题(以太坊在 SHA-3 标准化之前就采用了 Keccak)。

代码示例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title Keccak256Examples
 * @notice 演示 Keccak-256 哈希函数的使用
 */
contract Keccak256Examples {
    
    /**
     * @notice 基本哈希示例
     */
    function basicHash(string memory _text) public pure returns (bytes32) {
        // 对字符串进行哈希
        return keccak256(abi.encodePacked(_text));
    }
    
    /**
     * @notice 哈希多个参数
     */
    function hashMultipleParams(
        address _addr,
        uint256 _amount,
        string memory _message
    ) public pure returns (bytes32) {
        // 将多个参数编码后哈希
        return keccak256(abi.encodePacked(_addr, _amount, _message));
    }
    
    /**
     * @notice 演示雪崩效应
     * @dev 输入的微小变化导致完全不同的输出
     */
    function demonstrateAvalanche() public pure returns (
        bytes32 hash1,
        bytes32 hash2
    ) {
        // 两个几乎相同的输入
        hash1 = keccak256(abi.encodePacked("Hello"));
        hash2 = keccak256(abi.encodePacked("hello")); // 只有大小写不同
        
        // 输出完全不同!
        assert(hash1 != hash2);
        return (hash1, hash2);
    }
    
    /**
     * @notice 用途 1:数据完整性验证
     */
    function verifyData(
        string memory _data,
        bytes32 _expectedHash
    ) public pure returns (bool) {
        bytes32 actualHash = keccak256(abi.encodePacked(_data));
        return actualHash == _expectedHash;
    }
    
    /**
     * @notice 用途 2:承诺方案(Commit-Reveal)
     */
    mapping(address => bytes32) public commitments;
    
    function commit(bytes32 _commitment) public {
        commitments[msg.sender] = _commitment;
    }
    
    function reveal(uint256 _secret) public view returns (bool) {
        bytes32 hash = keccak256(abi.encodePacked(_secret, msg.sender));
        return hash == commitments[msg.sender];
    }
    
    /**
     * @notice 用途 3:生成唯一标识符
     */
    function generateUniqueId(
        address _user,
        uint256 _nonce
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_user, _nonce, block.timestamp));
    }
    
    /**
     * @notice 用途 4:计算函数选择器
     */
    function getFunctionSelector(string memory _signature) public pure returns (bytes4) {
        // 例如:"transfer(address,uint256)"
        return bytes4(keccak256(bytes(_signature)));
    }
    
    /**
     * @notice 用途 5:计算存储槽位置
     */
    function getStorageSlot(
        uint256 _mappingSlot,
        address _key
    ) public pure returns (bytes32) {
        // mapping 的存储位置 = keccak256(key . slot)
        return keccak256(abi.encodePacked(_key, _mappingSlot));
    }
    
    /**
     * @notice 用途 6:Merkle 树验证
     */
    function verifyMerkleProof(
        bytes32 _leaf,
        bytes32[] memory _proof,
        bytes32 _root
    ) public pure returns (bool) {
        bytes32 computedHash = _leaf;
        
        for (uint256 i = 0; i < _proof.length; i++) {
            bytes32 proofElement = _proof[i];
            
            if (computedHash < proofElement) {
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }
        
        return computedHash == _root;
    }
}

/**
 * @title HashingBestPractices
 * @notice 哈希最佳实践
 */
contract HashingBestPractices {
    
    /**
     * @notice ✅ 推荐:使用 abi.encode
     * @dev 避免哈希碰撞
     */
    function safeHash(string memory _a, string memory _b) public pure returns (bytes32) {
        // ✅ 使用 abi.encode:每个参数有明确的边界
        return keccak256(abi.encode(_a, _b));
    }
    
    /**
     * @notice ⚠️ 注意:使用 abi.encodePacked 可能导致碰撞
     * @dev 当参数是动态类型时要小心
     */
    function potentialCollision(
        string memory _a,
        string memory _b
    ) public pure returns (bytes32) {
        // ⚠️ abi.encodePacked 可能导致碰撞
        // 例如:("aa", "bb") 和 ("a", "abb") 产生相同的哈希
        return keccak256(abi.encodePacked(_a, _b));
    }
    
    /**
     * @notice 演示哈希碰撞
     */
    function demonstrateCollision() public pure returns (bool) {
        // 这两个调用产生相同的哈希!
        bytes32 hash1 = keccak256(abi.encodePacked("aa", "bb"));
        bytes32 hash2 = keccak256(abi.encodePacked("a", "abb"));
        
        return hash1 == hash2; // 返回 true!
    }
    
    /**
     * @notice ✅ 避免碰撞的方法
     */
    function avoidCollision(
        string memory _a,
        string memory _b
    ) public pure returns (bytes32) {
        // 方法 1:使用 abi.encode
        return keccak256(abi.encode(_a, _b));
        
        // 方法 2:添加分隔符
        // return keccak256(abi.encodePacked(_a, "|", _b));
        
        // 方法 3:包含长度信息
        // return keccak256(abi.encodePacked(_a.length, _a, _b.length, _b));
    }
}

/**
 * @title Keccak256Applications
 * @notice Keccak-256 的实际应用
 */
contract Keccak256Applications {
    
    /**
     * @notice 应用 1:密码哈希(不推荐直接存储密码)
     */
    mapping(address => bytes32) public passwordHashes;
    
    function setPassword(string memory _password) public {
        // 注意:实际应用中应该加盐
        passwordHashes[msg.sender] = keccak256(abi.encodePacked(_password));
    }
    
    function verifyPassword(string memory _password) public view returns (bool) {
        return keccak256(abi.encodePacked(_password)) == passwordHashes[msg.sender];
    }
    
    /**
     * @notice 应用 2:内容寻址存储
     */
    mapping(bytes32 => string) public contentStore;
    
    function storeContent(string memory _content) public returns (bytes32) {
        bytes32 contentHash = keccak256(abi.encodePacked(_content));
        contentStore[contentHash] = _content;
        return contentHash;
    }
    
    function retrieveContent(bytes32 _hash) public view returns (string memory) {
        return contentStore[_hash];
    }
    
    /**
     * @notice 应用 3:签名验证
     */
    function getMessageHash(string memory _message) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(_message));
    }
    
    function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) {
        // 以太坊签名消息格式
        return keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            _messageHash
        ));
    }
}

/**
 * @title Keccak256Performance
 * @notice Keccak-256 性能考虑
 */
contract Keccak256Performance {
    
    /**
     * @notice Gas 成本
     * - 基础成本:30 gas
     * - 每 32 字节:6 gas
     * 
     * 例如:
     * - 哈希 32 字节:~36 gas
     * - 哈希 64 字节:~42 gas
     * - 哈希 256 字节:~78 gas
     */
    
    /**
     * @notice 优化技巧 1:缓存哈希结果
     */
    mapping(string => bytes32) private hashCache;
    
    function getCachedHash(string memory _data) public returns (bytes32) {
        bytes32 cached = hashCache[_data];
        if (cached == bytes32(0)) {
            cached = keccak256(abi.encodePacked(_data));
            hashCache[_data] = cached;
        }
        return cached;
    }
    
    /**
     * @notice 优化技巧 2:预计算常量哈希
     */
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
    bytes32 public constant USER_ROLE = keccak256("USER");
    
    // 这些哈希在编译时计算,不消耗运行时 gas
}

理论补充:
Keccak-256 vs SHA3-256:

输入:"hello"

Keccak-256(以太坊使用):
0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8

SHA3-256(NIST 标准):
0x3338be694f50c5f338814986cdf0686453a888b84f424d792af4b9202398f392

它们不同!

以太坊中的哈希用途:

  1. 地址生成

    公钥 -> Keccak-256 -> 取后 20 字节 -> 地址
    
  2. 交易哈希

    交易数据 -> RLP 编码 -> Keccak-256 -> 交易哈希
    
  3. 区块哈希

    区块头 -> RLP 编码 -> Keccak-256 -> 区块哈希
    
  4. 状态根

    Merkle Patricia Tree -> Keccak-256 -> 状态根
    

安全考虑:

  • Keccak-256 被认为是密码学安全的
  • 没有已知的实际碰撞攻击
  • 抗原像攻击和第二原像攻击
  • 适合用于密码学应用

性能特点:

  • 比 SHA-256 稍快
  • 硬件实现效率高
  • 适合以太坊的需求

相关问题:

  • Q7: 在区块链上创建随机数有哪些挑战?
  • Q37: 什么是提交-揭示方案,什么时候会使用它?
  • Q44: 自定义错误和带有错误字符串的 require 在 EVM 层面的编码有什么区别?
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐