From 78fcb7aa378b16cd7d836f645f0cf28d11947d04 Mon Sep 17 00:00:00 2001 From: xiaoJ <1143020035@qq.com> Date: Mon, 9 Feb 2026 17:56:39 +0800 Subject: [PATCH] first commit --- .gitignore | 4 +- README.md | 20 +-- .../java/com/org/fisco/AccountGenerator.java | 165 ++++++++++++++++++ src/main/java/com/org/fisco/BcosSDKTest.java | 55 ++++++ src/main/java/com/org/fisco/HelloWorld.java | 97 ++++++++++ .../com/org/fisco/HelloWorldWithAccount.java | 160 +++++++++++++++++ 6 files changed, 481 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/org/fisco/AccountGenerator.java create mode 100644 src/main/java/com/org/fisco/BcosSDKTest.java create mode 100644 src/main/java/com/org/fisco/HelloWorld.java create mode 100644 src/main/java/com/org/fisco/HelloWorldWithAccount.java diff --git a/.gitignore b/.gitignore index b489d62..58b20fd 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,6 @@ bin/ ### Mac OS ### .DS_Store -fisco/ \ No newline at end of file +/fisco/ + +/conf \ No newline at end of file diff --git a/README.md b/README.md index bf6f850..091920e 100644 --- a/README.md +++ b/README.md @@ -110,22 +110,4 @@ CryptoKeyPair keyPair = AccountGenerator.loadPEMAccount("account/alice.pem", Cry - 证书文件 - 群组配置 - 加密类型配置 - -## 开发环境 - -- IDE: IntelliJ IDEA(推荐) -- 操作系统: macOS / Linux / Windows -- Java 版本: JDK 8+ - -## 许可证 - -本项目采用开源许可证发布。 - -## 相关资源 - -- [FISCO BCOS 官方文档](https://fisco-bcos-documentation.readthedocs.io/) -- [FISCO BCOS Java SDK 文档](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/sdk/java_sdk/index.html) - -## 贡献 - -欢迎提交 Issue 和 Pull Request 来改进这个项目。 +将SDK证书拷贝到Java SDK的示例如下(这里假设SDK证书位于~/fisco/nodes/127.0.0.1/sdk目录) \ No newline at end of file diff --git a/src/main/java/com/org/fisco/AccountGenerator.java b/src/main/java/com/org/fisco/AccountGenerator.java new file mode 100644 index 0000000..bf1e887 --- /dev/null +++ b/src/main/java/com/org/fisco/AccountGenerator.java @@ -0,0 +1,165 @@ +package com.org.fisco; + +import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; +import org.fisco.bcos.sdk.crypto.CryptoSuite; +import org.fisco.bcos.sdk.model.CryptoType; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +/** + * 账户生成工具类 + * 支持生成PEM格式的账户文件 + */ +public class AccountGenerator { + + private static final String ACCOUNT_DIR = "account"; + + /** + * 生成PEM格式的账户文件 + * @param accountName 账户名称 + * @param cryptoType 加密类型 (0: ECDSA, 1: 国密SM) + */ + public static CryptoKeyPair generatePEMAccount(String accountName, int cryptoType) { + try { + // 创建账户目录 + File accountDir = new File(ACCOUNT_DIR); + if (!accountDir.exists()) { + accountDir.mkdirs(); + } + + // 创建加密套件 + CryptoSuite cryptoSuite = new CryptoSuite(cryptoType); + + // 生成密钥对 + CryptoKeyPair cryptoKeyPair = cryptoSuite.createKeyPair(); + + // 获取私钥和公钥 + String privateKey = cryptoKeyPair.getHexPrivateKey(); + String publicKey = cryptoKeyPair.getHexPublicKey(); + String address = cryptoKeyPair.getAddress(); + + // 保存为PEM格式 + String pemFilePath = ACCOUNT_DIR + "/" + accountName + ".pem"; + savePEMFile(pemFilePath, privateKey, publicKey, address, cryptoType); + + System.out.println("=== PEM账户生成成功 ==="); + System.out.println("账户名称: " + accountName); + System.out.println("账户地址: " + address); + System.out.println("公钥: " + publicKey); + System.out.println("私钥: " + privateKey); + System.out.println("文件路径: " + pemFilePath); + System.out.println("加密类型: " + (cryptoType == CryptoType.ECDSA_TYPE ? "ECDSA" : "国密SM")); + System.out.println("========================\n"); + + return cryptoKeyPair; + + } catch (Exception e) { + System.err.println("生成PEM账户失败: " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + /** + * 保存PEM格式文件 + */ + private static void savePEMFile(String filePath, String privateKey, String publicKey, + String address, int cryptoType) throws IOException { + try (FileWriter writer = new FileWriter(filePath)) { + writer.write("-----BEGIN PRIVATE KEY-----\n"); + writer.write(privateKey + "\n"); + writer.write("-----END PRIVATE KEY-----\n"); + writer.write("-----BEGIN PUBLIC KEY-----\n"); + writer.write(publicKey + "\n"); + writer.write("-----END PUBLIC KEY-----\n"); + writer.write("-----BEGIN ADDRESS-----\n"); + writer.write(address + "\n"); + writer.write("-----END ADDRESS-----\n"); + writer.write("-----BEGIN CRYPTO TYPE-----\n"); + writer.write(cryptoType + "\n"); + writer.write("-----END CRYPTO TYPE-----\n"); + } + } + + /** + * 从PEM文件加载账户 + * @param pemFilePath PEM文件路径 + * @param cryptoType 加密类型 + */ + public static CryptoKeyPair loadPEMAccount(String pemFilePath, int cryptoType) { + try { + // 读取PEM文件获取私钥 + String privateKey = readPrivateKeyFromPEM(pemFilePath); + + // 创建加密套件 + CryptoSuite cryptoSuite = new CryptoSuite(cryptoType); + + // 从私钥恢复密钥对 + CryptoKeyPair cryptoKeyPair = cryptoSuite.createKeyPair(privateKey); + + System.out.println("=== PEM账户加载成功 ==="); + System.out.println("文件路径: " + pemFilePath); + System.out.println("账户地址: " + cryptoKeyPair.getAddress()); + System.out.println("=======================\n"); + + return cryptoKeyPair; + + } catch (Exception e) { + System.err.println("加载PEM账户失败: " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + /** + * 从PEM文件读取私钥 + */ + private static String readPrivateKeyFromPEM(String filePath) throws IOException { + java.nio.file.Path path = java.nio.file.Paths.get(filePath); + String content = new String(java.nio.file.Files.readAllBytes(path)); + + // 提取私钥部分 + String[] lines = content.split("\n"); + for (int i = 0; i < lines.length; i++) { + if (lines[i].contains("BEGIN PRIVATE KEY")) { + return lines[i + 1].trim(); + } + } + throw new IOException("无法从PEM文件中读取私钥"); + } + + /** + * 主函数 - 演示如何生成账户 + */ + public static void main(String[] args) { + System.out.println("========================================"); + System.out.println(" FISCO BCOS 账户生成工具"); + System.out.println("========================================\n"); + + // 加密类型:0 表示 ECDSA,1 表示国密SM + int cryptoType = CryptoType.SM_TYPE; // 根据你的配置使用国密 + + // 1. 生成PEM格式账户 + System.out.println("【1】生成PEM格式账户:"); + CryptoKeyPair pemAccount = generatePEMAccount("user_pem", cryptoType); + + // 2. 演示加载PEM账户 + System.out.println("【2】加载PEM账户:"); + CryptoKeyPair loadedPemAccount = loadPEMAccount(ACCOUNT_DIR + "/user_pem.pem", cryptoType); + + // 3. 生成多个账户示例 + System.out.println("【3】生成多个用户账户:"); + generatePEMAccount("alice", cryptoType); + generatePEMAccount("bob", cryptoType); + generatePEMAccount("charlie", cryptoType); + + System.out.println("========================================"); + System.out.println("账户生成完成!账户文件保存在 " + ACCOUNT_DIR + " 目录下"); + System.out.println("========================================"); + + System.exit(0); + } +} + diff --git a/src/main/java/com/org/fisco/BcosSDKTest.java b/src/main/java/com/org/fisco/BcosSDKTest.java new file mode 100644 index 0000000..ef03610 --- /dev/null +++ b/src/main/java/com/org/fisco/BcosSDKTest.java @@ -0,0 +1,55 @@ +package com.org.fisco; + +import org.fisco.bcos.sdk.BcosSDK; +import org.fisco.bcos.sdk.client.Client; +import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; +import org.fisco.bcos.sdk.model.TransactionReceipt; +import org.fisco.bcos.sdk.config.exceptions.ConfigException; +import org.fisco.bcos.sdk.transaction.model.exception.ContractException; + +public class BcosSDKTest +{ + // 获取配置文件路径 + public final String configFile = BcosSDKTest.class.getClassLoader().getResource("config.toml").getPath(); + + public void testClient() throws ConfigException, ContractException { + // 初始化BcosSDK + BcosSDK sdk = BcosSDK.build(configFile); + // 为群组1初始化client + Client client = sdk.getClient(Integer.valueOf(1)); + + // 获取群组1的块高 + java.math.BigInteger blockNumber = client.getBlockNumber().getBlockNumber(); + + // 向群组1部署HelloWorld合约 + CryptoKeyPair cryptoKeyPair = client.getCryptoSuite().getCryptoKeyPair(); + HelloWorld helloWorld = HelloWorld.deploy(client, cryptoKeyPair); + + // 调用HelloWorld合约的get接口 + String getValue = helloWorld.get(); + + // 调用HelloWorld合约的set接口 + TransactionReceipt receipt = helloWorld.set("Hello, fisco"); + + // 打印结果 + System.out.println("当前块高: " + blockNumber); + System.out.println("合约地址: " + helloWorld.getContractAddress()); + System.out.println("获取的值: " + getValue); + System.out.println("交易哈希: " + receipt.getTransactionHash()); + System.out.println("交易状态: " + receipt.getStatus()); + } + + public static void main(String[] args) { + try { + BcosSDKTest test = new BcosSDKTest(); + test.testClient(); + System.out.println("测试执行成功!"); + } catch (Exception e) { + System.err.println("测试执行失败: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } + // 强制退出程序,避免后台线程继续运行 + System.exit(0); + } +} \ No newline at end of file diff --git a/src/main/java/com/org/fisco/HelloWorld.java b/src/main/java/com/org/fisco/HelloWorld.java new file mode 100644 index 0000000..a969945 --- /dev/null +++ b/src/main/java/com/org/fisco/HelloWorld.java @@ -0,0 +1,97 @@ +package com.org.fisco; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.fisco.bcos.sdk.abi.FunctionReturnDecoder; +import org.fisco.bcos.sdk.abi.TypeReference; +import org.fisco.bcos.sdk.abi.datatypes.Function; +import org.fisco.bcos.sdk.abi.datatypes.Type; +import org.fisco.bcos.sdk.abi.datatypes.Utf8String; +import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple1; +import org.fisco.bcos.sdk.client.Client; +import org.fisco.bcos.sdk.contract.Contract; +import org.fisco.bcos.sdk.crypto.CryptoSuite; +import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; +import org.fisco.bcos.sdk.model.CryptoType; +import org.fisco.bcos.sdk.model.TransactionReceipt; +import org.fisco.bcos.sdk.model.callback.TransactionCallback; +import org.fisco.bcos.sdk.transaction.model.exception.ContractException; + +@SuppressWarnings("unchecked") +public class HelloWorld extends Contract { + public static final String[] BINARY_ARRAY = {"608060405234801561001057600080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6102d7806101166000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ed3885e146100515780636d4ce63c146100ba575b600080fd5b34801561005d57600080fd5b506100b8600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061014a565b005b3480156100c657600080fd5b506100cf610164565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010f5780820151818401526020810190506100f4565b50505050905090810190601f16801561013c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060009080519060200190610160929190610206565b5050565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101fc5780601f106101d1576101008083540402835291602001916101fc565b820191906000526020600020905b8154815290600101906020018083116101df57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a7230582038c8771306a5ae97d4fdca0877d9b0956d82586811ffcf98bb8156535fdf44160029"}; + + public static final String BINARY = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", BINARY_ARRAY); + + public static final String[] SM_BINARY_ARRAY = {"608060405234801561001057600080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6102d7806101166000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063299f7f9d146100515780633590b49f146100e1575b600080fd5b34801561005d57600080fd5b5061006661014a565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b50610148600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101ec565b005b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e25780601f106101b7576101008083540402835291602001916101e2565b820191906000526020600020905b8154815290600101906020018083116101c557829003601f168201915b5050505050905090565b8060009080519060200190610202929190610206565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a7230582055a23c331239de7d84cefde97edd7668949c0b256facb94ed4e0ee831f023d590029"}; + + public static final String SM_BINARY = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", SM_BINARY_ARRAY); + + public static final String[] ABI_ARRAY = {"[{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"}; + + public static final String ABI = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", ABI_ARRAY); + + public static final String FUNC_SET = "set"; + + public static final String FUNC_GET = "get"; + + protected HelloWorld(String contractAddress, Client client, CryptoKeyPair credential) { + super(getBinary(client.getCryptoSuite()), contractAddress, client, credential); + } + + public static String getBinary(CryptoSuite cryptoSuite) { + return (cryptoSuite.getCryptoTypeConfig() == CryptoType.ECDSA_TYPE ? BINARY : SM_BINARY); + } + + public TransactionReceipt set(String n) { + final Function function = new Function( + FUNC_SET, + Arrays.asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(n)), + Collections.>emptyList()); + return executeTransaction(function); + } + + public byte[] set(String n, TransactionCallback callback) { + final Function function = new Function( + FUNC_SET, + Arrays.asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(n)), + Collections.>emptyList()); + return asyncExecuteTransaction(function, callback); + } + + public String getSignedTransactionForSet(String n) { + final Function function = new Function( + FUNC_SET, + Arrays.asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(n)), + Collections.>emptyList()); + return createSignedTransaction(function); + } + + public Tuple1 getSetInput(TransactionReceipt transactionReceipt) { + String data = transactionReceipt.getInput().substring(10); + final Function function = new Function(FUNC_SET, + Arrays.asList(), + Arrays.>asList(new TypeReference() {})); + List results = FunctionReturnDecoder.decode(data, function.getOutputParameters()); + return new Tuple1( + + (String) results.get(0).getValue() + ); + } + + public String get() throws ContractException { + final Function function = new Function(FUNC_GET, + Arrays.asList(), + Arrays.>asList(new TypeReference() {})); + return executeCallWithSingleValueReturn(function, String.class); + } + + public static HelloWorld load(String contractAddress, Client client, CryptoKeyPair credential) { + return new HelloWorld(contractAddress, client, credential); + } + + public static HelloWorld deploy(Client client, CryptoKeyPair credential) throws ContractException { + return deploy(HelloWorld.class, client, credential, getBinary(client.getCryptoSuite()), ""); + } +} diff --git a/src/main/java/com/org/fisco/HelloWorldWithAccount.java b/src/main/java/com/org/fisco/HelloWorldWithAccount.java new file mode 100644 index 0000000..f8d2870 --- /dev/null +++ b/src/main/java/com/org/fisco/HelloWorldWithAccount.java @@ -0,0 +1,160 @@ +package com.org.fisco; + +import org.fisco.bcos.sdk.BcosSDK; +import org.fisco.bcos.sdk.client.Client; +import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair; +import org.fisco.bcos.sdk.model.TransactionReceipt; +import org.fisco.bcos.sdk.config.exceptions.ConfigException; +import org.fisco.bcos.sdk.transaction.model.exception.ContractException; +import org.fisco.bcos.sdk.model.CryptoType; + +/** + * 使用指定账户发送HelloWorld合约交易 + * 支持PEM格式的账户 + */ +public class HelloWorldWithAccount { + + // 获取配置文件路径 + public final String configFile = HelloWorldWithAccount.class.getClassLoader() + .getResource("config.toml").getPath(); + + // 账户目录 + private static final String ACCOUNT_DIR = "account"; + + /** + * 使用PEM格式账户发送交易 + * @param pemAccountName PEM账户名称 + * @param cryptoType 加密类型 + */ + public void testWithPEMAccount(String pemAccountName, int cryptoType) + throws ConfigException, ContractException { + + System.out.println("\n========================================"); + System.out.println("使用PEM格式账户发送交易"); + System.out.println("========================================"); + + // 初始化BcosSDK + BcosSDK sdk = BcosSDK.build(configFile); + + // 为群组1初始化client + Client client = sdk.getClient(Integer.valueOf(1)); + + // 从PEM文件加载账户 + String pemFilePath = ACCOUNT_DIR + "/" + pemAccountName + ".pem"; + CryptoKeyPair cryptoKeyPair = AccountGenerator.loadPEMAccount(pemFilePath, cryptoType); + + if (cryptoKeyPair == null) { + System.err.println("加载PEM账户失败!"); + return; + } + + // 部署HelloWorld合约 + System.out.println("【1】部署HelloWorld合约..."); + HelloWorld helloWorld = HelloWorld.deploy(client, cryptoKeyPair); + String contractAddress = helloWorld.getContractAddress(); + System.out.println("合约部署成功!"); + System.out.println("合约地址: " + contractAddress); + System.out.println("部署账户: " + cryptoKeyPair.getAddress()); + + // 调用get接口 + System.out.println("\n【2】调用get接口..."); + String getValue = helloWorld.get(); + System.out.println("获取的值: " + getValue); + + // 调用set接口 + System.out.println("\n【3】调用set接口..."); + String newValue = "Hello from PEM account!"; + TransactionReceipt setReceipt = helloWorld.set(newValue); + System.out.println("set交易发送成功!"); + System.out.println("交易哈希: " + setReceipt.getTransactionHash()); + System.out.println("交易状态: " + setReceipt.getStatus()); + System.out.println("Gas使用: " + setReceipt.getGasUsed()); + + // 再次调用get验证 + System.out.println("\n【4】再次调用get接口验证..."); + String updatedValue = helloWorld.get(); + System.out.println("更新后的值: " + updatedValue); + + // 获取当前块高 + java.math.BigInteger blockNumber = client.getBlockNumber().getBlockNumber(); + System.out.println("\n当前块高: " + blockNumber); + + System.out.println("\n========================================"); + System.out.println("PEM账户交易测试完成!"); + System.out.println("========================================\n"); + } + + /** + * 使用已存在的合约地址和指定账户进行交互 + * @param contractAddress 已部署的合约地址 + * @param cryptoKeyPair 账户密钥对 + * @param client 客户端 + */ + public void interactWithExistingContract(String contractAddress, CryptoKeyPair cryptoKeyPair, Client client) + throws ContractException { + + System.out.println("\n========================================"); + System.out.println("与已存在的合约交互"); + System.out.println("========================================"); + + // 加载已存在的合约 + HelloWorld helloWorld = HelloWorld.load(contractAddress, client, cryptoKeyPair); + System.out.println("合约地址: " + contractAddress); + System.out.println("操作账户: " + cryptoKeyPair.getAddress()); + + // 调用get接口 + System.out.println("\n【1】调用get接口..."); + String getValue = helloWorld.get(); + System.out.println("当前值: " + getValue); + + // 调用set接口 + System.out.println("\n【2】调用set接口..."); + String newValue = "Updated by " + cryptoKeyPair.getAddress().substring(0, 10) + "..."; + TransactionReceipt receipt = helloWorld.set(newValue); + System.out.println("交易哈希: " + receipt.getTransactionHash()); + System.out.println("交易状态: " + receipt.getStatus()); + + // 验证 + System.out.println("\n【3】验证更新..."); + String updatedValue = helloWorld.get(); + System.out.println("更新后的值: " + updatedValue); + + System.out.println("\n========================================"); + System.out.println("合约交互完成!"); + System.out.println("========================================\n"); + } + + /** + * 主函数 - 演示如何使用PEM格式账户发送交易 + */ + public static void main(String[] args) { + try { + HelloWorldWithAccount test = new HelloWorldWithAccount(); + + // 根据config.toml配置,这里使用国密 + int cryptoType = CryptoType.SM_TYPE; + + System.out.println("##########################################"); + System.out.println("# FISCO BCOS HelloWorld 账户交易测试 #"); + System.out.println("##########################################"); + + // 使用PEM格式账户发送交易 + System.out.println("\n【测试】使用PEM格式账户发送交易"); + System.out.println("------------------------------------------"); + test.testWithPEMAccount("user_pem", cryptoType); + + System.out.println("\n##########################################"); + System.out.println("# 测试执行成功! #"); + System.out.println("##########################################\n"); + + } catch (Exception e) { + System.err.println("测试执行失败: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } + + // 强制退出程序 + System.exit(0); + } +} +