当前位置: 首页 > article >正文

java每日精进 2.13 Ganache(区块链本地私有化部署)

需求:使用区块链实现数据村存储,记录一些不可篡改的交互信息,网络环境为内外网均需要部署;

1.准备工作(软件安装)


1.1 安装 Node.js 和 npm

1.2 安装 Ganache

地址如下:windows有可视化界面 ,本文章使用windows版

Ganache - Truffle Suite

点击“Quickstart”创建一个本地以太坊区块链网络

1.3 安装 Truffle

打开命令提示符(CMD)或 PowerShell,运行以下命令安装 Truffle:

npm install -g truffle

安装后验证

truffle version

类似如下则安装成功:

1.4 安装 Web3.js(前端和ganache连接需要,后端的话直接跳过即可)
npm install web3

2. 创建和配置区块链项目

2.1 初始化 Truffle 项目

打开命令提示符(CMD)或 PowerShell

找到合适的文件夹 下运行以下命令创建一个新目录并初始化 Truffle 项目:

mkdir my-blockchain-project
cd my-blockchain-project
truffle init
2.2 配置 Truffle
  • 在项目目录中找到 truffle-config.js 文件,用文本编辑器(如 Notepad++ 或 VSCode)打开。

  • 修改配置文件,配置 Ganache 作为开发网络:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1", // Ganache 的默认地址
      port: 7545,        // Ganache 的默认端口
      network_id: "*",   // 匹配任何网络ID
    },
  },
  compilers: {
    solc: {
      version: "0.8.0",  // 使用合适的 Solidity 版本
    },
  },
};

3. 编写和部署智能合约

3.1 编写智能合约

创建 Solidity 合约文件:

  • 在 contracts 目录下创建一个新的 Solidity 合约文件,例如 DataStorage.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

contract DataStorage {
    struct Data {
        string jsonData;
    }

    mapping(uint256 => Data) public dataMap;
    uint256 public dataCount;

    function storeData(string memory _jsonData) public {
        dataMap[dataCount] = Data(_jsonData);
        dataCount++;
    }

    function getData(uint256 _id) public view returns (string memory) {
        return dataMap[_id].jsonData;
    }
}

Solidity 合约定义了一个简单的数据存储和检索机制

**合约声明
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
  • // SPDX-License-Identifier: UNLICENSED: 这是一个 SPDX 声明,用于指明代码的许可证类型。UNLICENSED 表示该代码没有许可条款。
  • pragma solidity ^0.8.0(一定要和truffle-config.js 文件中的compilers的version相同,不然会报错): 指定该合约使用的 Solidity 编译器版本是 0.8.0 或更高版本。^ 表示向上兼容。
**合约定义
contract DataStorage {
  • contract DataStorage {: 定义了一个名为 DataStorage 的合约,所有的存储和检索功能都将在这个合约内实现。
**数据结构定义
    struct Data {
        string jsonData;
    }
  • struct Data: 定义了一个名为 Data 的结构体,它包含一个字段 jsonData,该字段是一个 string 类型,用来存储 JSON 格式的数据,表示使用此合约存储的是json类数据;
**状态变量
    mapping(uint256 => Data) public dataMap;
    uint256 public dataCount;
  • mapping(uint256 => Data) public dataMap;: 定义了一个 mapping,它将一个 uint256 类型的键映射到一个 Data 结构体。public 关键字使得该映射可以通过合约外部访问(自动生成 getter 函数)。dataMap 用来存储每个数据条目,数据是通过 dataCount 作为键存储的。
  • uint256 public dataCount;: 定义了一个计数器 dataCount,用来记录当前存储的 Data 的数量。每当存储新数据时,dataCount 会自增,用于将来遍历查询数据。
**存储函数
    function storeData(string memory _jsonData) public {
        dataMap[dataCount] = Data(_jsonData);
        dataCount++;
    }
  • function storeData(string memory _jsonData) public: 定义了一个公开函数 storeData,它接受一个 string 类型的参数 _jsonData,用于存储数据。
    • string memory _jsonData: 这是函数的输入参数,表示传入的 JSON 数据。
  • dataMap[dataCount] = Data(_jsonData);: 将传入的 JSON 数据 _jsonData 存储在 dataMap 中,以 dataCount 作为键,值是一个包含该 JSON 数据的 Data 结构体。
  • dataCount++;: 每次调用 storeData 函数时,dataCount 计数器会增加 1,确保下一个数据存储使用新的键。
  • 将来每次取值都是通过健访问dataMap的值,得以拿到数据;
**获取函数
    function getData(uint256 _id) public view returns (string memory) {
        return dataMap[_id].jsonData;
    }

这个合约实现了以下功能:

  • storeData 函数允许将 JSON 格式的数据存储到区块链中。
  • getData 函数允许根据存储时生成的 id(由 dataCount 自动递增)获取对应的 JSON 数据。
  • dataMap 是一个映射,它将数据的 id 映射到存储的数据 jsonData
  • dataCount 记录了当前存储的数据数量,并且用于为每个数据条目生成唯一的 id
3.2 编译智能合约

接下来命令都是在项目根目录下运行 , 运行以下命令编译合约:

truffle compile
3.3 部署智能合约

在 migrations 目录下创建一个新的迁移文件,例如 2_deploy_contracts.js

const DataStorage = artifacts.require("DataStorage");

module.exports = function (deployer) {
  deployer.deploy(DataStorage);
};

运行以下命令部署合约:

truffle migrate --network development
  • 部署成功后,控制台输出如下,记下合约地址(在终端输出中查找 contract address)。

Starting migrations...
======================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 6721975 (0x6691b7)


2_deploy_contracts.js
=====================

   Deploying 'DataStorage'
   -----------------------
   > transaction hash:    0x779d63bdaa8e9afb1ce4ff56751a923f861ce006d03028183570e1130a326dca
   > Blocks: 0            Seconds: 0
   > contract address:    0x9E6954C2B46ae3B7C1e6676964a2Cc5e4477Fedf
   > block number:        1
   > block timestamp:     1739346849
   > account:             0xEF8625527393F19118803b027631F215a6eE10c8
   > balance:             99.99854643475
   > gas used:            430686 (0x6925e)
   > gas price:           3.375 gwei
   > value sent:          0 ETH
   > total cost:          0.00145356525 ETH

   > Saving artifacts
   -------------------------------------
   > Total cost:       0.00145356525 ETH

Summary
=======
> Total deployments:   1
> Final cost:          0.00145356525 ETH

4. 使用 Java 与区块链交互

4.1 安装 Web3j

下载 Web3j:github中其地址如下

Releases · hyperledger-web3j/web3j (github.com)

下载.zip版本并解压

解压后目录如下:

在本文件夹下使用powershell

.\gradlew build
4.2. 生成 Java 合约文件

已经有了 Solidity 合约文件(.sol 文件),可以使用 Web3j 提供的工具来生成 Java 类。
在 PowerShell 中执行以下命令来安装 Web3j CLI 工具:

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/hyperledger/web3j-installer/main/installer.ps1'))

执行以下命令检查是否安装成功:

web3j --version

在项目目录下运行以下命令生成 Java 合约包装器

web3j generate truffle --truffle-json ./build/contracts/DataStorage.json -o ./src/main/java -p com.example.contract
  • 生成的 Java 文件将位于 src/main/java/com/example/contract 目录下。

4.3 编写 Java 程序

创建项目,将生成的合约包装器文件复制到项目的 src/main/java/com/example/contract 目录下

添加 Web3j 依赖

<dependency>
    <groupId>org.web3j</groupId>
    <artifactId>core</artifactId>
    <version>4.9.4</version>
</dependency>

编写java程序:

@RestController
@RequestMapping("/myBlockChain")
public class BlockChainController {
    @Autowired
    DataStorageExample dataStorageExample;
    @PostMapping("/storeData")
    public String addMsg(@RequestBody Transaction  transaction) throws Exception {
        // 将接收到的消息对象转化为 JSON 字符串
        String jsonData = String.format("{\"tid\":\"%s\",\"data\":%s}", transaction.getTid(), transaction.getData());
        String msg = DataStorageExample.storeData(jsonData);
        return msg;
    }

    @PostMapping("/getAllData")
    public String getMsg() throws Exception {
        // 将接收到的消息对象转化为 JSON 字符串
        String msg = DataStorageExample.getData();
        return msg;
    }
}
/**
 * 数据存储示例
 */
@Service
public class DataStorageExample {
    private static final String NODE_URL = "http://localhost:7545";
    private static final String PRIVATE_KEY = "******************************";
    private static Web3j web3j;
    private static Credentials credentials;
    private static DataStorage dataStorage;

    // 静态代码块初始化 Web3j 和 Credentials
    static {
        web3j = Web3j.build(new HttpService(NODE_URL));
        credentials = Credentials.create(PRIVATE_KEY);
    }

    /**
     * 获取单一的 DataStorage 合约实例
     * @return DataStorage
     * @throws Exception
     */
    public static DataStorage getDataStorage() throws Exception {
        if (dataStorage == null) {
            // 如果合约尚未部署,则进行部署,确保单例,不然不同合约下的数据不互通
            dataStorage = deployContract(web3j, credentials);
        }
        return dataStorage;
    }

    /**
     * 部署合约
     * @param web3j
     * @param credentials
     * @return
     * @throws Exception
     */
    private static DataStorage deployContract(Web3j web3j, Credentials credentials) throws Exception {
        System.out.println("正在部署合约...");
        DataStorage dataStorage = DataStorage.deploy(web3j, credentials, new DefaultGasProvider()).send();
        System.out.println("合约部署在地址: " + dataStorage.getContractAddress());
        return dataStorage;
    }

    /**
     * 存储JSON数据
     * @param jsonData
     * @throws Exception
     */
    public static String storeData(String jsonData) throws Exception {
        if (dataStorage == null) {
            getDataStorage(); // 确保合约已部署
        }
        System.out.println("存储的数据: " + jsonData);
        TransactionReceipt receipt = dataStorage.storeData(jsonData).send();
        System.out.println("交易收据: " + receipt.getTransactionHash());
        return receipt.getTransactionHash();
    }

    /**
     * 获取数据
     * @return String
     * @throws Exception
     */
    public static String getData() throws Exception {
        if (dataStorage == null) {
            getDataStorage(); // 确保合约已部署
        }
        BigInteger dataCount = dataStorage.dataCount().send(); // 获取数据的总条目数
        Map<BigInteger, String> allData = new HashMap<>();

        // 遍历所有数据,按照ID获取并存储
        for (BigInteger i = BigInteger.ZERO; i.compareTo(dataCount) < 0; i = i.add(BigInteger.ONE)) {
            String data = dataStorage.getData(i).send(); // 获取每条数据
            allData.put(i, data); // 将数据存入Map
        }
        return allData.toString();
    }
}

PRIVATE_KEY的值为任意ACCOUNT的 PRIVATE_KEY的值,表示哪个账户发起交易

每次交易都会生成一个新的Block存储数据

点击Transactions可查看每次交易的相关数据

运行示例:

至此,实现本地Ganache区块链私有化部署并使json数据上链


http://www.kler.cn/a/545525.html

相关文章:

  • Docker compose部署禅道
  • 创建和管理 Conda 环境:环境隔离与依赖管理
  • k8s向容器内传文件与下载文件
  • XMOS的多项音频技术创新将大模型与边缘AI应用密切联系形成生态化合
  • CentOS-Stream 9安装
  • 消息中间件:RabbitMQ镜像集群部署配置全流程
  • 国自然青年项目|基于CT影像组学和深度学习监测晚期非小细胞肺癌免疫微环境及预测免疫治疗疗效的研究|基金申请·25-02-10
  • CEF132 编译指南 MacOS 篇 - depot_tools 安装与配置 (四)
  • Java设计模式——责任链模式与策略模式
  • 可编程网卡芯片在京东云网络的应用实践【BGW边界网关篇】
  • 如何用CSS解决边距合并问题?
  • SANS 网络安全 网络安全三件套
  • MongoDB 入门操作指南
  • C++,STL容器适配器,stack:栈深入解析
  • 【网络安全 | 漏洞挖掘】跨子域账户合并导致的账户劫持与删除
  • Python + WhisperX:解锁语音识别的高效新姿势
  • 基于MATLAB的沥青试样孔隙率自动分析——原理详解与代码实现
  • 初识Linux · 重定向和缓冲区(续)
  • Go 语言常用的结构体标签解析
  • 基于 GEE 计算研究区年均地表温度数据