一:前言
由于目前现在还没有java的开源filecoin交易的代码,这面只做参考,目前是可以签名并发送交易成功的。
参考rust代码。
https://github.com/Zondax/filecoin-signing-tools
大家可以先看下这个代码。
代码链接:
https://github.com/codeFarmL/FileCoin/tree/master
目前对于rust转变java代码只有一个细节是不确定的
给大家看下代码:
if (new BigInteger(unsignedMessageAPI.getValue()).toByteArray()[0] != 0) {
byte[] byte1 = new byte[new BigInteger(unsignedMessageAPI.getValue()).toByteArray().length + 1];
byte1[0] = 0;
System.arraycopy(new BigInteger(unsignedMessageAPI.getValue()).toByteArray(), 0, byte1, 1, new BigInteger(unsignedMessageAPI.getValue()).toByteArray().length);
valueByteString = new co.nstant.in.cbor.model
.ByteString(byte1);
} else {
valueByteString = new co.nstant.in.cbor.model
.ByteString(new BigInteger(unsignedMessageAPI.getValue()).toByteArray());
}
unsignedMessage.setValue(valueByteString);
也就是字节补0这,我一直看rust代码没看懂,这面我如果不做这个判断,形成的value字节总会少个0,这样就和rust代码的value字节对不上,所以这面我做了判断,如果转换的value字节首位不是0则补0,是0则不补0,这面感觉会有问题,
但是我试了20多笔都是可以发起交易的。
大家先看交易。
二:交易流程:
用到的库:
ove.blake2b-alpha.0.jar (hash算法)
implementation 'co.nstant.in:cbor:0.9'(结构体形成字节)
implementation 'org.web3j:core:4.2.0'(签名)
1,构建交易结构体:
生成业务的交易对象
public static UnsignedMessageAPI createUnsignedMessageAPI(String json) {
UnsignedMessageAPI unsignedMessageAPI = new UnsignedMessageAPI();
unsignedMessageAPI.setFrom("f1b6esg2ynyuiwawowyzv3ioc7chboesp4ja6ewfi");
unsignedMessageAPI.setTo("f1wlsdn2phpzczyalerizzrrhhpqjlsn67an54thq");
unsignedMessageAPI.setNonce(12);
unsignedMessageAPI.setValue("100000000000");
unsignedMessageAPI.setGasFeeCap("101183");
unsignedMessageAPI.setGasPremium("100129");
unsignedMessageAPI.setGas_limit(1000000);
unsignedMessageAPI.setMethod(0);
unsignedMessageAPI.setParams("");
return unsignedMessageAPI;
}
value的值是乘以了10的18次方,原value是1个fil
交易的参数:
public class UnsignedMessageAPI {
private String to;
private String from;
private long nonce;
private String value;
private long gas_limit;
private String gasFeeCap;
private String gasPremium;
private long method;
private String params = "";
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public long getNonce() {
return nonce;
}
public void setNonce(long nonce) {
this.nonce = nonce;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getGasFeeCap() {
return gasFeeCap;
}
public void setGasFeeCap(String gasFeeCap) {
this.gasFeeCap = gasFeeCap;
}
public String getGasPremium() {
return gasPremium;
}
public void setGasPremium(String gasPremium) {
this.gasPremium = gasPremium;
}
public long getGas_limit() {
return gas_limit;
}
public void setGas_limit(long gas_limit) {
this.gas_limit = gas_limit;
}
public long getMethod() {
return method;
}
public void setMethod(long method) {
this.method = method;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
交易的序列化结构体
public class UnsignedMessage {
private UnsignedInteger version;
private ByteString from;
private ByteString to;
private UnsignedInteger sequence;
private ByteString value;
private ByteString gasFeeCap;
private ByteString gasPremium;
private UnsignedInteger gas_limit;
private UnsignedInteger method_num;
private ByteString params; //空数组
public UnsignedInteger getVersion() {
return version;
}
public void setVersion(UnsignedInteger version) {
this.version = version;
}
public ByteString getFrom() {
return from;
}
public void setFrom(ByteString from) {
this.from = from;
}
public ByteString getTo() {
return to;
}
public void setTo(ByteString to) {
this.to = to;
}
public UnsignedInteger getSequence() {
return sequence;
}
public void setSequence(UnsignedInteger sequence) {
this.sequence = sequence;
}
public ByteString getGasFeeCap() {
return gasFeeCap;
}
public void setGasFeeCap(ByteString gasFeeCap) {
this.gasFeeCap = gasFeeCap;
}
public ByteString getGasPremium() {
return gasPremium;
}
public void setGasPremium(ByteString gasPremium) {
this.gasPremium = gasPremium;
}
public ByteString getValue() {
return value;
}
public void setValue(ByteString value) {
this.value = value;
}
public UnsignedInteger getGas_limit() {
return gas_limit;
}
public void setGas_limit(UnsignedInteger gas_limit) {
this.gas_limit = gas_limit;
}
public UnsignedInteger getMethod_num() {
return method_num;
}
public void setMethod_num(UnsignedInteger method_num) {
this.method_num = method_num;
}
public ByteString getParams() {
return params;
}
public void setParams(ByteString params) {
this.params = params;
}
}
通过交易参数构建序列化结构体
public static UnsignedMessage try_from(UnsignedMessageAPI unsignedMessageAPI) {
//构建交易结构体
Address from = Address.from_str(unsignedMessageAPI.getFrom());
Address to = Address.from_str(unsignedMessageAPI.getTo());
UnsignedMessage unsignedMessage = new UnsignedMessage();
unsignedMessage.setVersion(new UnsignedInteger(0));
unsignedMessage.setTo(new co.nstant.in.cbor.model.ByteString(to.getPayload().getSecp256k1().getBytes()));
unsignedMessage.setFrom(new co.nstant.in.cbor.model.ByteString(from.getPayload().getSecp256k1().getBytes()));
unsignedMessage.setSequence(new UnsignedInteger(unsignedMessageAPI.getNonce()));
co.nstant.in.cbor.model.ByteString valueByteString = null;
if (new BigInteger(unsignedMessageAPI.getValue()).toByteArray()[0] != 0) {
byte[] byte1 = new byte[new BigInteger(unsignedMessageAPI.getValue()).toByteArray().length + 1];
byte1[0] = 0;
System.arraycopy(new BigInteger(unsignedMessageAPI.getValue()).toByteArray(), 0, byte1, 1, new BigInteger(unsignedMessageAPI.getValue()).toByteArray().length);
valueByteString = new co.nstant.in.cbor.model
.ByteString(byte1);
} else {
valueByteString = new co.nstant.in.cbor.model
.ByteString(new BigInteger(unsignedMessageAPI.getValue()).toByteArray());
}
unsignedMessage.setValue(valueByteString);
unsignedMessage.setGas_limit(new UnsignedInteger(unsignedMessageAPI.getGas_limit()));
co.nstant.in.cbor.model.ByteString gasFeeCapString = null;
if (new BigInteger(unsignedMessageAPI.getGasFeeCap()).toByteArray()[0] != 0) {
byte[] byte2 = new byte[new BigInteger(unsignedMessageAPI.getGasFeeCap()).toByteArray().length + 1];
byte2[0] = 0;
System.arraycopy(new BigInteger(unsignedMessageAPI.getGasFeeCap()).toByteArray(), 0, byte2, 1
, new BigInteger(unsignedMessageAPI.getGasFeeCap()).toByteArray().length);
gasFeeCapString = new co.nstant.in.cbor.model
.ByteString(byte2);
} else {
gasFeeCapString = new co.nstant.in.cbor.model
.ByteString(new BigInteger(unsignedMessageAPI.getGasFeeCap()).toByteArray());
}
unsignedMessage.setGasFeeCap(gasFeeCapString);
co.nstant.in.cbor.model.ByteString gasGasPremium = null;
if (new BigInteger(unsignedMessageAPI.getGasPremium()).toByteArray()[0] != 0) {
byte[] byte2 = new byte[new BigInteger(unsignedMessageAPI.getGasPremium()).toByteArray().length + 1];
byte2[0] = 0;
System.arraycopy(new BigInteger(unsignedMessageAPI.getGasPremium()).toByteArray(), 0, byte2, 1
, new BigInteger(unsignedMessageAPI.getGasPremium()).toByteArray().length);
gasGasPremium = new co.nstant.in.cbor.model
.ByteString(byte2);
} else {
gasGasPremium = new co.nstant.in.cbor.model
.ByteString(new BigInteger(unsignedMessageAPI.getGasPremium()).toByteArray());
}
unsignedMessage.setGasPremium(gasGasPremium);
unsignedMessage.setMethod_num(new UnsignedInteger(0));
unsignedMessage.setParams(new co.nstant.in.cbor.model.ByteString(new byte[0]));
return unsignedMessage;
}
public class Address {
private String network = "f";
private Payload payload; //应该是字节 截取20位
public String getNetwork() {
return network;
}
public void setNetwork(String network) {
this.network = network;
}
public Payload getPayload() {
return payload;
}
public void setPayload(Payload payload) {
this.payload = payload;
}
public static Address from_str(String addressStr) {
Address address = new Address();
//去掉前两位
String str = addressStr.substring(2);
byte[] bytes12 = new byte[21];
//为啥加1,因为是Secp256k1的标识就是1
bytes12[0] = 1;
System.arraycopy(Base32New.decode(str), 0, bytes12, 1, 20);
Secp256k1 secp256k1 = new Secp256k1();
secp256k1.setBytes(bytes12);
Payload payload = new Payload();
payload.setSecp256k1(secp256k1);
address.setPayload(payload);
return address;
}
}
2,构建完交易体之后生成交易字节
/**
* @param unsignedMessageAPI
*/
public static void transaction_serialize(UnsignedMessageAPI unsignedMessageAPI) {
/**
* 拼接UnsignedMessage对象
* 这面用的是CborEncoder
* 问题:什么是CborEncoder
*/
UnsignedMessage unsignedMessage = try_from(unsignedMessageAPI);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
new CborEncoder(baos).encode(new CborBuilder()
.addArray()
.add(unsignedMessage.getVersion())
// add string
.add(unsignedMessage.getTo())
.add(unsignedMessage.getFrom())
.add(unsignedMessage.getSequence())
.add(unsignedMessage.getValue())
.add(unsignedMessage.getGas_limit())
.add(unsignedMessage.getGasFeeCap())
.add(unsignedMessage.getGasPremium())
.add(unsignedMessage.getMethod_num())
// add integer
.add(new co.nstant.in.cbor.model.ByteString(new byte[]{}))
.end()
.build());
byte[] encodedBytes = baos.toByteArray();
byte[] cidHashBytes = getCidHash(encodedBytes);
sign(cidHashBytes);
} catch (CborException e) {
e.printStackTrace();
}
}
3,获取CidHash
/**
* 形成摘要需要拼接的字符串
*/
public static byte[] CID_PREFIX = new byte[]{0x01, 0x71, (byte) 0xa0, (byte) 0xe4, 0x02, 0x20};
/**
* @param message 交易结构体的序列化字节
* 通过交易结构体字节获取CidHash
*/
public static byte[] getCidHash(byte[] message) {
Blake2b.Param param = new Blake2b.Param();
param.setDigestLength(32);
//消息体字节
byte[] messageByte = Blake2b.Digest.newInstance(param).digest(message);
int xlen = CID_PREFIX.length;
int ylen = messageByte.length;
byte[] result = new byte[xlen + ylen];
System.arraycopy(CID_PREFIX, 0, result, 0, xlen);
System.arraycopy(messageByte, 0, result, xlen, ylen);
byte[] prefixByte = Blake2b.Digest.newInstance(param).digest(result);
String prefixByteHex = NumericUtil.bytesToHex(prefixByte);
Log.d(TAG, prefixByteHex);
return prefixByte;
}
4,对cidHash进行签名(签名用的是web3j的签名库):
/**
* @param cidHash 摘要
* 对摘要进行椭圆签名椭圆签名
*/
public static void sign(byte[] cidHash) {
ECKeyPair ecKeyPair = ECKeyPair.create(Numeric.toBigInt("私钥"));
org.web3j.crypto.Sign.SignatureData signatureData = org.web3j.crypto.Sign.signMessage(cidHash,
ecKeyPair, false);
byte[] sig = getSignature(signatureData);
String stringHex = NumericUtil.bytesToHex(sig);
Log.d(TAG, stringHex);
String base64 = Base64.encodeToString(sig, Base64.DEFAULT);
Log.d(TAG, "签名字符串:" + base64);
}
/**
* 获取签名
*
* @param signatureData
* @return
*/
private static byte[] getSignature(org.web3j.crypto.Sign.SignatureData signatureData) {
byte[] sig = new byte[65];
System.arraycopy(signatureData.getR(), 0, sig, 0, 32);
System.arraycopy(signatureData.getS(), 0, sig, 32, 32);
sig[64] = (byte) ((signatureData.getV() & 0xFF) - 27);//为啥减去27看signMessage()方法(内部源码)这面用的web3j的签名库,web3j的签名对recId加了27,所以这面要减去拿到原v
return sig;
}
5,这样就获取到了签名字符串。
三:总结
这样就结束了。
本人也是看rust代码翻译过来的,希望对大家有所帮助。
四:如何获取gasLimit,gasFeeCap,gasPremium?
下篇文章介绍了FileCoin的Gas模型及获取。
网友评论