学习一门知识、一项技术都先搞清楚他的概念,才能稳固、有目的信心。它是什么,干了什么(处理了什么问题)
以太坊是什么?
以太坊(英文Ethereum)是一个开源的有智能合约功能的公共区块链平台,通过其专用加密货币以太币(Ether,简称“ETH”)提供去中心化的以太虚拟机(Ethereum Virtual Machine)来处理点对点合约。
web3j是什么?
web3j是一种高度模块化、灵活、安全的Java类库和Android类库,用于处理智能合同,并与以太网络中的客户端(节点)集成。可以通过它进行以太坊区块链的开发,而无需为你的应用平台编写集成代码。
geth
Go-Ethereum简称Geth,用golang语言实现
按其官方GitHub的说法,Geth是以太坊协议的官方实现(Official golang implementation of the Ethereum protocol),是以太坊基金会对外提供的重要官方软件之一。
所谓以太坊协议的实现,个人理解就是对以太坊协议范围内的各项能力进行封装,并以简单的形式(命令行、API等)提供给用户使用,我们可以从两个使用角度来理解:
- Geth可以当客户端来使用
打开Geth,用户可以创建自己的以太坊私有链、管理账户、挖矿、交易、部署执行智能合约等,用户还可以下载以太坊主链、解析主链上任意交易数据等。 - Geth可以当服务器来使用
Geth提供很多服务和丰富的API,用户可以开发程序通过调用Geth服务,实现自己想要的功能,比如获取一段时间内以太币的所有交易账户。
infura
官网: https://infura.io/
本地安装geth的方法需要花比较多的时间和空间来同步区块,利用infura可以简单很多,infura提供公开以太坊和测试节点,可以利用infura提供的api访问以太坊以及IPFS。去官网只需要提供email注册得到链接即可。
本博客申请到的测试用例,后面代码会用到(测试用例apikey没什么价值,我在这里贴出来了)
安装
Maven
Java 8:
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
持续更新中...
交易机制
当你用一些以太币Ether创建了一个有效的帐户时,你可以使用两种机制来与以太坊进行交易。
- 通过以太坊ethereum客户端进行认证签名交易
- 离线交易签名认证
这两种机制都是Web3j所支持的。
这里我们使用第二种方式,不需要管理以太坊客户端
离线交易签名认证:
为了离线脱机交易,你需要有你的钱包文件或与私密钱包/账户相关的公共和私人密钥。
web3j能够为你生成一个新的安全的以太坊钱包文件Ethereum wallet file,或者与也可以通过私钥来和现有的钱包文件一起工作。
创建新的钱包文件:
/*************创建一个钱包文件**************/
private void creatAccount() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
String walletFileName0="";//文件名
String walletFilePath0="D:/my project/mybatis-plus-study/demo-web3j/etc-wallet";
//钱包文件保持路径,请替换位自己的某文件夹路径
walletFileName0 = WalletUtils.generateNewWalletFile("123456", new File(walletFilePath0), false);
//WalletUtils.generateFullNewWalletFile("password1",new File(walleFilePath1));
//WalletUtils.generateLightNewWalletFile("password2",new File(walleFilePath2));
log.info("walletName: "+walletFileName0);
}
加载凭据从钱包文件:
/********加载钱包文件**********/
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
至此,钱包的创建和加载已经完成,但这一过程全部发生在本地,并未同步到以太坊区块链。查询地址余额前,需要连接以太坊结点。
构建Web3j实体,连接以太坊结点
web3j是连接java端与以太坊的桥梁,广播交易,查询账户都需要通过web3j实体。web3j支持通过http进行构建,而且兼容了infura。在本文中,使用的是infura的测试网络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"));
//测试是否连接成功
String web3ClientVersion = web3j.web3ClientVersion().send().getWeb3ClientVersion();
log.info("version=" + web3ClientVersion);
}
web3j实体构建完成后,可以打印出版本号以测试是否连接成功。如果成功,就可以做其他的事情了。
查询账户余额
/***********查询指定地址的余额***********/
private void getBlanceOf() throws IOException {
if (web3 == null) return;
String address = "0x51003b4b5ee042480a6d2ee5f08ffd265ab311cf";//等待查询余额的地址
//第二个参数:区块的参数,建议选最新区块
EthGetBalance balance = web3.ethGetBalance(address, DefaultBlockParameter.valueOf("latest")).send();
//格式转化 wei-ether
String blanceETH = Convert.fromWei(balance.getBalance().toString(), Convert.Unit.ETHER).toPlainString().concat(" ether");
log.info("blanceETH = "+ blanceETH);
}
使用钱包转账
a.在转账之前我们得获取一些测试用的以太币
首先我们需要使用Twitter、Facebook、Google Plus发布新的公开内
容,内容是自己的钱包地址,然后粘贴相应的文章的地址到faucet.
查看是否到账和账户状态 我们可以通过我们的地址去 explorer:https://www.rinkeby.io/#explorer
这样我们就有可以操作的ETH进行转账等操作了。
b.使用钱包文件来发起交易
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("转账成功,转账hash = {}",hash);
} catch (Exception e) {
log.error("转账失败,失败原因{}",e);
}
这种方式相对安全一些,这里需要注意的是一些ETH的最小单位——WEI.
1ETH = 1*10^18 wei;
提示:转账这里存在一些gas的问题,所谓gas我们都知道是转账时需要花费的旷工费。这里我们可以通过gasLimit和gasPrice来调整,当然我们肯定希望gas小,但是gas一味的小,会导致没有人愿意帮我们打包我们得到交易,导致交易最终无法成交,形成一个pending的状态。如果gas给的多,那么交易成交速度会比较快,但是我们花费的钱就会变多。
在之前实现的基础上,我们可以拿到自己创建的钱包,和对应钱包的文件和密码。
相关问题
1.ETH交易模块的特点
- 必须按顺序处理事务(具有1的nonce为1的事务必须在具有2的nonce的事务之前处理)
- 不跳过(具有4的nonce的事务不能包含在块中,直到具有1,2,3的nonce的事务
通过这种方式,网络能够识别交易的重复并强制执行订单(这对于智能合约至关重要) 这也就上文中说过的nonce, ### 2.如何查看我们转账的状态 我们可以通过转账后拿到的TxHash去https://etherscan.io/查询。
2.如何取消Pending状态转账
上文说到了转账因为gas设置的比较小,可能造成无法成交,一直处于Pending状态。 然后我们可以通过nonce去取消Pending的转账, 我们通过nonce的机制可以得知,我们只要新创建一笔转账,其中nonce和Pending状态的订单的nonce一样就可以替换当前Pending状态得到转账。
那么最好的操作来实现就是发起一笔向自己转账的订单,当然gas我们需要设置得到>合理。 转账参考以上两种方法。
3.取消或者取代转账
通过之前2中所提到了,我们可以知道,其实一个交易发出后,被覆盖,代替,取消等操作的几率很小。但是也不是不可能。只要当前交易不被挖矿和包含在区块中,是可以做相关关操作的。 具体操作方法还是和3相似,但是切记不可更改nonce.(具体情况还要和实际相比较,毕竟这种操作实现的概率几乎为0)
上述过程需要翻墙
更新补充,(博主的Facebook和Twitter由于发送上述消息给封禁了,莫名其妙~)上诉过程测试网更改成Ropsten(https://infura.io/
)
这个端点获取测试币相交简单些
所以在构建Web3j实体,连接以太坊结点时构建web3j客户端参数更换一下
web3j = Web3j.build(new HttpService("https://ropsten.infura.io/v3/b23bd4d475cd42978a9d29293d7fb317"));
获取测试币
https://faucet.ropsten.be/
在网站中输入钱包地址就ok了
查询交易记录
https://ropsten.etherscan.io/
同样是输入钱包地址
转账记录
/****************交易*****************/
private void transto() throws Exception {
if (web3 == null) return;
if (credentials == null) return;
//开始发送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());
打印结果:
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复制到框内,查询交易记录详情:https://ropsten.etherscan.io