Solidity 和 web3 sha3

Posted

技术标签:

【中文标题】Solidity 和 web3 sha3【英文标题】:Solidity and web3 sha3 【发布时间】:2021-11-10 20:10:03 【问题描述】:

我尝试在我的智能合约中使用种子对 tokenId 进行哈希处理。为简单起见并避免其他错误,我暂时将种子放在一边。我基本上只是想在我的合同上散列一个数字并在我的 javascript 代码上散列相同的数字并接收相同的输出。 代码在 Solidity 上看起来像这样:

  function _tokenURI(uint256 tokenId) internal view returns (string memory) 
    string memory currentBaseURI = _baseURI();
    bytes32 hashedToken = keccak256(abi.encodePacked(tokenId));

    return
      bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, hashedToken, baseExtension))
        : "";
  

这也会导致客户端出现错误invalid codepoint at offset。为了解决这个问题,我尝试使用这些函数将 bit32 转换为字符串

  function _bytes32ToString(bytes32 _bytes32)
    private
    pure
    returns (string memory)
  
    uint8 i = 0;
    bytes memory bytesArray = new bytes(64);
    for (i = 0; i < bytesArray.length; i++) 
      uint8 _f = uint8(_bytes32[i / 2] & 0x0f);
      uint8 _l = uint8(_bytes32[i / 2] >> 4);

      bytesArray[i] = _toByte(_f);
      i = i + 1;
      bytesArray[i] = _toByte(_l);
    
    return string(bytesArray);
  

  function _toByte(uint8 _uint8) private pure returns (bytes1) 
    if (_uint8 < 10) 
      return bytes1(_uint8 + 48);
     else 
      return bytes1(_uint8 + 87);
    
  

虽然我不确定这是否等效。前端代码如下:

const hashed = web3.utils.soliditySha3(
           type: "uint256", value: tokenId
        );

为了获得完全相同的输出,我需要进行哪些更改? invalid codepoint at offset 是什么意思?

【问题讨论】:

【参考方案1】:

您收到invalid codepoint 错误,因为您在调用abi.encodePacked(currentBaseURI, hashedToken, baseExtension)) 时混合了字符串和字节数据。

当 Javascript 从合约中获取返回值时,它需要一个 UTF8 字符串,但在您的 hashedToken 中,您的字节值对于 UTF8 编码的字符串无效。

这种错误可能是“间歇性的”。它可能仅在某些情况下发生。您很幸运在开发过程中而不是在生产环境中看到它。

如何解决?

您将哈希结果转换为字符串是正确的。 在这个answer 中有另一种方法可以做到这一点,它只使用按位运算来使用更少的气体。

要在 Javascript 中转换十六进制值,您可以使用 web3.utils.hexToNumberString()。

【讨论】:

【参考方案2】:

也许问题是 tokenId 不是 uint256 或 Web3,Solidity 版本?我用 Remix IDE 做了一些测试,得到了相同的结果。 Solidity 代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
 
contract Hash 
    function getHashValue_1() public view returns(bytes32)
        return keccak256(abi.encodePacked(uint256(234)));
    
    // bytes32: 0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2
    
    function getHashValue_3() public view returns(bytes32)
        return keccak256(abi.encodePacked(uint256(10),string('StringSecretValue')));
    
    // bytes32: 0x5938b4caf29ac4903ee34628c3dc1eb5c670a6bd392a006d0cb91f1fc5db3819

JS代码:

(async () => 
    try 
        console.log('Web3 version is '+ Web3.version);
        // Web3 version is 1.3.0
        
        let theValueYouNeed = web3.utils.soliditySha3("234");
        theValueYouNeed = web3.utils.soliditySha3(type: 'uint256', value: '234');
        theValueYouNeed = web3.utils.soliditySha3(t: 'uint256', v: '234');
        // above hashed value is 0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2
        console.log('Hashed value 1 is '+theValueYouNeed);
        
        theValueYouNeed = web3.utils.soliditySha3(t: 'uint256', v: '10',t: 'string', v: 'StringSecretValue');
        console.log('Hashed value 2 is '+theValueYouNeed);
        // above hashed value is 0x5938b4caf29ac4903ee34628c3dc1eb5c670a6bd392a006d0cb91f1fc5db3819
        
     catch (e) 
        console.log(e.message)
    
)()

我不确定,但 invalid codepoint at offset 应该意味着 a designated value does not fall within the range or set of allowed values... 所以也许 tokenId 有问题,你可以做一些测试使用硬编码值?

【讨论】:

以上是关于Solidity 和 web3 sha3的主要内容,如果未能解决你的问题,请参考以下文章

Solidity、solc、web3.js、Ganache 版本组合目前正在使用啥

如何使用solidity和web3将以太币存入账户?

Solidity 函数将空数组返回给 web3.js

使用 Web3 从 Solidity 调用函数

如何将一组结构从 web3js 发送到 Solidity 合约?

Solidity 事件未从 Web3.js 显示