All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.hyperchain.sdk.common.utils.Decoder Maven / Gradle / Ivy

There is a newer version: 1.4.3
Show newest version
package cn.hyperchain.sdk.common.utils;

import cn.hyperchain.sdk.bvm.OperationResult;
import cn.hyperchain.sdk.bvm.Result;
import cn.hyperchain.sdk.common.adapter.StringNullAdapter;
import cn.hyperchain.sdk.common.protos.ProposalOuterClass;
import cn.hyperchain.sdk.kvsqlutil.Chunk;
import cn.hyperchain.sdk.kvsqlutil.Column;
import cn.hyperchain.sdk.kvsqlutil.IntegerDataType;
import cn.hyperchain.sdk.kvsqlutil.KVSQLField;
import com.google.common.io.ByteSource;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.LinkedHashSet;

public class Decoder {
    private static final int KVSQL_DECODEVERSION1 = 0;

    private static int defaultLen = 4;
    private static final Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .registerTypeAdapter(String.class, new StringNullAdapter())
            .create();

    /**
     * decode hvm receipt result to specific type.
     *
     * @param encode receipt result
     * @param clazz  specific type, if clazz is {@link String}, return directly
     * @param     clazz type
     * @return result
     */
    public static  T decodeHVM(String encode, Class clazz) {
        if (String.class.equals(clazz)) {
            return (T) ByteUtil.decodeHex(encode);
        }
        return gson.fromJson(ByteUtil.decodeHex(encode), clazz);
    }

    /**
     * decode hvm payload.
     *
     * @param payload invoke hvm payload or invoke directly hvm payload
     * @return HVMPayload
     */
    public static HVMPayload decodeHVMPayload(String payload) throws IOException {
        if (payload.startsWith("0x" + InvokeDirectlyParams.ParamBuilder.MAGIC) || payload.startsWith(InvokeDirectlyParams.ParamBuilder.MAGIC)) {
            return decodeHVMPayloadInvokeDirectly(payload);
        } else {
            return decodeHVMPayloadInvoke(payload);
        }
    }

    /**
     * decode invoke hvm payload.
     *
     * @param payload in: | class length(4B) | name length(2B) | class | class name | bin |
     * @return HVMPayload
     */
    private static HVMPayload decodeHVMPayloadInvoke(String payload) throws IOException {
        byte[] payloadBytes = ByteUtil.fromHex(payload);
        int classLen = ByteUtil.bytesToInteger(ByteUtil.copy(payloadBytes, 0, 4));
        int nameLen = ByteUtil.bytesToInteger(ByteUtil.copy(payloadBytes, 4, 2));
        byte[] classBytes = ByteUtil.copy(payloadBytes, 6, classLen);
        byte[] name = ByteUtil.copy(payloadBytes, 6 + classLen, nameLen);
        byte[] bin = ByteUtil.copy(payloadBytes, 6 + classLen + nameLen, payloadBytes.length - 6 - classLen - nameLen);

        Set methodNames = new LinkedHashSet<>();
        InputStream is = ByteSource.wrap(classBytes).openStream();
        ClassReader reader = new ClassReader(is);
        ClassNode classNode = new ClassNode();
        reader.accept(classNode, 0);
        for (MethodNode mn : classNode.methods) {
            if (mn.name.equalsIgnoreCase("invoke") && Type.getReturnType(mn.desc).toString().indexOf("Object") == -1) {
                Type[] argumentTypes = Type.getArgumentTypes(mn.desc);
                InsnList instructions = mn.instructions;
                for (ListIterator lan = instructions.iterator(); lan.hasNext(); ) {
                    AbstractInsnNode cur = lan.next();
                    if (cur instanceof MethodInsnNode) {
                        for (Type t : argumentTypes) {
                            if (t.toString().indexOf(((MethodInsnNode) cur).owner) != -1) {
                                methodNames.add(((MethodInsnNode) cur).name);
                            }
                        }
                    }
                }
            }
        }
        String invokeBeanName = new String(name, Utils.DEFAULT_CHARSET);
        String invokeArgs = new String(bin, Utils.DEFAULT_CHARSET);
        return new HVMPayload(invokeBeanName, invokeArgs, methodNames);
    }

    static class InvokeParam {
        private final String name;
        private final String type;
        private final String value;

        public InvokeParam(String name, String type, String value) {
            this.name = name;
            this.type = type;
            this.value = value;
        }
    }

    /**
     * decode invoke directly hvm payload.
     *
     * @param payload in: | methodName length(2B) | methodName | params |
     * @return HVMPayload
     */
    private static HVMPayload decodeHVMPayloadInvokeDirectly(String payload) {
        byte[] payloadBytes = ByteUtil.fromHex(payload);
        int nameLen = ByteUtil.bytesToInteger(ByteUtil.copy(payloadBytes, 4, 2));
        byte[] name = ByteUtil.copy(payloadBytes, 6, nameLen);
        int begin = 6 + nameLen;
        List invokeArgs = new ArrayList<>();
        while (begin < payloadBytes.length) {
            int paramTypeLen = ByteUtil.bytesToInteger(ByteUtil.copy(payloadBytes, begin, 2));
            int paramLen = ByteUtil.bytesToInteger(ByteUtil.copy(payloadBytes, begin + 2, 4));
            String paramType = new String(ByteUtil.copy(payloadBytes, begin + 6, paramTypeLen));
            String param = new String(ByteUtil.copy(payloadBytes, begin + 6 + paramTypeLen, paramLen));
            begin = begin + 6 + paramTypeLen + paramLen;
            invokeArgs.add(new InvokeParam(null, paramType, param));
        }
        Set invokeMethodsName = new HashSet<>();
        invokeMethodsName.add(new String(name, Utils.DEFAULT_CHARSET));
        return new HVMPayload("", gson.toJson(invokeArgs), invokeMethodsName);
    }

    /**
     * decode bvm receipt result to bvm.Result.
     *
     * @param encode receipt result
     * @return {@link Result}
     */
    public static Result decodeBVM(String encode) {
        return gson.fromJson(new String(ByteUtil.fromHex(encode)), Result.class);
    }

    /**
     * decode ret in bvm.Result to bvm.OperationResult list.
     *
     * @param resultRet the list of bvm.OperationResult
     * @return {@link List}
     */
    public static List decodeBVMResult(String resultRet) {
        List list = gson.fromJson(resultRet, new TypeToken>() {
        }.getType());
        if (list != null) {
            for (OperationResult op : list) {
                if (op.getMsg() == null) {
                    op.setMsg("");
                }
            }
        } else {
            list = new ArrayList<>();
        }
        return list;
    }

    /**
     * decodeKVSQL receipt result to kvsql.
     *
     * @param encode receipt result
     * @return {@link Chunk}
     */
    public static Chunk decodeKVSQL(String encode) {
        Buffer buffer = new Buffer(ByteUtil.fromHex(encode));
        int decodeVersion = (int) buffer.readInteger(IntegerDataType.INT1);
        switch (decodeVersion) {
            case KVSQL_DECODEVERSION1:
                return decodeVersion1(buffer);
            default:
                throw new RuntimeException("UNKNOW decode Version");
        }
    }

    private static Chunk decodeVersion1(Buffer resultPacket) {
        long columnCount = resultPacket.readInteger(IntegerDataType.INT4);
        if (columnCount == 0) {
            return buildResultSetForUpdate(resultPacket);
        } else {
            return buildResultSetForQuery((int) columnCount, resultPacket);
        }
    }

    private static Chunk buildResultSetForUpdate(Buffer resultPacket) {
        long updateCount = resultPacket.readInteger(IntegerDataType.INT4);
        long lastInsertID = resultPacket.readInteger(IntegerDataType.INT8);
        return new Chunk(updateCount, lastInsertID);
    }

    private static Chunk buildResultSetForQuery(int columnCount, Buffer resultPacket) {
        KVSQLField[] fields = new KVSQLField[columnCount];
        Column[] columns = new Column[columnCount];
        for (int i = 0; i < columnCount; i++) {
            fields[i] = unPackField(resultPacket);
        }

        int rowLength = (int) resultPacket.readInteger(IntegerDataType.INT4);

        for (int i = 0; i < columnCount; i++) {
            columns[i] = unPackColumn(resultPacket);
        }

        Chunk chunk = new Chunk(rowLength, fields, columns);

        return chunk;
    }

    private static final KVSQLField unPackField(Buffer packet) {
        String tableName = new String(packet.readLenByteArray());
        String originalTableName = new String(packet.readLenByteArray());
        String columnName = new String(packet.readLenByteArray());
        String originalColumnName;
        byte[] res = packet.readLenByteArray();
        if (res == null) {
            originalColumnName = null;
        } else {
            originalColumnName = new String(res);
        }

        short collationIndex = (short) packet.readInteger(IntegerDataType.INT2);
        long colLength = packet.readInteger(IntegerDataType.INT4);
        int colType = (int) packet.readInteger(IntegerDataType.INT1);
        short colFlag = (short) packet.readInteger(IntegerDataType.INT2);
        int colDecimals = (int) packet.readInteger(IntegerDataType.INT1);
        return new KVSQLField(tableName, originalTableName, columnName, originalColumnName, colLength, colType, colFlag, colDecimals, collationIndex);
    }

    private static Column unPackColumn(Buffer packet) {
        byte[] data = packet.readLenByteArray();
        byte[] nullBitmap = packet.readLenByteArray();

        int len = (int) packet.readInteger(IntegerDataType.INT_LENENC);
        int[] offsets = new int[len];
        for (int i = 0; i < len; i++) {
            offsets[i] = (int) packet.readInteger(IntegerDataType.INT4);
        }
        return new Column(data, nullBitmap, offsets);
    }

    /**
     * decodeProposalLog decode proposal from MQLog.Data of proposalContract.
     *
     * @param data MQLog.Data
     * @return {@link cn.hyperchain.sdk.common.protos.ProposalOuterClass.Proposal}
     */
    public static ProposalOuterClass.Proposal decodeProposalLog(String data) throws InvalidProtocolBufferException {
        ProposalOuterClass.Proposal proposal = ProposalOuterClass.Proposal.parseFrom(ByteUtil.fromHex(data));
        return proposal.toBuilder().setCode(decodeProposalContent(proposal.getCode().toByteArray())).build();
    }

    private static ByteString decodeProposalContent(byte[] code) {
        int operateLen = ByteUtil.bytesToInteger(ByteUtil.copy(code, 0, defaultLen));
        int index = defaultLen;
        Object[] content = new Object[operateLen];
        for (int i = 0; i < operateLen; i++) {
            int methodNameLen = ByteUtil.bytesToInteger(ByteUtil.copy(code, index, defaultLen));
            index += defaultLen;
            String methodName = new String(ByteUtil.copy(code, index, methodNameLen));
            index += methodNameLen;
            int paramCount = ByteUtil.bytesToInteger(ByteUtil.copy(code, index, defaultLen));
            index += defaultLen;
            String[] params = new String[paramCount];
            for (int j = 0; j < paramCount; j++) {
                int paramLen = ByteUtil.bytesToInteger(ByteUtil.copy(code, index, defaultLen));
                index += defaultLen;
                String param = new String(ByteUtil.copy(code, index, paramLen));
                index += paramLen;
                params[j] = param;
            }
            Map opt = new LinkedHashMap<>();
            opt.put("MethodName", methodName);
            opt.put("Params", params);
            content[i] = opt;
        }

        return ByteString.copyFromUtf8(gson.toJson(content));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy