一:前言
由于目前現(xiàn)在還沒(méi)有java的開(kāi)源filecoin交易的代碼,這面只做參考荐吵,目前是可以簽名并發(fā)送交易成功的错洁。
參考rust代碼。
https://github.com/Zondax/filecoin-signing-tools
大家可以先看下這個(gè)代碼疙剑。
代碼鏈接:
https://github.com/codeFarmL/FileCoin/tree/master
目前對(duì)于rust轉(zhuǎn)變java代碼只有一個(gè)細(xì)節(jié)是不確定的
給大家看下代碼:
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);
也就是字節(jié)補(bǔ)0這氯迂,我一直看rust代碼沒(méi)看懂,這面我如果不做這個(gè)判斷言缤,形成的value字節(jié)總會(huì)少個(gè)0嚼蚀,這樣就和rust代碼的value字節(jié)對(duì)不上,所以這面我做了判斷管挟,如果轉(zhuǎn)換的value字節(jié)首位不是0則補(bǔ)0轿曙,是0則不補(bǔ)0,這面感覺(jué)會(huì)有問(wèn)題僻孝,
但是我試了20多筆都是可以發(fā)起交易的导帝。
大家先看交易。
二:交易流程:
用到的庫(kù):
ove.blake2b-alpha.0.jar (hash算法)
implementation 'co.nstant.in:cbor:0.9'(結(jié)構(gòu)體形成字節(jié))
implementation 'org.web3j:core:4.2.0'(簽名)
1穿铆,構(gòu)建交易結(jié)構(gòu)體:
生成業(yè)務(wù)的交易對(duì)象
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個(gè)fil
交易的參數(shù):
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;
}
交易的序列化結(jié)構(gòu)體
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; //空數(shù)組
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;
}
}
通過(guò)交易參數(shù)構(gòu)建序列化結(jié)構(gòu)體
public static UnsignedMessage try_from(UnsignedMessageAPI unsignedMessageAPI) {
//構(gòu)建交易結(jié)構(gòu)體
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; //應(yīng)該是字節(jié) 截取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,因?yàn)槭荢ecp256k1的標(biāo)識(shí)就是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荞雏,構(gòu)建完交易體之后生成交易字節(jié)
/**
* @param unsignedMessageAPI
*/
public static void transaction_serialize(UnsignedMessageAPI unsignedMessageAPI) {
/**
* 拼接UnsignedMessage對(duì)象
* 這面用的是CborEncoder
* 問(wèn)題:什么是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 交易結(jié)構(gòu)體的序列化字節(jié)
* 通過(guò)交易結(jié)構(gòu)體字節(jié)獲取CidHash
*/
public static byte[] getCidHash(byte[] message) {
Blake2b.Param param = new Blake2b.Param();
param.setDigestLength(32);
//消息體字節(jié)
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,對(duì)cidHash進(jìn)行簽名(簽名用的是web3j的簽名庫(kù)):
/**
* @param cidHash 摘要
* 對(duì)摘要進(jìn)行橢圓簽名橢圓簽名
*/
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()方法(內(nèi)部源碼)這面用的web3j的簽名庫(kù)虐秦,web3j的簽名對(duì)recId加了27,所以這面要減去拿到原v
return sig;
}
5讯檐,這樣就獲取到了簽名字符串羡疗。
三:總結(jié)
這樣就結(jié)束了。
本人也是看rust代碼翻譯過(guò)來(lái)的别洪,希望對(duì)大家有所幫助叨恨。
四:如何獲取gasLimit,gasFeeCap,gasPremium?
下篇文章介紹了FileCoin的Gas模型及獲取。