java和android如何使用web3j開發(fā)以太坊智能合約并進(jìn)行交易教程

從廣義上講,有web3j支持三種類型的以太坊交易:

  • 1.以太幣從一方交易到另一方
  • 2.創(chuàng)建一個(gè)智能合約
  • 3.與智能合約交易

為了進(jìn)行這些交易,必須有以太幣(以太坊區(qū)塊鏈的代幣)存在于交易發(fā)生的以太坊賬戶中。這是為了支付gas成本,這是為支付參與交易的以太坊客戶端的交易執(zhí)行成本,支付了這個(gè)成本就能將結(jié)果提交到以太坊區(qū)塊鏈上。獲得以太幣的說明下文會(huì)說到。

此外,我們還可以查詢智能合約的狀態(tài)。

image

如何獲得以太幣Ether

要想獲得以太幣Ether你有兩種途徑可以選擇:

  • 1.自己開采挖礦
  • 2.從別人那里獲取以太幣

在私有鏈中自己挖礦,或者公共測試鏈(testnet)是非常簡單直接的。但是,在主要的公有鏈(mainnet)中,它需要很多很明顯的專用GPU時(shí)間,除非你已經(jīng)擁有多個(gè)專用GPU的礦機(jī),否則基本上不太可行。如果你希望使用私有鏈,則在這個(gè)官方文檔中有一些指導(dǎo)。

要購買以太幣Ether,你需要通過交易所。由于不同的地區(qū)有不同的交易所,你還需要研究自己去哪兒合適。官方文檔中包含多個(gè)交易所,是一個(gè)很好的參考。

以太坊測試鏈(testnets)

針對Ethereum以太坊有許多專用測試網(wǎng)絡(luò)或者叫測試鏈,他們由各種客戶端支持。

  • 1.Rinkeby:只支持geth客戶端。
  • 2.Kovan:只支持Parity客戶端。
  • 3.Ropsten:支持geth和Parity客戶端。

對于開發(fā),建議你使用RinkebyKoVan測試鏈。這是因?yàn)樗麄兪褂玫墓ぷ髁孔C明POA共識機(jī)制,確保交易和塊能夠一致并及時(shí)的創(chuàng)建。Ropsten測試鏈,雖然最接近公有鏈(Mainnet),但是因?yàn)樗褂玫墓ぷ髁孔C明是POW共識機(jī)制,過去已受到攻擊,對以太坊開發(fā)人員來說往往有更多的問題。

你可以通過Rinkeby測試鏈的Rinkeby Crypto Fauce請求以太坊幣,具體怎么做可以看這里https://www.rinkeby.io/。

有關(guān)如何請求Kovan測試鏈的細(xì)節(jié)可以在這里找到。

如果你需要在Ropsten上的得到一些以太幣,將你的錢包地址的消息發(fā)布到web3j gitter channel,然后會(huì)發(fā)送一些給你。

在testnet測試鏈或者私有鏈上挖掘

在ethereum以太坊測試鏈testnet中,挖掘難度低于公有鏈mainnet。這意味著你可以用普通的CPU,比如你的筆記本電腦來挖掘新的以太幣。你需要做的是運(yùn)行一個(gè)以太坊客戶端,例如gethParity,開始做一些儲備。進(jìn)一步的資料可在他們的官方網(wǎng)站上獲得。

一旦你開采了一些以太幣,你就可以開始使用以太坊區(qū)塊鏈了。

然而,如上所述,使用Kovan或者Rinkeby測試網(wǎng)絡(luò)更簡單些。

gas

當(dāng)在Ethereum以太坊發(fā)生交易時(shí),必須為執(zhí)行該交易的客戶端支付交易成本,將該交易的輸出提交到以太坊區(qū)塊鏈Ethereum blockchain。

此成本是通過gas來測量的,其中g(shù)as是用于在以太坊虛擬機(jī)中執(zhí)行交易指令的數(shù)量。請參閱官方文檔以獲取更多信息。

當(dāng)你使用以太坊客戶端時(shí),這意味著,有兩個(gè)參數(shù)用來指示你希望花費(fèi)多少以太來完成傳輸:

  • gas price :氣體價(jià)格,這是每單位gas中以太的消耗量。Web3j使用的默認(rèn)價(jià)格為22000000000 wei(22×10-8 Ether)。這是在交易管理中定義的。
  • gas limit:氣體最大量,這是你愿意在交易執(zhí)行上花費(fèi)的gas的最大總量。單個(gè)交易在一個(gè)以太坊區(qū)塊中有多大的上限,通常將該值限制為小于6700000。當(dāng)前的gas限制在這里查https://ethstats.net/。

這兩個(gè)參數(shù)共同決定了你愿意花費(fèi)在交易成本上的最大量的以太幣Ether。也就是說,你花費(fèi)的gas不會(huì)超過gas price * gas limit。gas價(jià)格也會(huì)影響交易發(fā)生的速度,這取決于其他交易是否能為礦工提供更有利的gas價(jià)格。

你可能需要調(diào)整這些參數(shù)以確保交易能及時(shí)進(jìn)行。

交易機(jī)制

當(dāng)你用一些以太幣Ether創(chuàng)建了一個(gè)有效的帳戶時(shí),你可以使用兩種機(jī)制來與以太坊進(jìn)行交易。

  • 通過以太坊ethereum客戶端進(jìn)行認(rèn)證簽名交易
  • 離線交易簽名認(rèn)證

這兩種機(jī)制都是Web3j所支持的。

通過以太坊ethereum客戶端進(jìn)行認(rèn)證簽名交易

為了通過以太坊客戶端進(jìn)行交易,首先需要確保你正在使用的客戶端知道你的錢包地址。最好是運(yùn)行自己的以太坊客戶端,比如geth/Parity,以便可以更方便的做到這一點(diǎn)。一旦你有一個(gè)客戶端運(yùn)行,你可以創(chuàng)建一個(gè)以太坊錢包,通過:

  • geth Wiki包含了geth支持的良好運(yùn)行的不同機(jī)制,例如導(dǎo)入私有密鑰文件,并通過控制臺創(chuàng)建新的以太坊帳戶。
  • 或者,你可以通過客戶端使用JSON-RPC管理命令,例如用personal_newAccountgeth/Parity創(chuàng)建新以太坊賬戶。

通過創(chuàng)建你的錢包文件,你可以通過web3j打開帳戶,首先創(chuàng)建支持geth/Parity管理命令的web3j實(shí)例:

Admin web3j = Admin.build(new HttpService());

然后,你可以解鎖帳戶,并如果是成功的,就可以發(fā)送一個(gè)交易:

PersonalUnlockAccount personalUnlockAccount = web3j.personalUnlockAccount("0x000...", "a password").send();
if (personalUnlockAccount.accountUnlocked()) {
    // send a transaction
}

以這種方式發(fā)送的交易應(yīng)該通過EthSendTransaction創(chuàng)建,使用Transaction類型:

Transaction transaction = Transaction.createContractTransaction(
              <from address>,
              <nonce>,
              BigInteger.valueOf(<gas price>),  // we use default gas limit
              "0x...<smart contract code to execute>"
      );

      org.web3j.protocol.core.methods.response.EthSendTransaction
              transactionResponse = parity.ethSendTransaction(ethSendTransaction)
              .send();

      String transactionHash = transactionResponse.getTransactionHash();

      // poll for transaction response via org.web3j.protocol.Web3j.ethGetTransactionReceipt(<txHash>)

其中nonce值獲得方式,下文會(huì)提到。
有關(guān)此交易工作流的詳細(xì)信息,請參閱 DeployContractITScenario。

web3j支持的各種管理命令的進(jìn)一步細(xì)節(jié)在Management APIs中。

離線交易簽名認(rèn)證Offline transaction signing

如果你不想管理自己的以太坊客戶端,或者不想向以太坊客戶端提供諸如密碼之類的錢包詳細(xì)信息,那么就通過離線交易認(rèn)證簽名。

離線交易簽名認(rèn)證允許你在web3j中使用你的以太坊錢包簽署交易,允許你完全控制你的私有憑據(jù)。然后,離線創(chuàng)建的交易可以被發(fā)送到網(wǎng)絡(luò)上的任何以太坊客戶端,只要它是一個(gè)有效的交易,它會(huì)將交易傳播到其他節(jié)點(diǎn)。

如果需要,還可以執(zhí)行進(jìn)程外交易簽名認(rèn)證。這可以通過重寫ECKeyPairsign方法來實(shí)現(xiàn)。

創(chuàng)建和使用錢包文件Ethereum wallet file

為了離線脫機(jī)交易,你需要有你的錢包文件或與私密錢包/賬戶相關(guān)的公共和私人密鑰。

web3j能夠?yàn)槟闵梢粋€(gè)新的安全的以太坊錢包文件Ethereum wallet file,或者與也可以通過私鑰來和現(xiàn)有的錢包文件一起工作。

創(chuàng)建新的錢包文件:

String fileName = WalletUtils.generateNewWalletFile(
        "your password",
        new File("/path/to/destination"));

加載憑據(jù)從錢包文件:

Credentials credentials = WalletUtils.loadCredentials(
        "your password",
        "/path/to/walletfile");

然后這些憑據(jù)會(huì)被用來簽署交易,請參閱Web3安全存儲定義錢包文件規(guī)范Web3 Secret Storage Definition

簽署以太坊交易

要使脫機(jī)簽名交易得到簽署,需要設(shè)定一個(gè)RawTransaction類型。RawTransaction類似于前面提到的Transaction類型,但是它不需要通過具體的賬號地址來請求,因?yàn)榭梢詮暮灻型茢喑鰜怼?/p>

為了創(chuàng)建和簽署原生交易,交易的順序如下:

  • 1.確定交易發(fā)起者帳戶的下一個(gè)可用隨機(jī)數(shù)nonce
  • 2.創(chuàng)建RawTransaction對象
  • 3.使用遞歸長度前綴編碼(RLP即Recursive Length Prefix)對RawTransaction對象進(jìn)行編碼
  • 4.簽署RawTransaction對象
  • 5.將RawTransaction對象發(fā)送到節(jié)點(diǎn)進(jìn)行處理

nonce是一個(gè)不斷增長的數(shù)值,用來唯一地標(biāo)識交易。一個(gè)nonce只能使用一次,直到交易被挖掘完成,可以以相同的隨機(jī)數(shù)發(fā)送交易的多個(gè)版本,但是一旦其中一個(gè)被挖掘完成,其他后續(xù)提交的都將被拒絕。

一旦獲得下一個(gè)可用的nonce,該值就可以用來創(chuàng)建transaction對象:

RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             nonce, <gas price>, <gas limit>, <toAddress>, <value>);

然后可以對交易進(jìn)行簽名和編碼:

byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, <credentials>);
String hexValue = Numeric.toHexString(signedMessage);

其中憑據(jù)是根據(jù)創(chuàng)建和使用錢包文件加載的。

然后使用eth_SendRawTransaction發(fā)送交易:

EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
String transactionHash = ethSendTransaction.getTransactionHash();
// poll for transaction response via org.web3j.protocol.Web3j.ethGetTransactionReceipt(<txHash>)

有關(guān)創(chuàng)建和發(fā)送原始事務(wù)的完整示例,請參閱 CreateRawTransactionIT。

交易隨機(jī)數(shù)nonce

nonce是一個(gè)不斷增長的數(shù)值,用來唯一地標(biāo)識交易。一個(gè)nonce只能使用一次,直到交易被挖掘完成,可以以相同的隨機(jī)數(shù)發(fā)送交易的多個(gè)版本,但是一旦其中一個(gè)被挖掘完成,其他后續(xù)提交的都將被拒絕。

可以通過eth_getTransactionCount方法獲得下一個(gè)可用的nonce

EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
             address, DefaultBlockParameterName.LATEST).sendAsync().get();

     BigInteger nonce = ethGetTransactionCount.getTransactionCount();

然后可以使用nonce創(chuàng)建你的交易對象:

RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             nonce, <gas price>, <gas limit>, <toAddress>, <value>);

交易類型

web3j中的不同類型的交易都使用TransactionRawTransaction對象。關(guān)鍵的區(qū)別是交易對象必須始終有一個(gè)地址,以便處理eth_sendTransaction請求的以太坊客戶端知道要使用哪個(gè)錢包來代表消息發(fā)送者并發(fā)送該交易。如上所述,對于離線簽名認(rèn)證簽署的原始交易而言,這不是必須的。

接下來的部分概述了不同交易類型所需的關(guān)鍵交易屬性。下列屬性對所有人都是不變:

  • Gas price 天然氣氣體價(jià)格
  • Gas limit 天然氣氣體限制
  • Nonce 隨機(jī)數(shù)
  • from 發(fā)送地址

TransactionRawTransaction對象在所有后續(xù)示例中都可互換使用。

以太幣從一方交易到另一方

在雙方之間發(fā)送以太幣Ether需要交易對象的最少量的信息:

  • to :目的地錢包地址
  • value:價(jià)值,希望發(fā)送到目的地的以太幣數(shù)量
BigInteger value = Convert.toWei("1.0", Convert.Unit.ETHER).toBigInteger();
RawTransaction rawTransaction  = RawTransaction.createEtherTransaction(
             <nonce>, <gas price>, <gas limit>, <toAddress>, value);
// send...

但是,建議你使用TransferClass來發(fā)送以太幣Ether,它負(fù)責(zé)對nonce管理和通過不斷的輪詢?yōu)槟闾峁╉憫?yīng):

Web3j web3 = Web3j.build(new HttpService());  // defaults to http://localhost:8545/
Credentials credentials = WalletUtils.loadCredentials("password", "/path/to/walletfile");
TransactionReceipt transactionReceipt = Transfer.sendFunds(
        web3, credentials, "0x<address>|<ensName>",
        BigDecimal.valueOf(1.0), Convert.Unit.ETHER).send();

使用智能合約打包器smart contract wrappers

當(dāng)使用下面列出的智能合約打包器時(shí),將不得不手動(dòng)執(zhí)行從Solidity到本機(jī)Java類型的所有轉(zhuǎn)換。使用Solidity smart contract wrappers是非常有效的,它負(fù)責(zé)所有的代碼生成和轉(zhuǎn)換。

創(chuàng)建一個(gè)智能合約

要部署新的智能合約,需要提供以下屬性:

  • value :在智能合約中希望存放的以太坊Ether量(如果沒有提供默認(rèn)為零)
  • data :十六進(jìn)制格式化、編譯的智能合約創(chuàng)建代碼
// using a raw transaction
RawTransaction rawTransaction = RawTransaction.createContractTransaction(
        <nonce>,
        <gasPrice>,
        <gasLimit>,
        <value>,
        "0x <compiled smart contract code>");
// send...

// get contract address
EthGetTransactionReceipt transactionReceipt =
             web3j.ethGetTransactionReceipt(transactionHash).send();

if (transactionReceipt.getTransactionReceipt.isPresent()) {
    String contractAddress = transactionReceipt.get().getContractAddress();
} else {
    // try again
}

如果智能合約包含構(gòu)造函數(shù),則必須對關(guān)聯(lián)的構(gòu)造函數(shù)字段值進(jìn)行編碼,并將其附加到編譯的智能合約代碼中compiled smart contract code

String encodedConstructor =
             FunctionEncoder.encodeConstructor(Arrays.asList(new Type(value), ...));

// using a regular transaction
Transaction transaction = Transaction.createContractTransaction(
        <fromAddress>,
        <nonce>,
        <gasPrice>,
        <gasLimit>,
        <value>,
        "0x <compiled smart contract code>" + encodedConstructor);

// send...

與智能合約交易

要與現(xiàn)有的智能合約進(jìn)行交易,需要提供以下屬性:

  • to:智能合同地址
  • value:在智能合約中你希望存放的以太幣Ether量(如果智能合約接受以太幣Ether的話)
  • data: 已編碼的函數(shù)選擇器和自變量參數(shù)

web3j負(fù)責(zé)函數(shù)編碼,有關(guān)實(shí)現(xiàn)的進(jìn)一步細(xì)節(jié),請參閱應(yīng)用程序二進(jìn)制接口部分Application Binary Interface。

Function function = new Function<>(
             "functionName",  // function we're calling
             Arrays.asList(new Type(value), ...),  // Parameters to pass as Solidity Types
             Arrays.asList(new TypeReference<Type>() {}, ...));

String encodedFunction = FunctionEncoder.encode(function)
Transaction transaction = Transaction.createFunctionCallTransaction(
             <from>, <gasPrice>, <gasLimit>, contractAddress, <funds>, encodedFunction);

org.web3j.protocol.core.methods.response.EthSendTransaction transactionResponse =
             web3j.ethSendTransaction(transaction).sendAsync().get();

String transactionHash = transactionResponse.getTransactionHash();

// wait for response using EthGetTransactionReceipt...

無論消息簽名的返回類型如何,都不可能從事務(wù)性函數(shù)調(diào)用返回值。但是,使用過濾器捕獲函數(shù)返回的值是可能的。詳情請參閱過濾器和事件部分。

查詢智能合約狀態(tài)

這種功能是由eth_call通過JSON-RPC調(diào)用來實(shí)現(xiàn)的。

eth_call允許你調(diào)用智能合約上的方法來查詢某個(gè)值。此函數(shù)沒有關(guān)聯(lián)交易成本,這是因?yàn)樗桓淖內(nèi)魏沃悄芎霞s方法的狀態(tài),它只返回它們的值:

Function function = new Function<>(
             "functionName",
             Arrays.asList(new Type(value)),  // Solidity Types in smart contract functions
             Arrays.asList(new TypeReference<Type>() {}, ...));

String encodedFunction = FunctionEncoder.encode(function)
org.web3j.protocol.core.methods.response.EthCall response = web3j.ethCall(
             Transaction.createEthCallTransaction(<from>, contractAddress, encodedFunction),
             DefaultBlockParameterName.LATEST)
             .sendAsync().get();

List<Type> someTypes = FunctionReturnDecoder.decode(
             response.getValue(), function.getOutputParameters());

注意:如果一個(gè)無效的函數(shù)調(diào)用被執(zhí)行,或者得到一個(gè)空null返回結(jié)果時(shí),返回值將是一個(gè)Collections.emptyList實(shí)例。

  • web3j教程,主要是針對java和android程序員進(jìn)行區(qū)塊鏈以太坊開發(fā)的web3j開發(fā)詳解。
  • 以太坊教程,主要介紹智能合約與dapp應(yīng)用開發(fā),適合入門。
  • 以太坊開發(fā),主要是介紹使用node.js、mongodb、區(qū)塊鏈、ipfs實(shí)現(xiàn)去中心化電商DApp實(shí)戰(zhàn),適合進(jìn)階。

匯智網(wǎng)原創(chuàng)翻譯,原文訪問這里

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容