Filecoin交易

一:前言

由於目前現在還沒有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代碼翻譯過來的,希望對大家有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章