一:前言
由於目前現在還沒有java的開源filecoin交易的代碼,這面只做參考,目前是可以簽名併發送交易成功的。
參考rust代碼。
https://github.com/Zondax/filecoin-signing-tools
大家可以先看下這個代碼。
目前對於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("t1b6esg2ynyuiwawowyzv3ioc7chboesp4ja6ewfi");
unsignedMessageAPI.setTo("t1wlsdn2phpzczyalerizzrrhhpqjlsn67an54thq");
unsignedMessageAPI.setNonce(12);
unsignedMessageAPI.setValue("1000000000000000000");
unsignedMessageAPI.setGas_price("1000");
unsignedMessageAPI.setGas_limit(1000);
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 String gas_price;
private long gas_limit;
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 getGas_price() {
return gas_price;
}
public void setGas_price(String gas_price) {
this.gas_price = gas_price;
}
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 gas_price;
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 getGas_price() {
return gas_price;
}
public void setGas_price(ByteString gas_price) {
this.gas_price = gas_price;
}
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);
co.nstant.in.cbor.model.ByteString gasPriceString = null;
if (new BigInteger(unsignedMessageAPI.getGas_price()).toByteArray()[0] != 0) {
byte[] byte2 = new byte[new BigInteger(unsignedMessageAPI.getGas_price()).toByteArray().length + 1];
byte2[0] = 0;
System.arraycopy(new BigInteger(unsignedMessageAPI.getGas_price()).toByteArray(), 0, byte2, 1, new BigInteger(unsignedMessageAPI.getGas_price()).toByteArray().length);
gasPriceString = new co.nstant.in.cbor.model
.ByteString(byte2);
} else {
gasPriceString = new co.nstant.in.cbor.model
.ByteString(new BigInteger(unsignedMessageAPI.getGas_price()).toByteArray());
}
unsignedMessage.setGas_price(gasPriceString);
unsignedMessage.setGas_limit(new UnsignedInteger(unsignedMessageAPI.getGas_limit()));
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 = "t";
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_price())
.add(unsignedMessage.getGas_limit())
.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代碼翻譯過來的,希望對大家有所幫助。