Filecoin发送交易

一:前言

由于目前现在还没有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模型及获取。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351