最近在使用Java写程序,来与ETH
合约进行交互。但是Ethereumj
和 Web3j
的使用体验都不是很好,与其他语言的版本有很大的差距。其中有一个Solidity
中的函数encodePacked
在Java
中没有找到任何已有的实现,最后只能是自己摸索了。
原函数探查
首先搜索了Solidity
相关的文档,发现encodePacked
函数已经转为 soliditySha3
。搜索后还是没有发现Java
版本。所以还是先找到了功能实现比较齐全的JavaScript
版本来作为对照。
在JavaScript
中的使用如下:
1 2
| let result = Web3.utils.soliditySha3({t: 'string', v:'233'}) console.log(result)
|
其内部根据传入参数的不同类型,首先对各个参数进行转换,转为byte array
,然后直接将byte array
进行拼接,最后转为Hex
字符串结束。
Address
直接使用ethereumj
中的ByteUtil.hexStringToBytes
进行转化即可;
uint(uint256)
使用ByteUtil.bigIntegerToBytes
进行转化。这里需要注意的是,要显示的指明 numBytes
参数为32,这里最开始没有进行制定,计算总是不一致,还是翻看了JavaScript
中的代码才找到了此问题;
string
直接使用 String.getBytes()
即可;
最终代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public static String soliditySha3(SParams... params) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); for (SParams param : params) { byte[] bytes = solidityType2Byte(param); try { bos.write(bytes); } catch (IOException e) { log.error("write byte error", e); throw new HBCExceiption("solidity sha3 calc error"); } }
return ByteUtil.toHexString(HashUtil.sha3(bos.toByteArray())); }
public static byte[] solidityType2Byte(SParams param) { switch (param.type) { case "address": return ByteUtil.hexStringToBytes((String) param.value); case "uint": if (param.value instanceof Integer) { return ByteUtil.bigIntegerToBytes(BigInteger.valueOf((int) param.value), 32); } else if (param.value instanceof BigInteger) { return ByteUtil.bigIntegerToBytes((BigInteger) param.value, 32); } else { return ByteUtil.bigIntegerToBytes(BigInteger.valueOf((long) param.value), 32); } case "string": return ((String) param.value).getBytes(); default: throw new HBCExceiption(String.format("unknown solidity type:%s", param.type)); } }
public static class SParams { public String type; public Object value;
public SParams(String type, Object value) { this.type = type; this.value = value; }
|