文章是本人學(xué)習(xí)過程翻譯帅霜,原文來自官方文檔:https://web3j.readthedocs.io/en/latest/#
官網(wǎng):https://web3j.io/
官方GitHub:https://github.com/web3j/web3j
官方demo:https://github.com/web3j/web3j/tree/master/integration-tests
文檔版本v3.4.0赎瑰。
一般來說羊瘩,在以太坊支持三種類型的交易:
- 交易以太幣
- 創(chuàng)建智能合約
- 發(fā)起交易到智能合約
要進(jìn)行這些交易俩檬,需要消耗gas要糊,如果你只是查詢合約的狀態(tài)則不需要gas.
獲取以太幣(Obtaining Ether)
有兩種獲取以太幣的方法:
- 挖礦
- 跟別人購買以太幣
以太坊測(cè)試網(wǎng)(Ethereum testnets)
下面列出的是Ethereum的各種測(cè)試網(wǎng)絡(luò)纲熏,支持各自的客戶端:
- Rinkeby (Geth only)
- Kovan (Parity only)
- Ropsten (Geth and Parity)
對(duì)于開發(fā)者,推薦使用Rinkeby/Kovan測(cè)試網(wǎng)絡(luò)锄俄,因?yàn)樗鼈兪褂肞OA共識(shí)機(jī)制局劲,可以確保交易和區(qū)塊可以及時(shí)被確認(rèn)打包。
Ropsten測(cè)試網(wǎng)絡(luò)很接近Mainet主網(wǎng)奶赠,同樣使用POW共識(shí)機(jī)制鱼填,過去曾經(jīng)被攻擊過,使用它的開發(fā)者可能會(huì)遇到很多問題毅戈。
在Rinkeby網(wǎng)絡(luò)獲取以太幣苹丸,請(qǐng)看here.
在Kovan網(wǎng)絡(luò)獲取以太幣,請(qǐng)看here
在Ropsten網(wǎng)絡(luò)獲取以太幣苇经,你需要把你的錢包地址發(fā)送到web3j Gitter channel
在測(cè)試網(wǎng)/私有網(wǎng)挖礦(Mining on testnet/private blockchains)
在測(cè)試網(wǎng)/私有網(wǎng)挖礦沒有主網(wǎng)那么高難度赘理,只需要一臺(tái)安裝客戶端節(jié)點(diǎn)的普通的電腦。
- Geth - https://github.com/ethereum/go-ethereum/wiki/Mining
- Parity - https://github.com/paritytech/parity/wiki/Mining
當(dāng)你通過挖礦獲得以太幣后扇单,你就可以發(fā)起交易了商模。
Gas
要進(jìn)行交易,需要發(fā)起的賬戶花費(fèi)一些gas蜘澜,把交易的結(jié)果提交到以太坊的區(qū)塊上阻桅。你需要指定兩個(gè)參數(shù),讓客戶端節(jié)點(diǎn)在處理交易時(shí)兼都,知道你希望花費(fèi)多少以太幣來完成交易嫂沉。
Gas price gas價(jià)格
web3j使用默認(rèn)價(jià)格22,000,000,000 Wei (22 x 10-8 Ether).這個(gè)默認(rèn)值定義在ManagedTransaction
Gas limit 最高gas
這個(gè)值一般要小于6,700,000, 可以在 https://ethstats.net/ 查看當(dāng)前的gas limit.
這兩個(gè)參數(shù)的設(shè)置會(huì)影響到交易被處理的速度扮碧,您可能需要調(diào)整這些參數(shù),以確保及時(shí)交易發(fā)生趟章。
發(fā)起交易查詢當(dāng)前gas price:
Transfer transfer = new Transfer(web3j, transactionManager);
BigInteger gasPrice = transfer.requestCurrentGasPrice();
交易機(jī)制(Transaction mechanisms)
當(dāng)你創(chuàng)建了一個(gè)擁有以太幣的賬戶后杏糙,你可以通過以下兩種交易機(jī)制,和以太坊網(wǎng)絡(luò)(私網(wǎng)/公網(wǎng))交易:
- 通過以太坊客戶端簽名交易 - Transaction signing via an Ethereum client
- 線下簽名交易 - Offline transaction signing
通過以太坊客戶端簽名交易
通過客戶端交易蚓土,首先你要啟動(dòng)客戶端(geth宏侍、parity),然后使用客戶端來創(chuàng)建你的錢包賬戶:
- Geth的控制臺(tái)支持導(dǎo)入一個(gè)已經(jīng)存在的私鑰文件蜀漆,或者創(chuàng)建一個(gè)新的賬戶谅河。
- Geth的控制臺(tái)提供了json-rpc的命令來讓你管理geth,比如 _personal_newAccount _ 來創(chuàng)建一個(gè)新的賬戶确丢。
創(chuàng)建了賬戶后绷耍,你就可以使用web3j來連接客戶端(ipc/http),這不需要提供秘鑰鲜侥,只需要保證客戶端可以連接褂始,連接后可以解鎖賬戶、發(fā)起交易描函,代碼如下:
Admin web3j = Admin.build(new WindowsIpcService("\\\\.\\pipe\\geth.ipc"));
PersonalUnlockAccount personalUnlockAccount = web3j
.personalUnlockAccount("0x053b2252a10356ec0ce1cfc587b909beee591409", "111").send();
System.out.println(personalUnlockAccount.accountUnlocked());
EthGetTransactionCount ethGetTransactionCount = web3j
.ethGetTransactionCount("0x053b2252a10356ec0ce1cfc587b909beee591409", DefaultBlockParameterName.LATEST)
.send();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();
Transaction transaction = Transaction.createEtherTransaction("0x053b2252a10356ec0ce1cfc587b909beee591409",
nonce, GAS_PRICE, GAS_LIMIT, "0xc7d9fffaf663c5dfa31b096164cc843c01d0797a", BigInteger.valueOf(20L));
org.web3j.protocol.core.methods.response.EthSendTransaction transactionResponse = web3j
.ethSendTransaction(transaction).send();
String transactionHash = transactionResponse.getTransactionHash();
System.out.println(transactionHash);
更多實(shí)例請(qǐng)看 DeployContractIT和它的父類 Scenario崎苗。
線下交易簽名(Offline transaction signing)
如果你不想管理你的客戶端節(jié)點(diǎn),或者不想把提供錢包密碼給客戶端節(jié)點(diǎn)舀寓,線下交易簽名比較適合你胆数。
線下交易簽名允許你使用web3j提供的錢包賬戶發(fā)起交易,你完全控制自己的私鑰互墓,交易發(fā)送到網(wǎng)絡(luò)上的其它節(jié)點(diǎn)并傳播幅慌。
通過覆蓋簽名方法ECKeyPair,來發(fā)起交易簽名轰豆。
ECKeyPair:橢圓曲線算法生成秘鑰對(duì)(Elliptic Curve SECP-256k1 generated key pair)
為了實(shí)現(xiàn)線下交易胰伍,你需要使用web3j生成安全的錢包賬戶,并這個(gè)賬戶交易酸休。
創(chuàng)建錢包文件:
String fileName = WalletUtils.generateNewWalletFile(
"your password",
new File("/path/to/destination"));
加載錢包文件:
Credentials credentials = WalletUtils.loadCredentials(
"your password",
"/path/to/walletfile");
credentials 用來簽名交易骂租。
查看完整的錢包文件規(guī)范:Web3 Secret Storage Definition
交易簽名
線下交易使用 RawTransaction 對(duì)象來完成,一共有如下幾步:
- 確定交易賬戶的下一個(gè) nonce 值
- 創(chuàng)建 RawTransaction 對(duì)象
- 使用 RLP 編碼 RawTransaction 對(duì)象
- 簽名 RawTransaction 對(duì)象
- 發(fā)送 RawTransaction 對(duì)象給節(jié)點(diǎn)處理斑司。
交易nonce
以太坊實(shí)戰(zhàn)-再談nonce使用陷阱:https://blog.csdn.net/wo541075754/article/details/79054937
- nonce 值用來唯一標(biāo)識(shí)賬戶的交易渗饮,一個(gè)值只能使用一次
- nonce 值可以通過eth_getTransactionCount方法獲取
- 如果一個(gè)賬戶使用相同的 nonce通過發(fā)起多個(gè)交易,只有一個(gè)交易會(huì)被接受宿刮,(gas price)手續(xù)費(fèi)高的會(huì)覆蓋手續(xù)費(fèi)低的交易互站,如果交易費(fèi)一樣,后發(fā)起的交易會(huì)被拒絕僵缺。
- Transaction的 nonce 值可以為空胡桃,不指定 nonce 值由客戶端自動(dòng)排序賦值
- RawTransaction的 nonce 值不能為空
Admin web3j = Admin.build(new HttpService()); // defaults to http://localhost:8545/
Credentials credentials = WalletUtils.loadCredentials("111", "E:\\develop\\geth\\data_dev\\keystore\\UTC--2018-05-05T06-50-18.813015000Z--053b2252a10356ec0ce1cfc587b909beee591409");
BigInteger value = Convert.toWei("5", Convert.Unit.ETHER).toBigInteger();
// get the next available nonce
EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
"0x053b2252a10356ec0ce1cfc587b909beee591409", DefaultBlockParameterName.LATEST).send();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();
// create our transaction
RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
nonce, GAS_PRICE, GAS_LIMIT, "0xc7d9fffaf663c5dfa31b096164cc843c01d0797a", value);
// sign & send our transaction
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
String hexValue = Numeric.toHexString(signedMessage);
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
System.out.println(ethSendTransaction.getTransactionHash());
交易類型(Transaction types)
兩種交易類型:
- Transaction
- RawTransaction
不管哪種交易類型,都需要下面的參數(shù):
- Gas price
- Gas limit
- Nonce
- From
從一個(gè)賬戶發(fā)送以太幣到另外一個(gè)賬戶
RawTransaction
BigInteger value = Convert.toWei("1.0", Convert.Unit.ETHER).toBigInteger();
RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
<nonce>, <gas price>, <gas limit>, <toAddress>, value);
Transfer
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();
與智能合約交互
在于智能合約交互時(shí)磕潮,你必須執(zhí)行所有的手動(dòng)轉(zhuǎn)換從solidity類型到本地Java類型翠胰。慶幸的時(shí)容贝,使用web3j的Solidity smart contract wrappers ,它可以幫你完成這些轉(zhuǎn)換之景,你可以很方便地使用斤富。
- 創(chuàng)建智能合約,有兩個(gè)屬性:
- value - 你希望存到智能合約的以太幣
- 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ù)锻狗,需要提供構(gòu)造參數(shù)满力,必須編碼這些參數(shù)并拼到合約代碼后面:
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...
- 發(fā)起交易到智能合約
與存在的智能合約交互,需要提供以下屬性:
- to - 智能合約地址
- value - 你希望存到智能合約的以太幣
- data - 編碼后的調(diào)用的函數(shù)和參數(shù)
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...
交易調(diào)用不可能直接得到返回值轻纪,要獲取返回值油额,必須用Filters and Events 。
更多關(guān)于函數(shù)編碼的介紹桐磁,請(qǐng)看Application Binary Interface
- 查詢智能合約的狀態(tài)
可以通過eth_call這個(gè)JSON-RPC call接口來查詢合約狀態(tài)悔耘,這個(gè)調(diào)用不用花費(fèi)gas讲岁,因?yàn)樗鼪]有改變賬戶的狀態(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)用,或一個(gè)空的結(jié)果,返回值將Collections.emptyList的實(shí)例()