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代码翻译过来的,希望对大家有所帮助。

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