美文网首页
Filecoin发送交易

Filecoin发送交易

作者: MrLgc | 来源:发表于2020-08-07 14:42 被阅读0次

    一:前言

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

    相关文章

      网友评论

          本文标题:Filecoin发送交易

          本文链接:https://www.haomeiwen.com/subject/gsyfdktx.html