Java与区块链:Web3j/Hyperledger Fabric SDK的应用开发实践

Java与区块链:Web3j/Hyperledger Fabric SDK的应用开发实践

大家好!今天我们来探讨Java在区块链应用开发中的实践,重点关注Web3j和Hyperledger Fabric SDK这两个强大的工具。我们将深入了解如何使用它们与以太坊和Hyperledger Fabric区块链网络进行交互,并构建实际的应用。

1. 区块链技术与Java:为什么选择Java?

区块链作为一种去中心化的分布式账本技术,正在深刻地改变各行各业。Java作为一种成熟、稳定、跨平台的编程语言,在企业级应用开发中占据主导地位。将Java与区块链技术结合,可以充分利用Java的生态系统和开发经验,快速构建可靠、可扩展的区块链应用。

Java的优势:

  • 成熟的生态系统: 拥有丰富的库、框架和工具,便于快速开发和集成。
  • 跨平台性: “一次编写,到处运行”的特性,方便部署到不同的环境。
  • 强大的性能: JVM的优化能力,保证了应用的性能。
  • 安全性: Java的安全特性,有助于构建安全的区块链应用。
  • 大型企业广泛使用: 容易找到具备相关开发经验的工程师。

2. Web3j:与以太坊交互的利器

Web3j是一个轻量级的Java库,用于与以太坊区块链进行交互。它提供了一系列API,可以方便地进行以下操作:

  • 连接到以太坊节点(例如:Geth、Parity)。
  • 创建和管理以太坊账户。
  • 发送以太币(ETH)交易。
  • 部署和调用智能合约。
  • 监听区块链事件。

2.1 Web3j入门:配置与连接

首先,我们需要在项目中引入Web3j依赖。如果是使用Maven,可以在pom.xml文件中添加以下依赖:

<dependency>
    <groupId>org.web3j</groupId>
    <artifactId>core</artifactId>
    <version>4.9.4</version>  <!--  请使用最新版本  -->
</dependency>

接下来,我们需要连接到以太坊节点。以下代码演示了如何连接到本地的Geth节点:

import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;

public class Web3jConnect {

    public static void main(String[] args) {
        // 连接到本地Geth节点 (需要确保Geth节点已经启动)
        Web3j web3 = Web3j.build(new HttpService("http://localhost:8545"));

        try {
            // 测试连接是否成功
            String clientVersion = web3.web3ClientVersion().send().getWeb3ClientVersion();
            System.out.println("Connected to Ethereum client: " + clientVersion);
        } catch (Exception e) {
            System.err.println("Failed to connect to Ethereum client: " + e.getMessage());
            e.printStackTrace();
        } finally {
            web3.shutdown();
        }
    }
}

代码解释:

  • Web3j.build(new HttpService("http://localhost:8545")): 创建一个Web3j实例,并通过HTTP连接到本地的Geth节点。你需要确保Geth节点已经启动,并且监听在8545端口。
  • web3.web3ClientVersion().send().getWeb3ClientVersion(): 调用web3ClientVersion方法获取客户端版本号,用于测试连接是否成功。
  • web3.shutdown(): 关闭Web3j连接,释放资源。

2.2 创建和管理账户

Web3j可以用来创建和管理以太坊账户。以下代码演示了如何创建一个新的以太坊账户:

import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;

import java.io.File;

public class AccountCreation {

    public static void main(String[] args) {
        try {
            // 生成钱包文件存储目录
            File walletDir = new File("wallets");
            if (!walletDir.exists()) {
                walletDir.mkdirs();
            }

            // 生成随机密码
            String password = "your_secret_password"; // 请替换为安全的密码

            // 创建新的钱包文件
            String fileName = WalletUtils.generateNewWalletFile(password, walletDir, true);

            System.out.println("New wallet file created: " + walletDir.getAbsolutePath() + "/" + fileName);

            // 从钱包文件加载账户
            Credentials credentials = WalletUtils.loadCredentials(password, walletDir.getAbsolutePath() + "/" + fileName);
            System.out.println("Account address: " + credentials.getAddress());

        } catch (Exception e) {
            System.err.println("Failed to create or load wallet: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

代码解释:

  • WalletUtils.generateNewWalletFile(password, walletDir, true): 使用给定的密码和目录生成一个新的钱包文件。true表示使用轻量级的KeyStore文件格式。
  • WalletUtils.loadCredentials(password, walletDir.getAbsolutePath() + "/" + fileName): 使用密码和钱包文件加载账户信息。
  • credentials.getAddress(): 获取账户的地址。

警告: 请务必妥善保管你的钱包文件和密码,丢失它们将导致你的以太币丢失。不要将密码硬编码到代码中,使用环境变量或配置文件来管理密码。

2.3 发送以太币交易

以下代码演示了如何发送以太币交易:

import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.crypto.Credentials;
import org.web3j.tx.Transfer;
import java.math.BigDecimal;
import java.math.BigInteger;

public class SendEther {

    public static void main(String[] args) {
        Web3j web3 = Web3j.build(new HttpService("http://localhost:8545"));

        try {
            // 替换为你的账户私钥 或 使用钱包文件加载 Credentials
            Credentials credentials = Credentials.create("YOUR_PRIVATE_KEY");

            // 收款人地址
            String recipientAddress = "RECIPIENT_ADDRESS";

            // 要发送的以太币数量 (单位: ether)
            BigDecimal amount = BigDecimal.valueOf(0.01);

            // 发送以太币
            TransactionReceipt transactionReceipt = Transfer.sendFunds(
                    web3,
                    credentials,
                    recipientAddress,
                    amount,
                    Transfer.GAS_PRICE,  // 默认 gas 价格
                    Transfer.GAS_LIMIT     // 默认 gas 限制
            ).send();

            System.out.println("Transaction hash: " + transactionReceipt.getTransactionHash());

        } catch (Exception e) {
            System.err.println("Failed to send ether: " + e.getMessage());
            e.printStackTrace();
        } finally {
            web3.shutdown();
        }
    }
}

代码解释:

  • Credentials.create("YOUR_PRIVATE_KEY"): 使用私钥创建Credentials对象。请替换为你的账户私钥。 强烈建议从环境变量或配置文件中读取私钥,而不是硬编码到代码中。也可以使用WalletUtils.loadCredentials从钱包文件加载Credentials。
  • Transfer.sendFunds(...): 使用Transfer类发送以太币。
  • transactionReceipt.getTransactionHash(): 获取交易哈希值。

注意事项:

  • 发送交易需要消耗Gas,Gas Price和Gas Limit需要根据当前网络状况进行调整。
  • 请确保你的账户有足够的以太币来支付Gas费用。
  • 在测试环境中,可以使用Faucet获取测试币。

2.4 智能合约交互

Web3j可以方便地与智能合约进行交互。首先,我们需要编译智能合约,并生成Java wrapper类。假设我们有一个简单的智能合约:

pragma solidity ^0.8.0;

contract Greeter {
    string greeting;

    constructor(string memory _greeting) {
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
    }
}

使用Solidity编译器(solc)编译智能合约,并使用Web3j命令行工具生成Java wrapper类:

solc Greeter.sol --bin --abi --optimize -o .
web3j generate solidity -b Greeter.bin -a Greeter.abi -o src/main/java -p com.example.greeter

这会在src/main/java/com/example/greeter目录下生成Greeter.java文件。

然后,我们可以使用Web3j部署和调用智能合约:

import com.example.greeter.Greeter; // 替换为你的包名
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.crypto.Credentials;
import org.web3j.tx.gas.DefaultGasProvider;

public class ContractInteraction {

    public static void main(String[] args) {
        Web3j web3 = Web3j.build(new HttpService("http://localhost:8545"));

        try {
            // 替换为你的账户私钥 或 使用钱包文件加载 Credentials
            Credentials credentials = Credentials.create("YOUR_PRIVATE_KEY");

            // 部署智能合约
            Greeter greeter = Greeter.deploy(
                    web3,
                    credentials,
                    new DefaultGasProvider(),
                    "Hello, World!" // 构造函数参数
            ).send();

            String contractAddress = greeter.getContractAddress();
            System.out.println("Contract deployed at address: " + contractAddress);

            // 调用智能合约的方法
            String greeting = greeter.greet().send();
            System.out.println("Greeting: " + greeting);

            // 设置新的greeting
            greeter.setGreeting("Hello, Java!").send();

            // 再次调用greet方法
            greeting = greeter.greet().send();
            System.out.println("New Greeting: " + greeting);

        } catch (Exception e) {
            System.err.println("Failed to interact with contract: " + e.getMessage());
            e.printStackTrace();
        } finally {
            web3.shutdown();
        }
    }
}

代码解释:

  • Greeter.deploy(...): 部署智能合约。
  • greeter.getContractAddress(): 获取合约地址。
  • greeter.greet().send(): 调用greet方法。
  • greeter.setGreeting("Hello, Java!").send(): 调用setGreeting方法。

2.5 监听区块链事件

Web3j可以监听智能合约发出的事件。以下代码演示了如何监听Greeter合约的GreetingChanged事件(需要修改Greeter.sol,添加事件):

pragma solidity ^0.8.0;

contract Greeter {
    string greeting;

    event GreetingChanged(string newGreeting); // 定义事件

    constructor(string memory _greeting) {
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
        emit GreetingChanged(_greeting); // 触发事件
    }
}
import com.example.greeter.Greeter;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.crypto.Credentials;
import org.web3j.tx.gas.DefaultGasProvider;

public class EventListener {

    public static void main(String[] args) {
        Web3j web3 = Web3j.build(new HttpService("http://localhost:8545"));

        try {
            // 替换为你的账户私钥 或 使用钱包文件加载 Credentials
            Credentials credentials = Credentials.create("YOUR_PRIVATE_KEY");

            // 部署智能合约
            Greeter greeter = Greeter.deploy(
                    web3,
                    credentials,
                    new DefaultGasProvider(),
                    "Hello, World!"
            ).send();

            String contractAddress = greeter.getContractAddress();
            System.out.println("Contract deployed at address: " + contractAddress);

            // 监听GreetingChanged事件
            greeter.greetingChangedEventFlowable(null).subscribe(event -> {
                System.out.println("Greeting changed to: " + event.newGreeting);
            });

            // 设置新的greeting (触发事件)
            greeter.setGreeting("Hello, Event!").send();

            // 为了保证能够接收到事件,这里需要等待一段时间。实际应用中不需要sleep。
            Thread.sleep(5000);

        } catch (Exception e) {
            System.err.println("Failed to listen to events: " + e.getMessage());
            e.printStackTrace();
        } finally {
            web3.shutdown();
        }
    }
}

代码解释:

  • greeter.greetingChangedEventFlowable(null).subscribe(event -> { ... }): 订阅GreetingChanged事件。当事件发生时,会执行subscribe方法中的代码。

3. Hyperledger Fabric SDK:构建企业级区块链应用

Hyperledger Fabric是一个企业级的许可型区块链平台,专注于高性能、可扩展性和安全性。Hyperledger Fabric SDK for Java 提供了 Java API,用于与 Fabric 网络进行交互,包括:

  • 连接到 Fabric 网络。
  • 注册和登记用户。
  • 安装、实例化和升级链码(智能合约)。
  • 调用链码。
  • 查询账本。

3.1 Fabric SDK入门:配置与连接

首先,我们需要在项目中引入Fabric SDK依赖。如果是使用Maven,可以在pom.xml文件中添加以下依赖(需要根据Fabric版本进行调整):

<dependency>
    <groupId>org.hyperledger.fabric</groupId>
    <artifactId>fabric-sdk-java</artifactId>
    <version>2.2.0</version>  <!--  请使用最新版本并确保与你的Fabric版本兼容  -->
</dependency>

接下来,我们需要配置 Fabric 网络信息,包括通道(Channel)、组织(Organization)、节点(Peer)和排序服务(Orderer)的信息。这些信息通常存储在一个配置文件中。

以下是一个简单的配置文件示例(fabric.properties):

# Channel configuration
channel.name=mychannel

# Organization configuration
org.example.mspid=Org1MSP
org.example.peer.0.name=peer0.org1.example.com
org.example.peer.0.grpc.host=localhost
org.example.peer.0.grpc.port=7051
org.example.peer.0.eventHub.host=localhost
org.example.peer.0.eventHub.port=7053
org.example.orderer.name=orderer.example.com
org.example.orderer.grpc.host=localhost
org.example.orderer.grpc.port=7050

然后,我们可以使用Fabric SDK连接到 Fabric 网络:

import org.hyperledger.fabric.gateway.Gateway;
import org.hyperledger.fabric.gateway.Network;
import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallets;

import java.nio.file.Paths;

public class FabricConnect {

    public static void main(String[] args) {
        try {
            // 加载钱包
            Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet")); // 需要提前创建钱包

            // 创建网关构建器
            Gateway.Builder builder = Gateway.createBuilder()
                    .identity(wallet, "user1") // 替换为你的用户身份
                    .networkConfig(Paths.get("connection.json")) // Fabric提供的连接配置文件,注意不是上面的fabric.properties
                    .discovery(true); // 启用服务发现

            // 创建网关
            try (Gateway gateway = builder.connect()) {

                // 获取网络
                Network network = gateway.getNetwork("mychannel"); // 替换为你的通道名称

                // 获取合约
                Contract contract = network.getContract("basic"); // 替换为你的链码名称

                System.out.println("Successfully connected to Fabric network.");

                // 可以调用链码或者查询账本
                // ...

            }

        } catch (Exception e) {
            System.err.println("Failed to connect to Fabric network: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

代码解释:

  • Wallets.newFileSystemWallet(Paths.get("wallet")): 创建一个基于文件系统的钱包。你需要提前创建钱包,并将用户身份添加到钱包中。
  • Gateway.createBuilder(): 创建一个网关构建器。
  • .identity(wallet, "user1"): 设置用户身份。
  • .networkConfig(Paths.get("connection.json")): 设置Fabric网络的连接配置文件。这个connection.json文件是由Fabric网络管理员提供的,包含了网络拓扑信息。
  • .discovery(true): 启用服务发现。
  • gateway.getNetwork("mychannel"): 获取通道。
  • network.getContract("basic"): 获取链码合约。

3.2 注册和登记用户

在 Fabric 网络中,每个用户都需要先注册和登记才能使用。可以使用 Fabric CA(Certificate Authority)进行用户管理。以下代码演示了如何使用 Fabric CA 客户端注册和登记用户:

import org.hyperledger.fabric.sdk.Enrollment;
import org.hyperledger.fabric.sdk.User;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric.sdk.security.CryptoSuiteFactory;
import org.hyperledger.fabric_ca.sdk.HFCAClient;
import org.hyperledger.fabric_ca.sdk.RegistrationRequest;

import java.nio.file.Paths;
import java.util.Properties;
import java.util.Set;

public class UserRegistration {

    public static void main(String[] args) {
        try {
            // Fabric CA 服务器地址
            String caUrl = "http://localhost:7054"; // 替换为你的 CA 地址

            // CA 客户端属性
            Properties caProps = new Properties();
            caProps.put("pemFile", Paths.get("../fabric-samples/test-network/organizations/fabric-ca/org1/ca_sk/ca.org1.example.com-cert.pem").toAbsolutePath().toString()); // 替换为你的 CA 证书路径
            caProps.put("allowAllHostNames", "true");

            // 创建 CA 客户端
            HFCAClient caClient = HFCAClient.createNewInstance(caUrl, caProps);
            CryptoSuite cryptoSuite = CryptoSuiteFactory.instance();
            caClient.setCryptoSuite(cryptoSuite);

            // 管理员用户
            User admin = // 从钱包或者其他方式加载管理员用户,需要具有 enroll 和 register 权限
              null;

            // 注册请求
            RegistrationRequest registrationRequest = new RegistrationRequest("user1", "org1.department1"); // 替换为你的用户名和组织
            registrationRequest.setAffiliation("org1.department1");
            registrationRequest.setEnrollmentID("user1");

            // 注册用户
            String enrollmentSecret = caClient.register(registrationRequest, admin);

            System.out.println("Successfully registered user. Enrollment secret: " + enrollmentSecret);

            // 登记用户
            Enrollment enrollment = caClient.enroll("user1", enrollmentSecret);

            // 将用户身份保存到钱包
            // ...

        } catch (Exception e) {
            System.err.println("Failed to register and enroll user: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

代码解释:

  • HFCAClient.createNewInstance(caUrl, caProps): 创建 Fabric CA 客户端。
  • caClient.register(registrationRequest, admin): 注册用户。需要管理员用户具有注册权限。
  • caClient.enroll("user1", enrollmentSecret): 登记用户。
  • 需要实现从钱包或其他方式加载管理员用户,并进行登记。

3.3 安装、实例化和升级链码

Fabric SDK 提供了 API 来安装、实例化和升级链码。这些操作通常需要管理员权限。由于代码量较大,这里只提供简要的说明:

  • 安装链码: 使用InstallProposalRequest创建安装提案,并将其发送给指定的 Peer 节点。
  • 实例化链码: 使用InstantiateProposalRequest创建实例化提案,并将其发送给 Orderer 节点。
  • 升级链码: 使用UpgradeProposalRequest创建升级提案,并将其发送给 Orderer 节点。

3.4 调用链码和查询账本

Fabric SDK 提供了 API 来调用链码和查询账本。以下代码演示了如何调用链码:

import org.hyperledger.fabric.gateway.Gateway;
import org.hyperledger.fabric.gateway.Network;
import org.hyperledger.fabric.gateway.Contract;
import org.hyperledger.fabric.gateway.Wallet;
import org.hyperledger.fabric.gateway.Wallets;

import java.nio.file.Paths;

public class InvokeChaincode {

    public static void main(String[] args) {
        try {
            // 加载钱包
            Wallet wallet = Wallets.newFileSystemWallet(Paths.get("wallet"));

            // 创建网关构建器
            Gateway.Builder builder = Gateway.createBuilder()
                    .identity(wallet, "user1")
                    .networkConfig(Paths.get("connection.json"))
                    .discovery(true);

            // 创建网关
            try (Gateway gateway = builder.connect()) {

                // 获取网络
                Network network = gateway.getNetwork("mychannel");

                // 获取合约
                Contract contract = network.getContract("basic");

                // 调用链码
                byte[] result = contract.submitTransaction("createAsset", "asset1", "blue", "5", "Tom", "100"); // 替换为你的链码方法和参数
                System.out.println("Transaction result: " + new String(result));

                // 查询账本
                byte[] queryResult = contract.evaluateTransaction("readAsset", "asset1"); // 替换为你的链码方法和参数
                System.out.println("Query result: " + new String(queryResult));

            }

        } catch (Exception e) {
            System.err.println("Failed to invoke chaincode: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

代码解释:

  • contract.submitTransaction(...): 调用链码的交易方法。该方法会将交易发送到 Fabric 网络,并等待交易被确认。
  • contract.evaluateTransaction(...): 调用链码的查询方法。该方法会在本地模拟执行交易,并返回结果,不会修改账本。

4. Web3j vs. Hyperledger Fabric SDK:选择合适的工具

特性 Web3j Hyperledger Fabric SDK
区块链类型 公有链(以太坊) 许可链(Hyperledger Fabric)
适用场景 去中心化应用(DApps),公开透明的场景 企业级应用,需要权限控制和高性能的场景
许可模式 开源,MIT License 开源,Apache 2.0 License
易用性 相对简单易用 相对复杂,需要对 Fabric 网络有深入了解
功能 与以太坊节点交互,智能合约部署和调用,事件监听 与 Fabric 网络交互,链码安装、实例化和调用,用户管理,权限控制

选择哪个工具取决于你的应用场景和需求。如果你需要构建一个去中心化的应用,并且希望利用以太坊的生态系统,那么 Web3j 是一个不错的选择。如果你需要构建一个企业级的应用,并且需要权限控制和高性能,那么 Hyperledger Fabric SDK 更适合你。

5. 案例:使用Web3j构建简单的投票DApp

我们可以使用Web3j构建一个简单的投票DApp。首先,我们需要创建一个智能合约:

pragma solidity ^0.8.0;

contract Voting {
    mapping(bytes32 => uint256) public votesReceived;
    bytes32[] public candidateList;

    constructor(bytes32[] memory candidateNames) {
        candidateList = candidateNames;
    }

    function voteForCandidate(bytes32 candidate) public {
        require(validCandidate(candidate));
        votesReceived[candidate] += 1;
    }

    function validCandidate(bytes32 candidate) public view returns (bool) {
        for(uint i = 0; i < candidateList.length; i++) {
            if (candidateList[i] == candidate) {
                return true;
            }
        }
        return false;
    }

    function totalVotesFor(bytes32 candidate) public view returns (uint256) {
        require(validCandidate(candidate));
        return votesReceived[candidate];
    }
}

然后,我们可以使用Web3j部署和调用智能合约,并创建一个简单的用户界面来让用户投票和查看投票结果。由于篇幅限制,这里只提供核心代码片段:

部署合约:

// 部署合约
Voting voting = Voting.deploy(
    web3,
    credentials,
    new DefaultGasProvider(),
    candidates // 候选人列表
).send();

投票:

// 投票
voting.voteForCandidate(candidateName).send();

查看投票结果:

// 查看投票结果
BigInteger votes = voting.totalVotesFor(candidateName).send();

用户界面:

可以使用Java Swing、JavaFX或者Web框架(例如:Spring MVC)创建一个简单的用户界面,让用户选择候选人并进行投票。用户界面可以显示每个候选人的投票结果。

6. 案例:使用Hyperledger Fabric SDK构建供应链管理系统

我们可以使用Hyperledger Fabric SDK构建一个简单的供应链管理系统。该系统可以跟踪商品的流转过程,并记录商品的详细信息。

首先,我们需要创建一个链码来管理商品信息:

package main

import (
    "encoding/json"
    "fmt"

    "github.com/hyperledger/fabric-contract-api-go/contractapi"
)

type SmartContract struct {
    contractapi.Contract
}

type Asset struct {
    ID             string `json:"ID"`
    Color          string `json:"color"`
    Size           int    `json:"size"`
    Owner          string `json:"owner"`
    AppraisedValue int    `json:"appraisedValue"`
}

// InitLedger creates the initial set of assets on the ledger.
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
    assets := []Asset{
        {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
        {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
        {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
        {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
        {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
        {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
    }

    for _, asset := range assets {
        assetJSON, err := json.Marshal(asset)
        if err != nil {
            return err
        }

        err = ctx.GetStub().PutState(asset.ID, assetJSON)
        if err != nil {
            return fmt.Errorf("failed to put to world state. %v", err)
        }
    }

    return nil
}

// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
    asset := Asset{
        ID:             id,
        Color:          color,
        Size:           size,
        Owner:          owner,
        AppraisedValue: appraisedValue,
    }

    assetJSON, err := json.Marshal(asset)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
}

// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
    assetJSON, err := ctx.GetStub().GetState(id)
    if err != nil {
        return nil, fmt.Errorf("failed to read from world state. %v", err)
    }
    if assetJSON == nil {
        return nil, fmt.Errorf("%s does not exist", id)
    }

    var asset Asset
    err = json.Unmarshal(assetJSON, &asset)
    if err != nil {
        return nil, err
    }

    return &asset, nil
}

// UpdateAsset updates an existing asset in the world state with provided parameters.
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
    asset, err := s.ReadAsset(ctx, id)
    if err != nil {
        return err
    }

    asset.Color = color
    asset.Size = size
    asset.Owner = owner
    asset.AppraisedValue = appraisedValue

    assetJSON, err := json.Marshal(asset)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(id, assetJSON)
}

// DeleteAsset deletes an given asset from the world state.
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
    err := ctx.GetStub().DelState(id)
    return err
}

func main() {

    chaincode, err := contractapi.NewChaincode(new(SmartContract))

    if err != nil {
        fmt.Printf("Error creating asset-transfer-basic chaincode: %s", err.Error())
        return
    }

    if err := chaincode.Start(); err != nil {
        fmt.Printf("Error starting asset-transfer-basic chaincode: %s", err.Error())
    }
}

然后,我们可以使用Hyperledger Fabric SDK调用链码,并创建一个用户界面来管理商品信息。

调用链码:

// 创建商品
contract.submitTransaction("CreateAsset", "asset7", "purple", "20", "Lisa", "900");

// 查询商品
byte[] queryResult = contract.evaluateTransaction("ReadAsset", "asset7");

用户界面:

可以使用Java Swing、JavaFX或者Web框架(例如:Spring MVC)创建一个用户界面,让用户创建、查询、更新和删除商品信息。用户界面可以显示商品的详细信息和流转过程。

7. 总结:Java在区块链应用开发中的角色

Java在区块链应用开发中扮演着重要的角色,Web3j和Hyperledger Fabric SDK 是两个常用的工具,分别适用于不同的区块链平台和应用场景。Web3j 简化了与以太坊的交互,方便构建 DApp。Hyperledger Fabric SDK 适用于企业级应用,提供权限控制和高性能。选择合适的工具,并结合Java的生态系统,可以快速构建可靠、可扩展的区块链应用。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注