在过去的几个月里,区块链是IT世界的流行语之一。该术语与加密货币有关,并与比特币一起创建。它是分散的,不可变的数据结构,分为块,使用加密算法进行链接和保护。此结构中的每个块通常都包含前一个块的加密哈希,时间戳和事务数据。区块链由对等网络管理,并且在节点间通信期间,每个新块在添加之前被验证。这是关于区块链的理论的一小部分。简而言之,这是一种技术,它允许我们以分散的方式管理双方之间的交易。现在,问题是我们如何在我们的系统中实现它。
来了以太坊。它是由Vitarik Buterin创建的分散式平台,为应用程序开发提供脚本语言。它基于比特币的想法,并由名为Ether的新加密货币驱动。今天,Ether是比特币之后的第二大加密货币。以太坊技术的核心是EVM(以太坊虚拟机),它可以被视为类似于JVM的东西,但使用完全分散的节点网络。要在Java世界中实现基于Ethereum的事务,我们使用web3j库。这是一个轻量级,反应性,类型安全的Java和Android库,用于与以太坊区块链上的节点集成。更多细节可以在其网站https://web3j.io上找到。
虽然网上有很多关于区块链和以太坊的文章,但要找到一个解决方案来描述如何在本地机器上运行以太网的即用型实例并不容易。值得一提的是,通常我们可以使用两种最受欢迎的以太坊客户:Geth和Parity。事实证明,我们可以使用Docker容器在本地轻松运行Geth节点。默认情况下,它将节点连接到以太坊主网络。或者,您可以将其连接到测试网络或Rinkeby网络。但是开始的最佳选择只是通过–dev在Docker容器运行命令上设置参数来在开发模式下运行它。
这是在开发模式下启动Docker容器并在端口8545上公开Ethereum RPC API的命令。
docker run -d --name ethereum -p 8545:8545 -p 30303:30303 ethereum/client-go --rpc --rpcaddr "0.0.0.0" --rpcapi="db,eth,net,web3,personal" --rpccorsdomain "*" --dev
在开发模式下运行该容器时,一个非常好的消息是您的默认测试帐户上有大量的Ethers。在这种情况下,您无需开采任何Ethers即可开始测试。现在,让我们创建一些其他测试帐户,并查看一些内容。为了实现它,我们需要在Docker容器中运行Geth的交互式JavaScript控制台。
docker exec -it ethereum geth attach ipc:/tmp/geth.ipc
Notice: 毫无疑问,你应该首先成功安装Docker。然后在win10平台下可以使用powershell
关闭docker后如何再次开启ethereum?(对Docker新手)
docker start ethereum
运行JavaScript控制台后,您可以轻松显示默认帐户(coinbase),所有可用帐户的列表及其余额。
现在,我们必须创建一些测试帐户。我们可以通过调用personal.newAccount(password)函数来实现。创建所需帐户后,您可以使用JavaScript控制台执行一些测试事务,并将一些资金从基本帐户转移到新创建的帐户。以下是用于创建帐户和执行事务的命令。
Notice: (在geth里面执行)
创建账户
personal.newAccount("password")
查询当前有哪些账户
eth.accounts
查看挖矿账户
eth.coinbase
查询余额
eth.getBalance(eth.accounts[Index])
eth.getBalance(eth.coinbase)
查看区块高度
eth.blockNumber
解锁账户(后文将会涉及)
personal.unlockAccount(eth.accounts[Index], "password")
交易
eth.sendTransaction({from: eth.coinbase,to: eth.accounts[1],value: 1000})
四、系统架构
我们的示例系统的架构非常简单。我不想让任何事情复杂化,只是告诉你如何将事务发送到Geth节点并接收通知。在transaction-service向以太坊节点发送新事务时,bonus-service观察节点并侦听传入事务。然后,从他的帐户收到的每10笔交易中,它会向发件人的帐户发送一次奖励。这是说明我们的示例系统的体系结构的图表。
我认为现在我们已经明确了我们想要做什么。那么,让我们继续实施。首先,我们应该包含所有必需的依赖项,以便能够web3j在Spring Boot应用程序中使用库。幸运的是,可以包含一个启动器。
org.web3j
web3j-spring-boot-starter
1.6.0
因为我们在Docker容器上运行Ethereum Geth客户端,所以我们需要更改自动配置的客户端地址web3j。
spring:
application:
name: transaction-service
server:
port: ${PORT:8090}
web3j:
client-address: http://192.168.99.100:8545 (原文如此,但是建议改为127.0.0.1:8545 )
如果我们将web3j启动程序包含在项目依赖项中,那么您需要的是autowire Web3jbean。Web3j负责向Geth客户端节点发送事务。如果已被节点接受,则它接收具有事务散列的响应,如果它已被拒绝,则接收错误对象。在创建事务对象时,将气体限制设置为最小值21000非常重要。如果发送的值较低,则可能会收到错误Error: intrinsic gas too low。
@Service
public class BlockchainService {
private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainService.class);
@Autowired
Web3j web3j;
public BlockchainTransaction process(BlockchainTransaction trx) throws IOException {
EthAccounts accounts = web3j.ethAccounts().send();
EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(accounts.getAccounts().get(trx.getFromId()), DefaultBlockParameterName.LATEST).send();
Transaction transaction = Transaction.createEtherTransaction(accounts.getAccounts().get(trx.getFromId()), transactionCount.getTransactionCount(), BigInteger.valueOf(trx.getValue()), BigInteger.valueOf(21_000), accounts.getAccounts().get(trx.getToId()),BigInteger.valueOf(trx.getValue()));
EthSendTransaction response = web3j.ethSendTransaction(transaction).send();
if (response.getError() != null) {
trx.setAccepted(false);
return trx;
}
trx.setAccepted(true);
String txHash = response.getTransactionHash();
LOGGER.info("Tx hash: {}", txHash);
trx.setId(txHash);
EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(txHash).send();
if (receipt.getTransactionReceipt().isPresent()) {
LOGGER.info("Tx receipt: {}", receipt.getTransactionReceipt().get().getCumulativeGasUsed().intValue());
}
return trx;
}
}
@Service上面可见的bean由控制器调用。POST方法的实现将BlockchainTransactionobject作为参数。您可以发送发件人ID,收件人ID和交易金额。发件人和收件人ID等同于查询中的索引eth.account[index]。
@RestController
public class BlockchainController {
@Autowired
BlockchainService service;
@PostMapping("/transaction")
public BlockchainTransaction execute(@RequestBody BlockchainTransaction transaction) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
return service.process(transaction);
}
}
您可以使用以下命令通过调用POST方法发送测试事务。
$ curl --header "Content-Type: application/json" --request POST --data '{"fromId":2,"toId":1,"value":3}' http://localhost:8090/transaction
在发送任何交易之前,您还应该解锁发件人帐户。
应用程序bonus-service侦听由以太坊节点处理的事务。它通过调用web3j.transactionObservable().subscribe(…)方法订阅来自Web3j库的通知。它会从该地址发送的每10笔交易中将收到的交易金额返回给发件人的帐户一次。这是应用程序内部的可观察方法的实现bonus-service。
@Autowired
Web3j web3j;
@PostConstruct
public void listen() {
Subscription subscription = web3j.transactionObservable().subscribe(tx -> {
LOGGER.info("New tx: id={}, block={}, from={}, to={}, value={}", tx.getHash(), tx.getBlockHash(), tx.getFrom(), tx.getTo(), tx.getValue().intValue());
try {
EthCoinbase coinbase = web3j.ethCoinbase().send();
EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(tx.getFrom(), DefaultBlockParameterName.LATEST).send();
LOGGER.info("Tx count: {}", transactionCount.getTransactionCount().intValue());
if (transactionCount.getTransactionCount().intValue() % 10 == 0) {
EthGetTransactionCount tc = web3j.ethGetTransactionCount(coinbase.getAddress(), DefaultBlockParameterName.LATEST).send();
Transaction transaction = Transaction.createEtherTransaction(coinbase.getAddress(), tc.getTransactionCount(), tx.getValue(), BigInteger.valueOf(21_000), tx.getFrom(), tx.getValue());
web3j.ethSendTransaction(transaction).send();
}
} catch (IOException e) {
LOGGER.error("Error getting transactions", e);
}
});
LOGGER.info("Subscribed");
}
区块链和加密货币不是一个容易开始的主题。通过提供完整的脚本语言,以太坊简化了使用区块链的应用程序的开发。使用web3j库以及以太坊Geth客户端的Spring Boot和Docker映像,可以快速启动实现区块链技术的解决方案的本地开发。如果您想在本地尝试,只需克隆我在GitHub上提供的存储库
https://github.com/ybeario/sample-spring-blockchain
Notice: 由于Piotr Mińkowski大佬已经将代码上传同时采用swagger,因此为了方便测试在spring boot启动成功后,可登录
http://localhost:8090/swagger-ui.html
进行测试。
选择Blockchain Controller–>try it out–>Execute(json数值自己填,记得unlockAccount!)
交易已经完成,那么如何部署自定义智能合约呢?未完待续。。。