java使用spring boot和web3j開發以太坊區塊鏈dapp

langyahappy發表於2018-08-15

區塊鏈最近IT世界的流行語之一。這項有關數字加密貨幣的技術,並與比特幣一起構成了這個熱門的流行趨勢。它是去中心化的,不可變的分塊資料結構,這是可以安全連線和使用的密碼演算法。在這種結構中的每一區塊通常包含前一個區塊的加密雜湊,一個時間戳,和交易資料。區塊鏈是點對點管理網路的,並在加入每一個新的塊之前進行節點間通訊的驗證。這是關於區塊鏈的部分理論。簡而言之,這是一種技術,它允許我們使用一個去中心化的方式管理雙方的交易。現在,問題是我們如何在我們的系統中實現它。

於是以太坊來了。這是一個Vitarik Buterin提供的去中心化的平臺,可以通過指令碼語言建立開發應用。它的想法是從比特幣獲得的,並由新的叫Ether即以太坊幣的加密數字幣驅動。今天,以太幣是繼比特幣之後的第二大加密數字貨幣。以太坊技術的核心是EVM(以太坊虛擬機器),它可以被視為類似於Java虛擬機器,而且用一種完全去中心化的節點網路。基於java世界實現以太坊交易我們使用web3j庫。這是一個輕量級的、響應式、型別安全的java和Android庫結合了以太坊區塊鏈節點。更多的細節可以在這裡找到web3j.io中文版

1.本地執行

雖然有許多針對區塊鏈文章,但以太坊相關的網路內容中不容易找到一個解決方案描述如何準備在本地機器使用例項執行以太坊。值得一提的是,一般有兩種最基本的客戶端可以使用:Geth和Parity。原來,我們可以很容易地在本地使用Docker容器執行節點。預設情況下,連線節點的以太坊主網路(公有鏈)。或者,你可以將它連線到測試網路或Rinkeby網路。但開始最好的選擇就是執行在設定了開發引數(--dev)的開發模式下,並在Docker容器中執行命令。

下面的命令啟動Docker容器開發模式在埠8545呼叫以太坊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

在開發模式中執行該容器時,一個非常好的訊息是,在預設的測試帳戶上有大量的Ether。在這種情況下,你不必挖掘任何Ether,便能夠開始測試。超級棒!現在,讓我們建立一些其他的測試帳號,並做一些檢查。為了實現這一點,我們需要在容器內部執行Geth的互動式JavaScript控制檯。

$ docker exec -it ethereum geth attach ipc:/tmp/geth.ipc

2.以太坊節點使用JavaScript控制檯管理

執行JavaScript控制檯可以方便顯示預設帳戶(Coinbase),所有可用的賬戶及其餘額清單。這裡的螢幕顯示我的以太坊結果。

現在,我們必須建立一些測試帳號。我們可以通過呼叫personal.newAccount(password)函式來實現這一點。在建立必需的帳戶之後,我們可以使用JavaScript控制檯執行一些測試交易,並將一些資金從基礎帳戶轉移到新建立的帳戶。下面是用於建立帳戶和執行交易的命令。

3.系統體系結構

我們的demo系統的體系結構非常簡單。不用想複雜的事情,只是告訴大家如何傳送交易到geth節點和接收交易收據。而transaction-service傳送新交易到以太坊節點,bonus-service節點監聽傳入的交易。然後每10筆交易傳送者的帳戶收到一次獎金(bonus)。這裡的圖表說明了一個我們的demo的系統架構。

4.spring boot應用程式使用web3j

我想現在我們清楚了我們到底想做什麼。所以,讓我們來進行實施。首先,我們應該包括所有必需的依賴項,以便能夠在Spring boot應用程式中使用web3j庫。幸運的是,有一個starter可以使用。

<dependency> <groupId>org.web3j</groupId> <artifactId>web3j-spring-boot-starter</artifactId> <version>1.6.0</version> </dependency>

因為我們在Docker容器執行以太坊客戶端需要改變客戶端的自動預設配置的web3j的呼叫地址。

spring: application: name: transaction-service server: port: ${PORT:8090} web3j: client-address: http://192.168.99.100:8545

5.構建應用

如果我們將web3j starter包含到專案依賴項中,需要的是自動裝載web3j bean。web3j負責向Geth客戶端節點傳送交易。它用交易雜湊接收響應,不管是節點接受或由於錯誤被拒絕。在建立交易物件時,重要的是將gas限制最小值設定為21000。如果傳送較低的值,則可能會收到錯誤資訊: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從上面的程式碼看由控制器呼叫。POST方法的需要BlockchainTransaction物件作為引數。你可以傳送發件人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、spring boot和以太坊geth客戶端的docker容器映象,可以快速啟動解決方案,實現區塊鏈技術的本地開發。

如果你想進行本地開發時clone我的庫,可以在github上下載原始碼

如果希望快速進行web3j、java、以太坊開發,那請看我們精心打造的教程: web3j教程,主要是針對java和android程式設計師進行區塊鏈以太坊開發的web3j詳解。

匯智網原創翻譯,轉載請標明出處。這裡是原文

相關文章