Solidity:如何将 bytes32 表示为字符串

问题描述 投票:0回答:3

这在其他语言中可能很简单,但我不知道如何在 Solidity 中做到这一点。
我有一个像这样的

bytes32
0x05416460deb76d57af601be17e777b93592d8d4d4a4096c57876a91c84f4a712

不想想要将字节转换为字符串,而是我只想将整个内容表示为字符串,例如“0x05416460deb76d57af601be17e777b93592d8d4d4a4096c57876a91c84f4a712”。
在 Solidity 中如何做到这一点?

更新:
为什么我需要这样做:基本上我连接到一个预言机,它在链外执行一些工作,最后将文件上传到 IPFS。我需要从预言机将内容标识符添加到我的合约中。预言机只能发送

bytes32
作为响应,因此我将其转换为 multihash 并仅将
digest
作为
bytes32
从预言机发送到合约。
到目前为止一切顺利,我可以在合约中重新创建多重哈希。问题是,在此之后我创建了一个
ERC721
(NFT)令牌,并且我必须在元数据中存储一些对 IPFS 文件的引用,该元数据只能采用
string
格式。这就是我目前陷入困境的地方。

ethereum solidity
3个回答
28
投票

虽然 @Burt 的答案看起来是正确的(虽然没有测试),但有一种更有效的方法来解决相同的任务:

function toHex16 (bytes16 data) internal pure returns (bytes32 result) {
    result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
          (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
    result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
          (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
    result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
          (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
    result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
          (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
    result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
          (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
    result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
           uint256 (result) +
           (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
           0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);
}

function toHex (bytes32 data) public pure returns (string memory) {
    return string (abi.encodePacked ("0x", toHex16 (bytes16 (data)), toHex16 (bytes16 (data << 128))));
}

此代码产生大写输出。对于小写输出,只需将代码中的 7 更改为 39 即可。

解释

这个想法是使用二进制运算一次处理 16 个字节。

toHex16
函数将表示为
bytes16
值的 16 个字节序列转换为表示为
bytes32
值的 32 个十六进制数字序列。
toHex
函数将
bytes32
值拆分为两个
bytes16
块,通过
toHex16
函数将每个块转换为十六进制表示形式,最后使用
0x
函数将
abi.encodePacked
前缀与转换后的块连接起来。

最复杂的部分是

toHex16
函数的工作原理。我们逐句解释一下吧。

第一句话:

result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
      (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;

这里我们将输入的最后 64 位向右移动 64 位,基本上是这样做的:

0123456789abcdeffedcba9876543210
\______________/\______________/
       |               |
       |               +---------------+
 ______V_______                  ______V_______
/              \                /              \
0123456789abcdef0000000000000000fedcba9876543210

第二句:

result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
      (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;

这里我们将两个 64 位块的最后 32 位向右移动 32 位:

0123456789abcdef0000000000000000fedcba9876543210
\______/\______/                \______/\______/
   |       |                       |       |
   |       +-------+               |       +-------+
 __V___          __V___          __V___          __V___
/      \        /      \        /      \        /      \
012345670000000089abcdef00000000fedcba980000000076543210

下一句:

result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
      (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;

是:

012345670000000089abcdef00000000fedcba980000000076543210
\__/\__/        \__/\__/        \__/\__/        \__/\__/
 |   |           |   |           |   |           |   |
 |   +---+       |   +---+       |   +---+       |   +---+
 V_      V_      V_      V_      V_      V_      V_      V_
/  \    /  \    /  \    /  \    /  \    /  \    /  \    /  \
012300004567000089ab0000cdef0000fedc0000ba980000765400003210

下一张:

result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
      (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;

是:

012300004567000089ab0000cdef0000fedc0000ba980000765400003210
\/\/    \/\/    \/\/    \/\/    \/\/    \/\/    \/\/    \/\/
| |     | |     | |     | |     | |     | |     | |     | |
| +-+   | +-+   | +-+   | +-+   | +-+   | +-+   | +-+   | +-+
V   V   V   V   V   V   V   V   V   V   V   V   V   V   V   V
/\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\
01002300450067008900ab00cd00ef00fe00dc00ba00980076005400320010

本系列的最后一句话有点不同:

result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
      (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;

它将奇数半字节右移 4 位,偶数半字节右移 8 位:

01002300450067008900ab00cd00ef00fe00dc00ba00980076005400320010
|\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\  |\
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \
 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
 V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V
000102030405060708090a0b0c0d0e0f0f0e0d0c0b0a09080706050403020100

因此初始数据的所有半字节每个字节都分布一个。

现在对于每个字节

x
我们需要进行以下转换:

x` = x < 10 ? '0' + x : 'A' + (x - 10)

让我们稍微重写一下这个公式:

x` = ('0' + x) + (x < 10 ? 0 : 'A' - '0' - 10)
x` = ('0' + x) + (x < 10 ? 0 : 1) * ('A' - '0' - 10)

注意,

(x < 10 ? 0 : 1)
可以计算为
((x + 6) >> 4)
,因此我们有:

x` = ('0' + x) + ((x + 6) >> 4) * ('A' - '0' - 10)
x` = (0x30 + x) + ((x + 0x06) >> 4) * 7

最后声明:

result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
       uint256 (result) +
       (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
       0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 7);

基本上对每个字节都进行上述计算。

0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F

需要右移后的掩码将原始公式中右移“丢弃”的位清零。

顺便说一句,最好在 https://ethereum.stackexchange.com/

提出这样的问题

4
投票

函数

bytes32ToString
将 bytes32 转换为十六进制字符串

function bytes32ToString(bytes32 _bytes32) public 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) public pure returns (byte) {
    if(_uint8 < 10) {
        return byte(_uint8 + 48);
    } else {
        return byte(_uint8 + 87);
    }
}

0
投票

在openzeppeline库中,有Strings库可以将uint256转换为字符串,现在您需要将bytes32转换为uint256,通过uint256(bytes32Data)函数很容易做到,如果您需要在remixd中使用console.log,请使用“导入“hardhat/console.sol”;”一开始。

https://docs.openzeppelin.com/contracts/5.x/api/utils#Strings

以下是我的测试代码,供大家参考:

    function getKeccak256(string memory _text) public pure returns(bytes32) {
    //bytes32
    bytes32 hexBytes = keccak256(abi.encodePacked(_text));
    
    //uint256
    uint256 numHex = uint256(hexBytes); 
    console.log("the numHex is : ", numHex); 

    // string
    string memory hexString = Strings.toHexString(numHex);
    console.log("the hexString is : ", hexString); 

    return keccak256(abi.encodePacked(_text)); 
}
© www.soinside.com 2019 - 2024. All rights reserved.