🔮

solidity バイト処理まとめ

2024/06/03に公開

solidityのbyte周りが苦手なのでまとめました。

低レイヤーの実装をするようになると頻繁に利用するようになるので、solidityのエンジニアには必須の知識です。

ちなみにsolidityの実装にAIを使うと、ほぼ100%の確率で適当なコードを吐き出してくる(要はネットに出回っているコードが間違いだらけ)ので、現時点でのAIの利用はやめましょう。

環境

  • solidity ^0.8.19
  • hardhat

bytes1~bytes32

1バイトから32バイトまでのバイト列を保持できます。
非常に低コストなので、バイト長を制限できるならば、bytesではなくbytes1~bytes32を利用した方が良いです。

まずはbytes1

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test() public {
        bytes1 data1 = 0xff;
        console.logBytes1(data1); // 0xff
    }
}

接頭の0xは16進数を表しています。

hardhatのlogで出力を確認したい場合はconsole.logBytes1(data1);のようにします。

次にbytes16

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test() public {
        bytes1 data1 = 0xff;
        bytes16 data16 = 0xffaabbccddeeff112233445566778899;
        console.logBytes1(data1); // 0xff
        console.logBytes16(data16); // 0xffaabbccddeeff112233445566778899
    }
}

hardhatのlogで出力を確認したい場合はconsole.logBytes16(data16);のようにします。

bytes1~bytes32 length

lengthでサイズを取得できます。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test() public {
        bytes1 data1 = 0xff;
        bytes16 data16 = 0xffaabbccddeeff112233445566778899;
        console.logBytes1(data1); // 0xff
        console.logBytes16(data16); // 0xffaabbccddeeff112233445566778899
        console.log(data1.length); // 1
        console.log(data16.length); // 16
    }
}

indexで要素にアクセス

indexを使ってbyteの配列に格納できます。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test() public {
        bytes1 data1 = 0xff;
        bytes16 data16 = 0xffaabbccddeeff112233445566778899;
        console.logBytes1(data1); // 0xff
        console.logBytes16(data16); // 0xffaabbccddeeff112233445566778899
        console.log(data1.length); // 1
        console.log(data16.length); // 16
        console.logBytes1(data1[0]); // 0xff
        console.logBytes1(data16[1]); // 0xaa
    }
}

bytes

byte大きさがわからない時はbytesを使います。

大きさがわからないので、関数の引数に使います。

bytesはeth.jsのencodeFunctionData等の関数で作成できます。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test2(bytes calldata initCode) public {
        console.log("initCode");
        console.logBytes(initCode); // 0xf8a8fd6d
        console.logBytes(initCode[0 : 3]); // 0xf8a8fd
        console.logBytes20(bytes20(initCode[0 : 3])); // 0xf8a8fd0000000000000000000000000000000000
    }
}

上記では引数のbytesが4byteで、bytes20を使うことで20バイトのデータにしています。
bytes20を呼び出すと、右側を0paddingで埋めます。

addressは20バイトなので、addressとしても利用できます。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test2(bytes calldata initCode) public {
        console.log("initCode");
        console.logBytes(initCode); // 0xf8a8fd6d
        console.logBytes(initCode[0 : 3]); // 0xf8a8fd
        console.logBytes20(bytes20(initCode[0 : 3])); // 0xf8a8fd0000000000000000000000000000000000
        address factory = address(bytes20(initCode[0 : 3]));
        console.log("address");
        console.log(factory); // 0xf8a8fd0000000000000000000000000000000000
    }
}

この辺は低レイヤーの実装でよく使うので理解しておきましょう。

keccak256

文字列をbytes32にするにはkeccak256を使います。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test2(bytes calldata initCode) public {
        bytes32 b32str = keccak256("hello");
        console.logBytes32(b32str); // 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
    }
}

abi.encodePacked

abi.encodePackedはBytesを返します。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test2(bytes calldata initCode) public {
        console.logBytes(abi.encodePacked("hello")); // 0x68656c6c6f
    }
}

認証ではkeccak256と一緒に使ってメッセージのハッシュを作成します。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test2(bytes calldata initCode) public {
        console.logBytes32(keccak256(abi.encodePacked("hello"))); // 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
    }
}

署名の場合には以下のように実装します。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import "hardhat/console.sol";

contract Study {
    function test2(bytes calldata initCode) public {
        console.logBytes32(keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(abi.encodePacked("hello")))
        )); // 0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3
    }
}

参考記事

https://docs.soliditylang.org/en/v0.8.19/types.html#bytes-and-string-as-arrays

https://qiita.com/oatnnimi/items/db55a641a6e5c88f7e5b

https://qiita.com/uskayyyyy/items/19008d997d4c21954e9a

Discussion