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

com.github.DNAProject.DnaSdk Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2018 The DNA Authors
 * This file is part of The DNA library.
 *
 *  The DNA is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The DNA is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with The DNA.  If not, see .
 *
 */

package com.github.DNAProject;

import com.alibaba.fastjson.JSON;
import com.github.DNAProject.account.Account;
import com.github.DNAProject.common.Address;
import com.github.DNAProject.common.Common;
import com.github.DNAProject.common.ErrorCode;
import com.github.DNAProject.common.Helper;
import com.github.DNAProject.core.DataSignature;
import com.github.DNAProject.core.payload.DeployCode;
import com.github.DNAProject.core.payload.InvokeCode;
import com.github.DNAProject.core.program.Program;
import com.github.DNAProject.core.transaction.Transaction;
import com.github.DNAProject.core.asset.Sig;
import com.github.DNAProject.crypto.Digest;
import com.github.DNAProject.crypto.SignatureScheme;
import com.github.DNAProject.sdk.exception.SDKException;
import com.github.DNAProject.sdk.manager.*;
import com.github.DNAProject.smartcontract.NativeVm;
import com.github.DNAProject.smartcontract.NeoVm;
import com.github.DNAProject.smartcontract.Vm;
import com.github.DNAProject.smartcontract.WasmVm;
import com.github.DNAProject.smartcontract.nativevm.abi.NativeBuildParams;
import com.github.DNAProject.smartcontract.nativevm.abi.Struct;
import com.github.DNAProject.smartcontract.neovm.abi.BuildParams;

import java.io.IOException;
import java.math.BigInteger;
import java.util.*;

/**
 * DNA Sdk
 */
public class DnaSdk {
    private WalletMgr walletMgr;
    private ConnectMgr connRpc;
    private ConnectMgr connRestful;
    private ConnectMgr connWebSocket;
    private ConnectMgr connDefault;
    private  ConnectMgr sideChainConnectMgr;

    private Vm vm = null;
    private NativeVm nativevm = null;
    private NeoVm neovm = null;
    private WasmVm wasmvm = null;
    private SignServer signServer = null;


    private static DnaSdk instance = null;
    public SignatureScheme defaultSignScheme = SignatureScheme.SHA256WITHECDSA;
    public long DEFAULT_GAS_LIMIT = 20000;
    public long DEFAULT_DEPLOY_GAS_LIMIT = 30000000;
    public static synchronized DnaSdk getInstance(){
        if(instance == null){
            instance = new DnaSdk();
        }
        return instance;
    }
    private DnaSdk(){
    }
    public SignServer getSignServer() throws SDKException{
        if(signServer == null){
            throw new SDKException(ErrorCode.OtherError("signServer null"));
        }
        return signServer;
    }
    public NativeVm nativevm() throws SDKException{
        if(nativevm == null){
            vm();
            nativevm = new NativeVm(getInstance());
        }
        return nativevm;
    }
    public NeoVm neovm() {
        if(neovm == null){
            vm();
            neovm = new NeoVm(getInstance());
        }
        return neovm;
    }
    public WasmVm wasmvm() {
        if(wasmvm == null){
            vm();
            wasmvm = new WasmVm(getInstance());
        }
        return wasmvm;
    }

    public Vm vm() {
        if(vm == null){
            vm = new Vm(getInstance());
        }
        return vm;
    }
    public ConnectMgr getRpc() throws SDKException{
        if(connRpc == null){
            throw new SDKException(ErrorCode.ConnRestfulNotInit);
        }
        return connRpc;
    }

    public ConnectMgr getRestful() throws SDKException{
        if(connRestful == null){
            throw new SDKException(ErrorCode.ConnRestfulNotInit);
        }
        return connRestful;
    }
    public ConnectMgr getConnect(){
        if(connDefault != null){
            return connDefault;
        }
        if(connRpc != null){
            return  connRpc;
        }
        if(connRestful != null){
            return  connRestful;
        }
        if(connWebSocket != null){
            return  connWebSocket;
        }
        return null;
    }

    public ConnectMgr getSideChainConnectMgr() {
        return sideChainConnectMgr;
    }

    public void setSideChainRpc(String url) {
        this.sideChainConnectMgr = new ConnectMgr(url, "rpc");
    }
    public void setSideChainRest(String url) {
        this.sideChainConnectMgr = new ConnectMgr(url, "restful");
    }
    public void setSideChainWebsocket(String url,Object lock) {
        this.sideChainConnectMgr = new ConnectMgr(url, "websocket", lock);
    }
    public void setDefaultConnect(ConnectMgr conn){
        connDefault = conn;
    }
    public void setConnectTestNet(){
        try {
            String rpcUrl = "http://polaris1.ont.io:20336";
            getInstance().setRpc(rpcUrl);
            connDefault = getInstance().getRpc();
        } catch (SDKException e) {
            e.printStackTrace();
        }
    }
    public void setConnectMainNet(){
        try {
            String rpcUrl = "http://dappnode1.ont.io:20336";
            getInstance().setRpc(rpcUrl);
            connDefault = getInstance().getRpc();
        } catch (SDKException e) {
            e.printStackTrace();
        }
    }
    public ConnectMgr getWebSocket() throws SDKException{
        if(connWebSocket == null){
            throw new SDKException(ErrorCode.WebsocketNotInit);
        }
        return connWebSocket;
    }


    /**
     * get Wallet Mgr
     * @return
     */
    public WalletMgr getWalletMgr() {
        return walletMgr;
    }


    /**
     *
     * @param scheme
     */
    public void setSignatureScheme(SignatureScheme scheme) {
        defaultSignScheme = scheme;
        walletMgr.setSignatureScheme(scheme);
    }

    public void setSignServer(String url) throws Exception{
        this.signServer = new SignServer(url);
    }
    public void setRpc(String url) {
        this.connRpc = new ConnectMgr(url, "rpc");
    }

    public void setRestful(String url) {
        this.connRestful = new ConnectMgr(url,"restful");
    }

    public void setWesocket(String url,Object lock) {
        connWebSocket = new ConnectMgr(url,"websocket",lock);
    }
    /**
     *
     * @param path
     */
    public void openWalletFile(String path) {

        try {
            this.walletMgr = new WalletMgr(path,defaultSignScheme);
            setSignatureScheme(defaultSignScheme);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param tx
     * @param addr
     * @param password
     * @return
     * @throws Exception
     */
    public Transaction addSign(Transaction tx,String addr,String password,byte[] salt) throws Exception {
        return addSign(tx,getWalletMgr().getAccount(addr,password,salt));
    }
    public Transaction addSign(Transaction tx,Account acct) throws Exception {
        if(tx.sigs == null){
            tx.sigs = new Sig[0];
        } else {
            if (tx.sigs.length >= Common.TX_MAX_SIG_SIZE) {
                throw new SDKException(ErrorCode.ParamErr("the number of transaction signatures should not be over 16"));
            }
        }
        Sig[] sigs = new Sig[tx.sigs.length + 1];
        for(int i= 0; i< tx.sigs.length; i++){
            sigs[i] = tx.sigs[i];
        }
        sigs[tx.sigs.length] = new Sig();
        sigs[tx.sigs.length].M = 1;
        sigs[tx.sigs.length].pubKeys = new byte[1][];
        sigs[tx.sigs.length].sigData = new byte[1][];
        sigs[tx.sigs.length].pubKeys[0] = acct.serializePublicKey();
        sigs[tx.sigs.length].sigData[0] = tx.sign(acct,acct.getSignatureScheme());
        tx.sigs = sigs;
        return tx;
    }

    /**
     *
     * @param tx
     * @param M
     * @param pubKeys
     * @param acct
     * @return
     * @throws Exception
     */
    public Transaction addMultiSign(Transaction tx,int M,byte[][] pubKeys, Account acct) throws Exception {
        addMultiSign(tx,M,pubKeys,tx.sign(acct, acct.getSignatureScheme()));
        return tx;
    }
    public Transaction addMultiSign(Transaction tx,int M,byte[][] pubKeys, byte[] signatureData) throws Exception {
        pubKeys = Program.sortPublicKeys(pubKeys);
        if (tx.sigs == null) {
            tx.sigs = new Sig[0];
        } else {
            if (tx.sigs.length  > Common.TX_MAX_SIG_SIZE || M > pubKeys.length || M <= 0 || signatureData == null || pubKeys == null) {
                throw new SDKException(ErrorCode.ParamError);
            }
            for (int i = 0; i < tx.sigs.length; i++) {
                if(Arrays.deepEquals(tx.sigs[i].pubKeys,pubKeys)){
                    if (tx.sigs[i].sigData.length + 1 > pubKeys.length) {
                        throw new SDKException(ErrorCode.ParamErr("too more sigData"));
                    }
                    if(tx.sigs[i].M != M){
                        throw new SDKException(ErrorCode.ParamErr("M error"));
                    }
                    int len = tx.sigs[i].sigData.length;
                    byte[][] sigData = new byte[len+1][];
                    for (int j = 0; j < tx.sigs[i].sigData.length; j++) {
                        sigData[j] = tx.sigs[i].sigData[j];
                    }
                    sigData[len] = signatureData;
                    tx.sigs[i].sigData = sigData;
                    return tx;
                }
            }
        }
        Sig[] sigs = new Sig[tx.sigs.length + 1];
        for (int i = 0; i < tx.sigs.length; i++) {
            sigs[i] = tx.sigs[i];
        }
        sigs[tx.sigs.length] = new Sig();
        sigs[tx.sigs.length].M = M;
        sigs[tx.sigs.length].pubKeys = pubKeys;
        sigs[tx.sigs.length].sigData = new byte[1][];
        sigs[tx.sigs.length].sigData[0] = signatureData;

        tx.sigs = sigs;
        return tx;
    }
    public Transaction signTx(Transaction tx, String addressOrDnaid, String password,byte[] salt) throws Exception{
        signTx(tx, new Account[][]{{getWalletMgr().getAccount(addressOrDnaid, password,salt)}});
        return tx;
    }
    /**
     * sign tx
     * @param tx
     * @param accounts
     * @return
     */
    public Transaction signTx(Transaction tx, Account[][] accounts) throws Exception{
        if (accounts.length > Common.TX_MAX_SIG_SIZE) {
            throw new SDKException(ErrorCode.ParamErr("the number of transaction signatures should not be over 16"));
        }
        Sig[] sigs = new Sig[accounts.length];
        for (int i = 0; i < accounts.length; i++) {
            sigs[i] = new Sig();
            sigs[i].pubKeys = new byte[accounts[i].length][];
            sigs[i].sigData = new byte[accounts[i].length][];
            for (int j = 0; j < accounts[i].length; j++) {
                sigs[i].M++;
                byte[] signature = tx.sign(accounts[i][j], accounts[i][j].getSignatureScheme());
                sigs[i].pubKeys[j] = accounts[i][j].serializePublicKey();
                sigs[i].sigData[j] = signature;
            }
        }
        tx.sigs = sigs;
        return tx;
    }

    /**
     *  signTx
     * @param tx
     * @param accounts
     * @param M
     * @return
     * @throws SDKException
     */
    public Transaction signTx(Transaction tx, Account[][] accounts, int[] M) throws Exception {
        if (accounts.length > Common.TX_MAX_SIG_SIZE) {
            throw new SDKException(ErrorCode.ParamErr("the number of transaction signatures should not be over 16"));
        }
        if (M.length != accounts.length) {
            throw new SDKException(ErrorCode.ParamError);
        }
        tx = signTx(tx,accounts);
        for (int i = 0; i < tx.sigs.length; i++) {
            if (M[i] > tx.sigs[i].pubKeys.length || M[i] < 0) {
                throw new SDKException(ErrorCode.ParamError);
            }
            tx.sigs[i].M = M[i];
        }
        return tx;
    }

    public byte[] signatureData(Account acct, byte[] data) throws SDKException {
        DataSignature sign = null;
        try {
            data = Digest.sha256(Digest.sha256(data));
            sign = new DataSignature(defaultSignScheme, acct, data);
            return sign.signature();
        } catch (Exception e) {
            throw new SDKException(e);
        }
    }

    public boolean verifySignature(byte[] pubkey, byte[] data, byte[] signature) throws SDKException {
        DataSignature sign = null;
        try {
            sign = new DataSignature();
            data = Digest.sha256(Digest.sha256(data));
            return sign.verifySignature(new Account(false, pubkey), data, signature);
        } catch (Exception e) {
            throw new SDKException(e);
        }
    }
    public boolean verifyTransaction(Transaction tx) {
        try {
            boolean result = true;
            for (int i = 0; i < tx.sigs.length; i++) {
                if (tx.sigs[i].M == 1) {
                    if (tx.sigs[i].pubKeys.length != 1 || tx.sigs[i].sigData.length != 1) {
                        throw new SDKException(ErrorCode.OtherError("index" + i + "pubKeys or sigData number != 1"));
                    }
                    Account account = new Account(false, tx.sigs[i].pubKeys[0]);
                    boolean verify = account.verifySignature(Digest.hash256(tx.getHashData()), tx.sigs[i].sigData[0]);
                    if (!verify) {
                        return false;
                    }
                } else if (tx.sigs[i].M > 1) {
                    int m = 0;
                    for (int j = 0; j < tx.sigs[i].pubKeys.length; j++) {
                        Account account = new Account(false, tx.sigs[i].pubKeys[j]);
                        for (int k = 0; k < tx.sigs[i].sigData.length; k++) {
                            boolean verify = account.verifySignature(Digest.hash256(tx.getHashData()), tx.sigs[i].sigData[k]);
                            if (verify) {
                                m++;
                                break;
                            }
                        }
                    }
                    if (m < tx.sigs[i].M) {
                        return false;
                    }
                }
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    private void buildMap(Map map,Object ele){
        try {
            for(Map.Entry e:((Map) ele).entrySet()) {
                Object tmp = e.getValue();
                if(tmp instanceof String){
                    String pre = ((String) tmp).substring(0,10);
                    if(pre.contains("String")) {
                        String data = ((String) tmp).replace("String:","");
                        e.setValue(data.getBytes());
                    }else if(pre.contains("ByteArray")) {
                        String data = ((String) tmp).replace("ByteArray:","");
                        e.setValue(Helper.hexToBytes(data));
                    }else if(pre.contains("Long")) {
                        String data = ((String) tmp).replace("Long:","");
                        e.setValue(data);
                    }else if(pre.contains("Address")) {
                        String data = ((String) tmp).replace("Address:","");
                        e.setValue(Address.decodeBase58(data).toArray());
                    } else {
                        throw new Exception(ErrorCode.OtherError("String type data error: "+ e));
                    }
                }else if(tmp instanceof Map){
                    Map data = new HashMap();
                    buildMap(data, tmp);
                    e.setValue(data);
                }
                map.put(e.getKey(),e.getValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void buildArgs(List args,Object ele){
        try {
            if(ele instanceof Boolean){
                args.add(ele);
            }else if(ele instanceof Long){
                args.add(ele);
            } else if(ele instanceof Integer){
                args.add(ele);
            } else if(ele instanceof Map){
                Map map = new HashMap();
                buildMap(map,ele);
                args.add(map);
            } else if(ele instanceof String){
                if(((String) ele).substring(0,5).contains("Long:")) {
                    String data = ((String) ele).replace("Long:","");
                    args.add(new BigInteger(data).longValue());
                }else if(((String) ele).substring(0,7).contains("String:")) {
                    String data = ((String) ele).replace("String:","");
                    args.add(data.getBytes());
                }else if(((String) ele).substring(0,8).contains("Address:")) {
                    String data = ((String) ele).replace("Address:","");
                    args.add(Address.decodeBase58(data).toArray());
                }else if(((String) ele).substring(0,10).contains("ByteArray:")) {
                    String data = ((String) ele).replace("ByteArray:","");
                    args.add(Helper.hexToBytes(data));
                }else {
                    throw new Exception(ErrorCode.OtherError("String type data error: "+ele));
                }
            } else if(ele instanceof List){
                List tmp = new ArrayList();
                for (int i = 0; i < ((List)ele).size(); i++) {
                    buildArgs(tmp, ((List) ele).get(i));
                }
                args.add(tmp);
            } else{
                throw new Exception(ErrorCode.OtherError("type not found"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public List[] buildInvokeFunctionByJson(String configStr) {
        try {
            Map config = (Map) JSON.parseObject(configStr);
            List functions = ((List)config.get("functions"));
            List[] paramLists = new List[functions.size()];
            for(int i =0;i < functions.size();i++) {
                Map func = (Map)functions.get(i);
                String operation = (String) func.get("operation");
                List args = (List) func.get("args");
                List paramList = new ArrayList<>();
                paramList.add(operation.getBytes());
                List args2 = new ArrayList();
                for (int j = 0; j < args.size(); j++) {
                    Object ele = ((Map) args.get(j)).get("value");
                    buildArgs(args2, ele);
                }
                paramList.add(args2);
                paramLists[i] = paramList;
            }
            return paramLists;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private Transaction makeNativeTransaction(String contractHash, List paramList, String payer, long gasLimit, long gasPrice) {
        try {
            String GAS = "0200000000000000000000000000000000000000";
            String DNAID = "0300000000000000000000000000000000000000";
            String method = new String((byte[]) paramList.get(0));
            List list = new ArrayList();
            Struct struct = new Struct();
            struct.list = (List) paramList.get(1);
            if (contractHash.equals(GAS)) {
                List listStruct = new ArrayList();
                listStruct.add(struct);
                if(method.equals("transfer")) {
                    list.add(listStruct);
                }else{
                    list.add(struct);
                }
            }else if(contractHash.equals(DNAID)){
                if(method.equals("getDDO")){
                    list.add(struct.list.get(0));
                }else {
                    list.add(struct);
                }
            }
            byte[] args = NativeBuildParams.createCodeParamsScript(list);
            return vm().buildNativeParams(new Address(Helper.hexToBytes(Helper.reverse(contractHash))), method, args, payer, gasLimit, gasPrice);
        } catch (SDKException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Transaction[] makeTransactionByJson(String str) {
        Map map = JSON.parseObject(str);
        Map config = null;
        try {
            String action = ((String) map.get("action"));
            if (!action.equals("invoke") && !action.equals("invokeRead") && !action.equals("invokePasswordFree")) {
                throw new Exception(ErrorCode.OtherError("not found action is invoke or invokeRead or invokePasswordFree"));
            }
            config = (Map) ((Map) map.get("params")).get("invokeConfig");
            String payer = (String) config.get("payer");
            long gasLimit = (int) config.get("gasLimit");
            long gasPrice = (int) config.get("gasPrice");
            String contractHash = (String) config.get("contractHash");

            List[] paramList = buildInvokeFunctionByJson(JSON.toJSONString(config));
            Transaction[] txs = new Transaction[paramList.length];
            for(int i=0;i< paramList.length;i++) {
                if(contractHash.contains("00000000000000000000000000000000000000")){
                    txs[i] = makeNativeTransaction(contractHash,paramList[i],payer, gasLimit, gasPrice);
                } else {
                    byte[] params = BuildParams.createCodeParamsScript(paramList[i]);
                    txs[i] = vm().makeInvokeCodeTransaction(Helper.reverse(contractHash), null, params, payer, gasLimit, gasPrice);
                }
            }
            return txs;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Map parseTransaction(String txhexstr) throws SDKException {
        Map map = new HashMap();
        try {
            Transaction tx = Transaction.deserializeFrom(Helper.hexToBytes(txhexstr));
            if (tx instanceof DeployCode) {
                map.put("txType", "deploy");
                map.put("author", ((DeployCode) tx).author);
                map.put("email", ((DeployCode) tx).email);
                map.put("version", ((DeployCode) tx).version);
                map.put("description", ((DeployCode) tx).description);
                map.put("name", ((DeployCode) tx).name);
            } else if (tx instanceof InvokeCode) {
                map.put("txType", "invoke");
                byte[] code = ((InvokeCode) tx).code;
                String codeHexStr = Helper.toHexString(code);
                if (codeHexStr.length() > 44 && codeHexStr.substring(codeHexStr.length() - 44, codeHexStr.length()).equals(Helper.toHexString(Vm.NATIVE_INVOKE_NAME.getBytes()))) {
                    if (codeHexStr.substring(codeHexStr.length() - 92 - 16, codeHexStr.length() - 92).equals(Helper.toHexString("transfer".getBytes()))) {
                        map.put("method", "transfer");
                        map.put("from", Address.parse(codeHexStr.substring(8, 48)).toBase58());
                        map.put("to", Address.parse(codeHexStr.substring(56, 96)).toBase58());
                        map.put("amount", new BigInteger("00"));
                        if (codeHexStr.substring(102, 103).equals("5")) {
                            map.put("amount", code[51] - 0x50);
                        } else {
                            map.put("amount", Helper.BigIntFromNeoBytes(Helper.hexToBytes(codeHexStr.substring(104, 104 + code[51] * 2))));
                        }
                        if (codeHexStr.substring(codeHexStr.length() - 50 - 40, codeHexStr.length() - 50).equals(this.nativevm().gas().getContractAddress())) {
                            map.put("asset", "gas");
                            map.put("amount", ((BigInteger) map.get("amount")).doubleValue() / 1000000000L);
                        }
                    } else if (codeHexStr.substring(codeHexStr.length() - 92 - 24, codeHexStr.length() - 92).equals(Helper.toHexString("transferFrom".getBytes()))) {
                        map.put("method", "transferFrom");
                        map.put("from", Address.parse(codeHexStr.substring(8, 48)).toBase58());
                        map.put("to", Address.parse(codeHexStr.substring(56, 96)).toBase58());
                        map.put("amount", new BigInteger("00"));
                        if (codeHexStr.substring(102, 103).equals("5")) {
                            map.put("amount", code[51] - 0x50);
                        } else {
                            map.put("amount", Helper.BigIntFromNeoBytes(Helper.hexToBytes(codeHexStr.substring(104, 104 + code[51] * 2))));
                        }
                        if (codeHexStr.substring(codeHexStr.length() - 50 - 40, codeHexStr.length() - 50).equals(this.nativevm().gas().getContractAddress())) {
                            map.put("asset", "gas");
                            map.put("amount", ((BigInteger) map.get("amount")).doubleValue() / 1000000000L);
                        }
                    }
                }
            } else {
                throw new SDKException(ErrorCode.OtherError("tx type error"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy