Spring Boot集成BlockChain快速入门Demo

1.什么是区块链?

区块链定义: 区块链是一个共享的、不可篡改的账本,旨在促进业务网络中的交易记录和资产跟踪流程。 资产 可以是有形的(如房屋、汽车、现金、土地),也可以是无形的(如知识产权、专利、版权、品牌)。几乎任何有价值的东西都可以在区块链网络上跟踪和交易,从而降低各方面的风险和成本。 **为什么区块链很重要:**业务运营依靠信息。信息接收速度越快,内容越准确,越有利于业务运营。区块链是用于传递这些信息的理想之选,因为它可提供即时、共享和完全透明的信息,这些信息存储在不可篡改的账本上,只能由获得许可的网络成员访问。区块链网络可跟踪订单、付款、帐户、生产等信息。由于成员之间共享单一可信视图,因此,您可采取端到端方式查看交易的所有细节,从而增强信心,提高效率并获得更多的新机会。

区块链的关键元素

  1. 分布式分类账技术:所有网络参与者都可以访问分布式分类账及其不可篡改的交易记录。借助这种共享分类账,交易只记录一次,可消除传统业务网络典型的重复工作。
  2. 不可篡改记录:交易记录至共享分类账后,任何参与者都不能更改或篡改交易。如果交易记录中包含错误,则必须添加新交易来撤销错误,然后两笔交易均可见。
  3. 智能合约:为了加快交易速度,区块链上存储并自动运行了一组称为智能合约的规则。智能合约可定义企业债券转让的条件,包括支付旅行保险的条款等等。

区块链如何运作

  • 每笔交易发生时,都会记录为一个数据"区块"

    这些交易表明资产的流动,资产可以是有形的(产品),也可以是无形的(知识)。数据区块可以记录您选择的信息:人物、事件、时间、地点、价格。它甚至可以记录条件,例如食品运输温度。

  • 每个区块都与其前后的区块相连

    随着资产从一地转移至另一地,或所有权易手,这些区块会形成数据链。区块可确认交易的准确时间和顺序,并且区块之间安全地链接在一起,以防止任何区块遭到篡改,或在两个现有区块之间插入一个其他区块。

  • 交易以区块形式组合在一条不可逆的链中:区块链

    每添加一个区块都会加强对前一个区块的验证,从而加强整条区块链的验证。区块链篡改会变得容易发现,这就是不可篡改性的关键优势。这可以消除恶意行为者进行篡改的可能性,并建立您预其他网络成员可以信任的交易分类账。

2.环境搭建

Deploy a Custom Ethereum Network

docker run -d --name ethereum -p 8545:8545 -p 30303:30303 ethereum/client-go --http --http.addr="0.0.0.0" --http.api="db,eth,net,web3,personal" --http.corsdomain="*" --dev

view logs

docker logs -f ethereum

Create Account

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

Welcome to the Geth JavaScript console!

instance: Geth/v1.10.15-unstable-356bbe34/linux-amd64/go1.17.5
coinbase: 0x40f074a6c0e40f7c5167718355375c6f2c509690
at block: 0 (Thu Jan 01 1970 00:00:00 GMT+0000 (UTC))
 datadir:
 modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> personal.newAccount('ABC')
"0x9b418710ce8438e5fe585b519e8d709e1ea77aca"
> eth.accounts
["0x40f074a6c0e40f7c5167718355375c6f2c509690", "0x9b418710ce8438e5fe585b519e8d709e1ea77aca"]

Send Ethers to Accounts

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(100000, 'ether')})
"0x48fe4bd2d6db424ecc1f3713809d53e103fa7fc63646f4051a6a280d5f7080ea"
> eth.getBalance(eth.accounts[1])

3.代码工程

实验目标

使用Java对接ethereum网络

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-demo</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Blockchain</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.web3j</groupId>
            <artifactId>core</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.ethereum</groupId>
            <artifactId>solcJ-all</artifactId>
            <version>0.4.25</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
    <plugins>
        <plugin>
            <groupId>org.web3j</groupId>
            <artifactId>web3j-maven-plugin</artifactId>
            <version>0.3.7</version>
            <configuration>
                <packageName>org.sgitario.lottery.blockchain.model</packageName>
                <nativeJavaType>true</nativeJavaType>
                <outputFormat>java,bin,abi</outputFormat>
                <soliditySourceFiles>
                    <directory>src/main/resources/contracts</directory>
                    <includes>
                        <include>*.sol</include>
                    </includes>
                </soliditySourceFiles>
                <outputDirectory>
                    <java>src/main/java</java>
                    <bin>src/main/resources/bin/generated</bin>
                    <abi>src/main/resources/abi/generated</abi>
                </outputDirectory>
            </configuration>
        </plugin>
    </plugins>
    </build>
    <repositories>

        <repository>
            <id>nexus-ethereum</id>
            <name>Nexus ethereum</name>
            <layout>default</layout>
            <url>https://dl.bintray.com/ethereum/maven/</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
</project>

controller

package com.et.bc.controller;

import com.et.bc.model.Balance;
import com.et.bc.service.LotteryService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetBalance;

import java.io.IOException;
import java.util.List;

@RestController
public class OwnerController {

    @Value("${lottery.contract.owner-address}")
    private String ownerAddress;

    @Autowired
    private Web3j web3j;

    @Autowired
    private LotteryService lotteryService;

    @GetMapping("/owner")
    public String getAddress() {
        return ownerAddress;
    }

    @GetMapping("/owner/balance")
    public Balance getBalance() throws IOException {
        EthGetBalance wei = web3j.ethGetBalance(ownerAddress, DefaultBlockParameterName.LATEST).send();

        return new Balance(wei.getBalance());
    }

    @GetMapping("/owner/lottery/players")
    public List<String> getPlayers() throws Exception {
        return lotteryService.getPlayers(ownerAddress);
    }

    @GetMapping("/owner/lottery/pickWinner")
    public void pickWinner() throws Exception {
        lotteryService.pickWinner(ownerAddress);
    }
}

service

package com.et.bc.service;

import com.et.bc.model.Lottery;
import com.et.bc.model.Player;
import com.et.bc.properties.LotteryProperties;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.tx.ClientTransactionManager;
import org.web3j.tx.TransactionManager;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;

import java.io.IOException;
import java.math.BigInteger;
import java.util.List;

public class LotteryService {

    private final String contractAddress;
    private final Web3j web3j;
    private final LotteryProperties config;

    public LotteryService(String contractAddress, Web3j web3j, LotteryProperties config) {
        this.contractAddress = contractAddress;
        this.web3j = web3j;
        this.config = config;
    }

    public BigInteger getBalance() throws IOException {
        return web3j.ethGetBalance(contractAddress, DefaultBlockParameterName.LATEST).send().getBalance();
    }

    public void join(Player player) throws Exception {
        Lottery lottery = loadContract(player.getAddress());
        lottery.enter(Convert.toWei(player.getEthers(), Unit.ETHER).toBigInteger()).send();
    }

    @SuppressWarnings("unchecked")
    public List<String> getPlayers(String ownerAddress) throws Exception {
        Lottery lottery = loadContract(ownerAddress);
        return lottery.getPlayers().send();
    }

    public void pickWinner(String ownerAddress) throws Exception {
        Lottery lottery = loadContract(ownerAddress);
        lottery.pickWinner().send();
    }

    private Lottery loadContract(String accountAddress) {
        return Lottery.load(contractAddress, web3j, txManager(accountAddress), config.gas());
    }

    private TransactionManager txManager(String accountAddress) {
        return new ClientTransactionManager(web3j, accountAddress);
    }
}

转换器

package com.et.bc.utils;

import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;

import java.math.BigDecimal;
import java.math.BigInteger;

public class ConvertUtils {
    public static BigDecimal toEther(BigInteger wei) {
        return Convert.fromWei(new BigDecimal(wei), Unit.ETHER);
    }
}

注入的属性文件

package com.et.bc.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.web3j.tx.gas.StaticGasProvider;

import java.math.BigInteger;

@Configuration
@ConfigurationProperties(prefix = "lottery.contract")
@Getter
@Setter
public class LotteryProperties {
    private BigInteger gasPrice;
    private BigInteger gasLimit;

    public StaticGasProvider gas() {
        return new StaticGasProvider(gasPrice, gasLimit);
    }
}

配置类

package com.et.bc.config;

import com.et.bc.model.Lottery;
import com.et.bc.properties.LotteryProperties;
import com.et.bc.service.LotteryService;
import okhttp3.OkHttpClient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.ClientTransactionManager;
import org.web3j.tx.TransactionManager;

@Configuration
public class LotteryConfiguration {

    private static final Logger LOG = LoggerFactory.getLogger(LotteryConfiguration.class);

    @Value("${lottery.contract.owner-address}")
    private String ownerAddress;

    @Value("${web3j.client-address}")
    private String clientAddress;

    @Autowired
    private LotteryProperties config;

    @Bean
    public Web3j web3j() {
        return Web3j.build(new HttpService(clientAddress, new OkHttpClient.Builder().build()));
    }

    @Bean
    public LotteryService contract(Web3j web3j, @Value("${lottery.contract.address:}") String contractAddress)
            throws Exception {
        if (StringUtils.isEmpty(contractAddress)) {
            Lottery lottery = deployContract(web3j);
            return initLotteryService(lottery.getContractAddress(), web3j);
        }

        return initLotteryService(contractAddress, web3j);
    }

    private LotteryService initLotteryService(String contractAddress, Web3j web3j) {
        return new LotteryService(contractAddress, web3j, config);
    }

    private Lottery deployContract(Web3j web3j) throws Exception {
        LOG.info("About to deploy new contract...");
        Lottery contract = Lottery.deploy(web3j, txManager(web3j), config.gas()).send();
        LOG.info("Deployed new contract with address '{}'", contract.getContractAddress());
        return contract;
    }

    private TransactionManager txManager(Web3j web3j) {
        return new ClientTransactionManager(web3j, ownerAddress);
    }

}

application.properties

web3j.client-address=http://localhost:8545

lottery.contract.owner-address=0x9b418710ce8438e5fe585b519e8d709e1ea77aca

lottery.contract.gas-price=1
lottery.contract.gas-limit=2000000
lottery.contract.address=0x1c0fe20304e76882fe7ce7bb3e2e63dc92ce64de

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

4.测试

5.引用

相关推荐
敲代码的小王!1 小时前
MD5加密算法和BCrypt密码加密算法
java·算法·安全
魔尔助理顾问2 小时前
一个简洁高效的Flask用户管理示例
后端·python·flask
李长渊哦6 小时前
使用Druid连接池优化Spring Boot应用中的数据库连接
数据库·spring boot·后端
web135085886356 小时前
【Spring Boot】Spring AOP动态代理,以及静态代理
spring boot·后端·spring
罗政6 小时前
冒险岛079 V8 整合版源码搭建教程+IDEA启动
java·ide·intellij-idea
nbsaas-boot7 小时前
Go 自动升级依赖版本
开发语言·后端·golang
架构默片7 小时前
【JAVA工程师从0开始学AI】,第五步:Python类的“七十二变“——当Java的铠甲遇见Python的液态金属
java·开发语言·python
zzyh1234567 小时前
springcloud的组件及作用
后端·spring·spring cloud
不只会拍照的程序猿7 小时前
从插入排序到希尔排序
java·开发语言·数据结构·算法·排序算法
尚学教辅学习资料8 小时前
基于SpringBoot的图书借阅小程序+LW参考示例
spring boot·后端·小程序·java毕设·图书借阅