學(xué)習(xí)一門(mén)知識(shí)、一項(xiàng)技術(shù)都先搞清楚他的概念,才能穩(wěn)固、有目的信心。它是什么,干了什么(處理了什么問(wèn)題)
以太坊是什么?
以太坊(英文Ethereum)是一個(gè)開(kāi)源的有智能合約功能的公共區(qū)塊鏈平臺(tái),通過(guò)其專用加密貨幣以太幣(Ether,簡(jiǎn)稱“ETH”)提供去中心化的以太虛擬機(jī)(Ethereum Virtual Machine)來(lái)處理點(diǎn)對(duì)點(diǎn)合約。
web3j是什么?
??web3j是一種高度模塊化、靈活、安全的Java類庫(kù)和Android類庫(kù),用于處理智能合同,并與以太網(wǎng)絡(luò)中的客戶端(節(jié)點(diǎn))集成??梢酝ㄟ^(guò)它進(jìn)行以太坊區(qū)塊鏈的開(kāi)發(fā),而無(wú)需為你的應(yīng)用平臺(tái)編寫(xiě)集成代碼。
geth
Go-Ethereum簡(jiǎn)稱Geth,用golang語(yǔ)言實(shí)現(xiàn)
按其官方GitHub的說(shuō)法,Geth是以太坊協(xié)議的官方實(shí)現(xiàn)(Official golang implementation of the Ethereum protocol),是以太坊基金會(huì)對(duì)外提供的重要官方軟件之一。
所謂以太坊協(xié)議的實(shí)現(xiàn),個(gè)人理解就是對(duì)以太坊協(xié)議范圍內(nèi)的各項(xiàng)能力進(jìn)行封裝,并以簡(jiǎn)單的形式(命令行、API等)提供給用戶使用,我們可以從兩個(gè)使用角度來(lái)理解:
- Geth可以當(dāng)客戶端來(lái)使用
打開(kāi)Geth,用戶可以創(chuàng)建自己的以太坊私有鏈、管理賬戶、挖礦、交易、部署執(zhí)行智能合約等,用戶還可以下載以太坊主鏈、解析主鏈上任意交易數(shù)據(jù)等。 - Geth可以當(dāng)服務(wù)器來(lái)使用
Geth提供很多服務(wù)和豐富的API,用戶可以開(kāi)發(fā)程序通過(guò)調(diào)用Geth服務(wù),實(shí)現(xiàn)自己想要的功能,比如獲取一段時(shí)間內(nèi)以太幣的所有交易賬戶。
infura
官網(wǎng): https://infura.io/
本地安裝geth的方法需要花比較多的時(shí)間和空間來(lái)同步區(qū)塊,利用infura可以簡(jiǎn)單很多,infura提供公開(kāi)以太坊和測(cè)試節(jié)點(diǎn),可以利用infura提供的api訪問(wèn)以太坊以及IPFS。去官網(wǎng)只需要提供email注冊(cè)得到鏈接即可。
本博客申請(qǐng)到的測(cè)試用例,后面代碼會(huì)用到(測(cè)試用例apikey沒(méi)什么價(jià)值,我在這里貼出來(lái)了)

安裝
Maven
Java 8:
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
持續(xù)更新中...
交易機(jī)制
當(dāng)你用一些以太幣Ether創(chuàng)建了一個(gè)有效的帳戶時(shí),你可以使用兩種機(jī)制來(lái)與以太坊進(jìn)行交易。
- 通過(guò)以太坊ethereum客戶端進(jìn)行認(rèn)證簽名交易
- 離線交易簽名認(rèn)證
這兩種機(jī)制都是Web3j所支持的。
這里我們使用第二種方式,不需要管理以太坊客戶端
離線交易簽名認(rèn)證:
為了離線脫機(jī)交易,你需要有你的錢包文件或與私密錢包/賬戶相關(guān)的公共和私人密鑰。
web3j能夠?yàn)槟闵梢粋€(gè)新的安全的以太坊錢包文件Ethereum wallet file,或者與也可以通過(guò)私鑰來(lái)和現(xiàn)有的錢包文件一起工作。
創(chuàng)建新的錢包文件:
/*************創(chuàng)建一個(gè)錢包文件**************/
private void creatAccount() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
String walletFileName0="";//文件名
String walletFilePath0="D:/my project/mybatis-plus-study/demo-web3j/etc-wallet";
//錢包文件保持路徑,請(qǐng)?zhí)鎿Q位自己的某文件夾路徑
walletFileName0 = WalletUtils.generateNewWalletFile("123456", new File(walletFilePath0), false);
//WalletUtils.generateFullNewWalletFile("password1",new File(walleFilePath1));
//WalletUtils.generateLightNewWalletFile("password2",new File(walleFilePath2));
log.info("walletName: "+walletFileName0);
}
加載憑據(jù)從錢包文件:
/********加載錢包文件**********/
private void loadWallet() throws IOException, CipherException {
// FIXME: 替換為自己的錢包路徑
String walleFilePath = "D:/my project/demo-web3j/etc-wallet/UTC--2020-04-17T09-39-23.188000000Z--51003b4b5ee042480a6d2ee5f08ffd265ab311cf.json";
String passWord = "123456";
credentials = WalletUtils.loadCredentials(passWord, walleFilePath);
String address = credentials.getAddress();
BigInteger publicKey = credentials.getEcKeyPair().getPublicKey();
BigInteger privateKey = credentials.getEcKeyPair().getPrivateKey();
log.info("address=" + address);
log.info("public key=" + publicKey);
log.info("private key=" + privateKey);
}
17:34:39.186 [main] INFO com.yao.eth.util.Wallet - version=Geth/v1.9.8-omnibus-f14759fb-20191127/linux-amd64/go1.13.4
17:34:39.473 [main] INFO com.yao.eth.util.Wallet - address=0x51003b4b5ee042480a6d2ee5f08ffd265ab311cf
17:34:39.473 [main] INFO com.yao.eth.util.Wallet - public key=2252572073522424835987964408234834980696663221134034915707746353465859237410910323885187508316742508641207135834625486912634365340742976768431135181723788
17:34:39.474 [main] INFO com.yao.eth.util.Wallet - private key=84441263344107740006778209316822253467814847306459083060609524447653087837710
至此,錢包的創(chuàng)建和加載已經(jīng)完成,但這一過(guò)程全部發(fā)生在本地,并未同步到以太坊區(qū)塊鏈。查詢地址余額前,需要連接以太坊結(jié)點(diǎn)。
構(gòu)建Web3j實(shí)體,連接以太坊結(jié)點(diǎn)
web3j是連接java端與以太坊的橋梁,廣播交易,查詢賬戶都需要通過(guò)web3j實(shí)體。web3j支持通過(guò)http進(jìn)行構(gòu)建,而且兼容了infura。在本文中,使用的是infura的測(cè)試網(wǎng)絡(luò)Rinkeby。
/*******連接以太坊客戶端**************/
private void conectETHclient() throws IOException {
//連接方式1:使用infura 提供的客戶端
web3j = Web3j.build(new HttpService("https://rinkeby.infura.io/b23bd4d475cd42978a9d29293d7fb317"));// TODO: 2018/4/10 token更改為自己的
//連接方式2:使用本地客戶端
//web3j = Web3j.build(new HttpService("127.0.0.1:7545"));
//測(cè)試是否連接成功
String web3ClientVersion = web3j.web3ClientVersion().send().getWeb3ClientVersion();
log.info("version=" + web3ClientVersion);
}
web3j實(shí)體構(gòu)建完成后,可以打印出版本號(hào)以測(cè)試是否連接成功。如果成功,就可以做其他的事情了。
查詢賬戶余額
/***********查詢指定地址的余額***********/
private void getBlanceOf() throws IOException {
if (web3 == null) return;
String address = "0x51003b4b5ee042480a6d2ee5f08ffd265ab311cf";//等待查詢余額的地址
//第二個(gè)參數(shù):區(qū)塊的參數(shù),建議選最新區(qū)塊
EthGetBalance balance = web3.ethGetBalance(address, DefaultBlockParameter.valueOf("latest")).send();
//格式轉(zhuǎn)化 wei-ether
String blanceETH = Convert.fromWei(balance.getBalance().toString(), Convert.Unit.ETHER).toPlainString().concat(" ether");
log.info("blanceETH = "+ blanceETH);
}
使用錢包轉(zhuǎn)賬
a.在轉(zhuǎn)賬之前我們得獲取一些測(cè)試用的以太幣
首先我們需要使用Twitter、Facebook、Google Plus發(fā)布新的公開(kāi)內(nèi)
容,內(nèi)容是自己的錢包地址,然后粘貼相應(yīng)的文章的地址到faucet.

查看是否到賬和賬戶狀態(tài) 我們可以通過(guò)我們的地址去 explorer:https://www.rinkeby.io/#explorer
這樣我們就有可以操作的ETH進(jìn)行轉(zhuǎn)賬等操作了。
b.使用錢包文件來(lái)發(fā)起交易
try {
Credentials credentials = WalletUtils.loadCredentials(password,filePath);
TransactionReceipt transferReceipt = Transfer.sendFunds(web3j, credentials, to,amount, Convert.Unit.WEI).send();
String hash = transferReceipt.getTransactionHash();
log.info("轉(zhuǎn)賬成功,轉(zhuǎn)賬hash = {}",hash);
} catch (Exception e) {
log.error("轉(zhuǎn)賬失敗,失敗原因{}",e);
}
這種方式相對(duì)安全一些,這里需要注意的是一些ETH的最小單位——WEI.
1ETH = 1*10^18 wei;
提示:轉(zhuǎn)賬這里存在一些gas的問(wèn)題,所謂gas我們都知道是轉(zhuǎn)賬時(shí)需要花費(fèi)的曠工費(fèi)。這里我們可以通過(guò)gasLimit和gasPrice來(lái)調(diào)整,當(dāng)然我們肯定希望gas小,但是gas一味的小,會(huì)導(dǎo)致沒(méi)有人愿意幫我們打包我們得到交易,導(dǎo)致交易最終無(wú)法成交,形成一個(gè)pending的狀態(tài)。如果gas給的多,那么交易成交速度會(huì)比較快,但是我們花費(fèi)的錢就會(huì)變多。
在之前實(shí)現(xiàn)的基礎(chǔ)上,我們可以拿到自己創(chuàng)建的錢包,和對(duì)應(yīng)錢包的文件和密碼。
相關(guān)問(wèn)題
1.ETH交易模塊的特點(diǎn)
- 必須按順序處理事務(wù)(具有1的nonce為1的事務(wù)必須在具有2的nonce的事務(wù)之前處理)
- 不跳過(guò)(具有4的nonce的事務(wù)不能包含在塊中,直到具有1,2,3的nonce的事務(wù)
通過(guò)這種方式,網(wǎng)絡(luò)能夠識(shí)別交易的重復(fù)并強(qiáng)制執(zhí)行訂單(這對(duì)于智能合約至關(guān)重要) 這也就上文中說(shuō)過(guò)的nonce, ### 2.如何查看我們轉(zhuǎn)賬的狀態(tài) 我們可以通過(guò)轉(zhuǎn)賬后拿到的TxHash去https://etherscan.io/查詢。
2.如何取消Pending狀態(tài)轉(zhuǎn)賬
上文說(shuō)到了轉(zhuǎn)賬因?yàn)間as設(shè)置的比較小,可能造成無(wú)法成交,一直處于Pending狀態(tài)。 然后我們可以通過(guò)nonce去取消Pending的轉(zhuǎn)賬, 我們通過(guò)nonce的機(jī)制可以得知,我們只要新創(chuàng)建一筆轉(zhuǎn)賬,其中nonce和Pending狀態(tài)的訂單的nonce一樣就可以替換當(dāng)前Pending狀態(tài)得到轉(zhuǎn)賬。
那么最好的操作來(lái)實(shí)現(xiàn)就是發(fā)起一筆向自己轉(zhuǎn)賬的訂單,當(dāng)然gas我們需要設(shè)置得到>合理。 轉(zhuǎn)賬參考以上兩種方法。
3.取消或者取代轉(zhuǎn)賬
通過(guò)之前2中所提到了,我們可以知道,其實(shí)一個(gè)交易發(fā)出后,被覆蓋,代替,取消等操作的幾率很小。但是也不是不可能。只要當(dāng)前交易不被挖礦和包含在區(qū)塊中,是可以做相關(guān)關(guān)操作的。 具體操作方法還是和3相似,但是切記不可更改nonce.(具體情況還要和實(shí)際相比較,畢竟這種操作實(shí)現(xiàn)的概率幾乎為0)
上述過(guò)程需要翻墻
更新補(bǔ)充,(博主的Facebook和Twitter由于發(fā)送上述消息給封禁了,莫名其妙~)上訴過(guò)程測(cè)試網(wǎng)更改成Ropsten(https://infura.io/
)

這個(gè)端點(diǎn)獲取測(cè)試幣相交簡(jiǎn)單些
所以在構(gòu)建Web3j實(shí)體,連接以太坊結(jié)點(diǎn)時(shí)構(gòu)建web3j客戶端參數(shù)更換一下
web3j = Web3j.build(new HttpService("https://ropsten.infura.io/v3/b23bd4d475cd42978a9d29293d7fb317"));
獲取測(cè)試幣
https://faucet.ropsten.be/
在網(wǎng)站中輸入錢包地址就ok了

查詢交易記錄
https://ropsten.etherscan.io/
同樣是輸入錢包地址

轉(zhuǎn)賬記錄
/****************交易*****************/
private void transto() throws Exception {
if (web3 == null) return;
if (credentials == null) return;
//開(kāi)始發(fā)送0.01 =eth到指定地址
String address_to = "0x41F1dcbC0794BAD5e94c6881E7c04e4F98908a87";
TransactionReceipt send = Transfer.sendFunds(web3, credentials, address_to, BigDecimal.ONE, Convert.Unit.FINNEY).send();
log.info("Transaction complete:");
log.info("trans hash=" + send.getTransactionHash());
log.info("from :" + send.getFrom());
log.info("to:" + send.getTo());
log.info("gas used=" + send.getGasUsed());
log.info("status: " + send.getStatus());
打印結(jié)果:
19:28:37.062 [main] INFO com.yao.eth.util.Wallet - Transaction complete:
19:28:37.062 [main] INFO com.yao.eth.util.Wallet - trans hash=0xa8dd680b1fa7d82693d922f16f554746bba06ec0558daa0be30e6f50ed620071
19:28:37.062 [main] INFO com.yao.eth.util.Wallet - from :0x51003b4b5ee042480a6d2ee5f08ffd265ab311cf
19:28:37.062 [main] INFO com.yao.eth.util.Wallet - to:0x41f1dcbc0794bad5e94c6881e7c04e4f98908a87
19:28:37.062 [main] INFO com.yao.eth.util.Wallet - gas used=21000
19:28:37.062 [main] INFO com.yao.eth.util.Wallet - status: 0x1
把trans hash復(fù)制到框內(nèi),查詢交易記錄詳情:https://ropsten.etherscan.io

