如何在Java/Kotlin中实现与Solidity一致的getMessageHash计算功能
Solidity abi.encodePacked + keccak256 等价 Java/Kotlin 实现
核心原理
Solidity的abi.encodePacked为紧密编码模式,不会对参数做字节对齐补零,本题中三个参数的编码规则如下:
address类型:直接编码为20字节原始二进制uint(默认等价uint256)类型:编码为32字节大端序无符号二进制,高位补零至32字节长度
将三个参数的编码结果按顺序拼接后执行keccak256哈希,最终转为0x开头的十六进制字符串即可得到和Solidity完全一致的结果。
你之前使用的org.web3j.crypto.Hash.sha3有接收字节数组的重载方法,不需要拼接为单个字符串再传入。
前置依赖
使用web3j的crypto工具包即可,Maven依赖示例:
<dependency> <groupId>org.web3j</groupId> <artifactId>crypto</artifactId> <version>4.10.0</version> </dependency>
Kotlin 实现
import org.web3j.crypto.Hash import org.web3j.utils.Numeric import java.math.BigInteger // 支持大数值入参版本 fun getMessageHash(signer: String, amount: BigInteger, id: BigInteger): String { // 处理address参数:去掉0x前缀转20字节数组 val signerBytes = Numeric.hexStringToByteArray(signer.removePrefix("0x")) require(signerBytes.size == 20) { "非法的address格式" } // 处理uint256参数,转32字节大端序数组,高位补零 val amountBytes = Numeric.toBytesPadded(amount, 32) val idBytes = Numeric.toBytesPadded(id, 32) // 拼接所有字节数组 val totalBytes = signerBytes + amountBytes + idBytes // 计算keccak256并返回0x开头的十六进制字符串 return Hash.sha3(totalBytes) } // Long类型入参重载,适合数值不超过Long范围的场景 fun getMessageHash(signer: String, amount: Long, id: Long): String { return getMessageHash(signer, amount.toBigInteger(), id.toBigInteger()) }
Java 实现
import org.web3j.crypto.Hash; import org.web3j.utils.Numeric; import java.math.BigInteger; public class SolidityHashCalculator { // 支持大数值入参版本 public static String getMessageHash(String signer, BigInteger amount, BigInteger id) { // 处理address参数 byte[] signerBytes = Numeric.hexStringToByteArray(signer.replaceFirst("^0x", "")); if (signerBytes.length != 20) { throw new IllegalArgumentException("非法的address格式"); } // 处理uint256参数 byte[] amountBytes = Numeric.toBytesPadded(amount, 32); byte[] idBytes = Numeric.toBytesPadded(id, 32); // 拼接字节数组 byte[] totalBytes = new byte[signerBytes.length + amountBytes.length + idBytes.length]; System.arraycopy(signerBytes, 0, totalBytes, 0, signerBytes.length); System.arraycopy(amountBytes, 0, totalBytes, signerBytes.length, amountBytes.length); System.arraycopy(idBytes, 0, totalBytes, signerBytes.length + amountBytes.length, idBytes.length); // 计算哈希返回 return Hash.sha3(totalBytes); } // long类型入参重载 public static String getMessageHash(String signer, long amount, long id) { return getMessageHash(signer, BigInteger.valueOf(amount), BigInteger.valueOf(id)); } }
注意事项
- 若你的Solidity代码中uint不是默认的uint256,比如是uint64,对应的转字节数组时补零长度要改为对应类型的字节数(比如uint64对应8字节)
- 输入的address需要是合法的40/42位十六进制字符串,否则会抛出参数校验异常
内容的提问来源于stack exchange,提问作者Daguna
相关产品推荐
相关产品推荐

