1. 程式人生 > >web3j實現離線發起交易

web3j實現離線發起交易

1 使用web3j發起交易的倆種方式

使用web3j封裝的json-RPC方法來訪問geth客戶端,並且向geth端發起交易,可以有倆種方式方式來發起交易的方法。方式1,使用者解鎖用的keyStore檔案儲存在geth節點上,使用者使用解鎖賬戶的方式發起交易,這種方式適用於中心化的方式比如交易所。方式2,使用者的keyStore檔案存在本地,使用者使用錢包檔案給交易簽名的方式來發起交易,這種方式適用於DApp方式比如imToken等。

2 使用解鎖方式發起交易

解鎖方式發起交易,使用者的錢包檔案存在geth節點,發起交易時要先使用密碼解鎖賬戶,然後再發起交易。使用該方式來轉賬以太坊的函式可以寫成:

    //以太坊轉賬
    //from:轉出方賬戶
    //password:轉出方密碼
    //addrTo:收款賬戶
    //value:轉賬額
    public  String transferEth(String from,String password,String to,BigInteger value) throws  Exception
    {
        EthGetTransactionCount ethGetTransactionCount = ethClient.ethGetTransactionCount(
                from, DefaultBlockParameterName.LATEST).sendAsync().get();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
       PersonalUnlockAccount personalUnlockAccount = ethClient.personalUnlockAccount(from,password).send();
       if (personalUnlockAccount.accountUnlocked())//解鎖賬戶,需要錢包檔案存在要訪問的geth節點上
        {
            BigInteger gasPrice = Contract.GAS_PRICE;
            //BigInteger gasPrice = new BigInteger("12000000000000");
            BigInteger gasLimit = new BigInteger("90000");//Contract.GAS_LIMIT.divide(new BigInteger("2"));
            synchronized(TestLocal.class) {
                Transaction transaction = Transaction.createEtherTransaction(from,nonce,gasPrice,gasLimit,to,value);
                EthSendTransaction transactionResponse = ethClient.ethSendTransaction(transaction).sendAsync().get();;
                if(transactionResponse.hasError()){
                    String message=transactionResponse.getError().getMessage();
                    System.out.println("transaction failed,info:"+message);
                    Utils.writeFile("F:/testErr.txt","transaction failed,info:"+message);
                    return message;
                }else{
                    String hash=transactionResponse.getTransactionHash();
                    EthGetTransactionReceipt send = ethClient.ethGetTransactionReceipt(hash).send();
                    System.out.println("transaction from "+from+" to "+to+" amount:"+value);
                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Utils.writeFile("F:/log1.txt","transaction from "+from+" to "+to+" amount:"+value+" time:"+df.format(new Date()));

                    //use this code to get gasUsed and gasPrice
                    BigInteger gas_quility = ethClient.ethEstimateGas(transaction).send().getAmountUsed();
                    BigInteger gas_price = ethClient.ethGasPrice().send().getGasPrice();
                    BigInteger bgasUsed = gas_quility.multiply(gas_price);
                    BigDecimal gasUsed = Convert.fromWei(bgasUsed.toString(),Convert.Unit.ETHER);
                    System.out.println("transaction gas_quility:"+gas_quility+",gas_price:"+gas_price+",gasUsed:"+gasUsed);
                    return  hash;
                }
            }

        }
        return null;
    }

使用該方式來發起智慧合約轉賬的方式:

    //呼叫Test智慧合約的演算法函式來批量轉賬以太幣
    //fromAddress:轉出方地址 toAddressList:轉入方地址列表
    //scoresList:分數列表  sum:分母   amount:分配額度,轉出金額=ratio*amount/sum
    //password:fromAddress的密碼   coinTypeAddress:合約地址
    public String multiTransferEther_Test(List<Address> toAddressList, List<Uint256> moneyList,BigInteger sum,String fromAddress,String password, String coinTypeAddress) throws  Exception{
        EthGetTransactionCount ethGetTransactionCount = ethClient.ethGetTransactionCount(
                fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        if(gNoce == null)
            gNoce = nonce;
        BigInteger gasPrice = Contract.GAS_PRICE;
        //BigInteger gasLimit = Contract.GAS_LIMIT.divide(new BigInteger("5"));
        BigInteger gasLimit = new BigInteger("90000");
                List<Type> inputParameters = new ArrayList<>();
        inputParameters.add(new DynamicArray(toAddressList));
        inputParameters.add(new DynamicArray(moneyList));
        inputParameters.add(new Uint256(sum));

        Function function = new Function("batchTransferEther",
                inputParameters,
                Collections.<TypeReference<?>>emptyList());
        String functionEncoder = FunctionEncoder.encode(function);
        PersonalUnlockAccount personalUnlockAccount = ethClient.personalUnlockAccount(fromAddress,password).send();
        if (personalUnlockAccount.accountUnlocked()) {//解鎖賬戶,需要錢包檔案存在要訪問的geth節點
            // send a transaction
            Transaction transaction = Transaction.createFunctionCallTransaction(
                    fromAddress, gNoce, gasPrice,
                    gasLimit, coinTypeAddress, new BigInteger("0"),
                    functionEncoder);
            gNoce = gNoce.add(new BigInteger("1"));
            EthSendTransaction transactionResponse =
                    ethClient.ethSendTransaction(transaction).sendAsync().get();
            if(transactionResponse.hasError()){
                String message=transactionResponse.getError().getMessage();
                System.out.println("transaction failed,info:"+message);
                return message;
            }else{
                String hash=transactionResponse.getTransactionHash();
                return  hash;
            }
        }
        return  null;
    }

3 使用錢包檔案簽名的方式發起交易

使用這種方式需要錢包檔案儲存在呼叫端本地,先載入錢包檔案來生成錢包許可物件,然後使用錢包許可物件來對只讀型交易進行簽名。使用該方式發起以太坊轉賬的交易如下:

   //本地呼叫keystore檔案轉賬方式
    public void localSendEther(String from,String password,String keyStore,String to,BigInteger value) throws Exception
    {
        //載入本地KeyStore檔案生成Credentials物件
        Credentials credentials = WalletUtils.loadCredentials(password,keyStore);
        EthGetTransactionCount ethGetTransactionCount = ethClient.ethGetTransactionCount(
                from, DefaultBlockParameterName.LATEST).sendAsync().get();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        BigInteger gasPrice = new BigInteger("120000000000");
        BigInteger gasLimit = Contract.GAS_LIMIT.divide(new BigInteger("2"));
        //生成RawTransaction交易物件
        RawTransaction rawTransaction  = RawTransaction.createTransaction(nonce,gasPrice,gasLimit,to,value,"abcde123");//可以額外帶資料
        //使用Credentials物件對RawTransaction物件進行簽名
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction,credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        EthSendTransaction ethSendTransaction = ethClient.ethSendRawTransaction(hexValue).sendAsync().get();
        String transactionHash = ethSendTransaction.getTransactionHash();
        if(ethSendTransaction.hasError())
        {
            String message=ethSendTransaction.getError().getMessage();
            System.out.println("transaction failed,info:"+message);
            Utils.writeFile("F:/testErr.txt","transaction failed,info:"+message);
        }
        else
        {
            String hash=ethSendTransaction.getTransactionHash();
            EthGetTransactionReceipt send = ethClient.ethGetTransactionReceipt(hash).send();
            System.out.println("transaction from "+from+" to "+to+" amount:"+value);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Utils.writeFile("F:/log1.txt","transaction from "+from+" to "+to+" amount:"+value+" time:"+df.format(new Date()));
        }
    }

轉賬以太坊還有一種不用設定Nonce的簡單方式:

    //本地呼叫keystore檔案轉賬方式,不用設定Nonce和交易費用,相當於geth客戶端的eth.sendTransaction
    public String localTransferEther(String password,String keyStore,String to,BigInteger value) throws Exception
    {
        Credentials credentials = WalletUtils.loadCredentials(password,keyStore);
        try{
            TransactionReceipt transactionReceipt = Transfer.sendFunds(ethClient, credentials, to,
                    new BigDecimal(value), Convert.Unit.WEI).send();
            return transactionReceipt.getTransactionHash();
        }catch (Exception e)//轉賬失敗會列印錯誤資訊
        {
            System.out.println(e);
            return e.toString();
        }
    }

對智慧合約轉賬進行簽名的函式:

   //在本地呼叫智慧合約實現批量轉賬
    public void multiTransferEtherLocal(List<Address> toAddressList, List<Uint256> scoresList, BigInteger sum,String from,String keyStore,String password, String contractAddress) throws  Exception
    {
        Credentials credentials = WalletUtils.loadCredentials(password,keyStore);
        EthGetTransactionCount ethGetTransactionCount = ethClient.ethGetTransactionCount(
                from, DefaultBlockParameterName.LATEST).sendAsync().get();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();
        //BigInteger gasPrice = Contract.GAS_PRICE;
        BigInteger gasPrice = new BigInteger("12000000000000");
        BigInteger gasLimit = new BigInteger("90000");

        List<Type> inputParameters = new ArrayList<>();
        inputParameters.add(new DynamicArray(toAddressList));
        inputParameters.add(new DynamicArray(scoresList));
        inputParameters.add(new Uint256(sum));

        Function function = new Function("batchTransferEther",
                inputParameters,
                Collections.<TypeReference<?>>emptyList());
        String functionEncoder = FunctionEncoder.encode(function);
        
        RawTransaction transaction = RawTransaction.createTransaction(nonce,gasPrice,gasLimit,contractAddress,functionEncoder);
        byte[] signedMessage = TransactionEncoder.signMessage(transaction,credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        EthSendTransaction ethSendTransaction = ethClient.ethSendRawTransaction(hexValue).sendAsync().get();
        String transactionHash = ethSendTransaction.getTransactionHash();
        if(ethSendTransaction.hasError())
        {
            String message=ethSendTransaction.getError().getMessage();
            System.out.println("transaction failed,info:"+message);
            Utils.writeFile("F:/testErr.txt","transaction failed,info:"+message);
        }
        else
        {
            String hash=ethSendTransaction.getTransactionHash();
            EthGetTransactionReceipt send = ethClient.ethGetTransactionReceipt(hash).send();
            System.out.println("multiTransferEtherLocal send success");
        }
    }