🎈亲爱的读者朋友们:
如果你觉得这篇文章对你有所帮助,恳请你为我点个赞👍。你的每一个赞都是对我辛勤创作的认可,是我继续前行的动力源泉。
同时,也欢迎你关注我的 CSDN 博客。在这里,我会持续分享更多实用的技术干货、独特的编程见解和宝贵的学习经验。
期待你的点赞与关注,让我们携手共进,共创更精彩的技术世界!
搭建区块链系统和管理平台分别用的的fisco、webase。
关于我们在利用java开发DApp(去中心化引用),与区块链系统交互,可以用:
1.webase前置服务给开发者提供的api:我们在搭建好fisco链之后,在搭一个webase-front服务,我们就能通过front服务提供的api,间接在fisco上面,进行部署、调用合约、获取块高,等与区块链系统交互的行为。
webase-front接口说明: 接口说明 — WeBASE v1.5.5 文档 (webasedoc.readthedocs.io)
2.利用fisco官方为Java开发者提供的 fisco-sdk:通过引入他调用相关的方法,与区块链系统交互。
fisco-java-sdk快速入门: 快速入门 — FISCO BCOS v2 v2.9.0 文档 (fisco-bcos-documentation.readthedocs.io)
嗯,...但是按照官方文档,一旦我们项目大起来,其实就相对比较麻烦了(也不麻烦),然后我们可以利用webase-front节点控制台,将合约上传后,将合约对应的Java项目一键导出。
如下图:
这次我们利用webase-front搭建好,默认自带的Asset合约,来进行演示。
PS: 利用fisco-sdk 开发Dapp,先建议看fisco、webase有一定的基础之后,再建议尝试。
利用fisco-jdk第一次交互,可以看看这个,里面引用了Linux的IDEA、Maven、Java的安装:fisco Java-sdk 快速入门案例-CSDN博客
梦开始的地方
项目导出后,是一个boot项目,包管理工具是gradle,我习惯用Maven,因此我新建一个Maven项目,并将包移植到我自己的项目里。
pom.xml
我的 jdk 是 14。测试了jdk 11、8、14 ,就14不会报错对应fisco-sdk 2.9.2
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>fiscoDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>14</java.version>
<spring-boot.version>2.6.13</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<!-- fisco bcos -->
<dependency>
<groupId>org.fisco-bcos.java-sdk</groupId>
<artifactId>fisco-bcos-java-sdk</artifactId>
<version>2.9.2</version>
<!-- 2.7.2 version, I think do not match jdk-14 -->
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
项目结构大体概览图
第一个红框:
contracts:存放合约的目录,没啥用。
第二个红框:
接触java web之后,应该都知道。
第三个红框:
abi:与合约交互用的abi; bin: 合约编译之后生成二进制文件; conf: 存放证书用到的目录。
接下来,我们从第2个红框开始,从上往下,依次讲解学习主要的目录:
config
SystemConfig
package org.example.demo.config;
import java.lang.String;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.context.annotation.Configuration;
/**
* 自动配置类 对应 配置文件信息(网络/群组/证书文件。。。)
*/
@Data
@Configuration
@ConfigurationProperties(
prefix = "system"
)
public class SystemConfig {
private String peers;
private int groupId = 1;
private String certPath;
private String hexPrivateKey;
@NestedConfigurationProperty
private ContractConfig contract;
}
boot的自动装配类,当boot项目启动时,自动读取application.properties/yaml/yml中的自定义配置信息。并且,我们可以在spring 容器中获取到他。
对应的配置信息:
application.properties
# 搭建区块链第一个节点(node0)的,ip:port
system.peers=127.0.0.1:20200
# 合约所属群组id
system.groupId=1
# 证书所放的目录
system.certPath=conf
# 可选: 私钥文件
system.hexPrivateKey=19eb7fd7a47a487265c6c109d560929deaee8e378fd4990dcce7cebd8a34f195
#可选: 合约地址
system.contract.assetAddress=0x385dfad96f483042686273d5fda5c379b111bb20
server.port=8088
server.session.timeout=60
banner.charset=UTF-8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
ContractConfig
@Data
public class ContractConfig {
private String assetAddress;
}
自动配置类所需要的类,创建对应的对象,将地址填充进去对应的字段。
PS:属性名要对应配置字段。
SdkBeanConfig
package org.example.demo.config;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.config.ConfigOption;
import org.fisco.bcos.sdk.config.exceptions.ConfigException;
import org.fisco.bcos.sdk.config.model.ConfigProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 读取我们配置类的信息,初始化Client
**/
@Configuration
@Slf4j
public class SdkBeanConfig {
@Autowired
private SystemConfig config;
/**
* 读取配置信息,初始化client并返回
*/
@Bean
public Client client() throws Exception {
String certPaths = this.config.getCertPath();
String[] possibilities = certPaths.split(",|;");
for(String certPath: possibilities ) {
try{
ConfigProperty property = new ConfigProperty();
configNetwork(property); // concat network
configCryptoMaterial(property,certPath); // concat cerpath
ConfigOption configOption = new ConfigOption(property);
Client client = new BcosSDK(configOption).getClient(config.getGroupId());
BigInteger blockNumber = client.getBlockNumber().getBlockNumber();
log.error("Chain connect successful. Current block number {}", blockNumber);
configCryptoKeyPair(client);
log.error("is Gm:{}, address:{}", client.getCryptoSuite().cryptoTypeConfig == 1, client.getCryptoSuite().getCryptoKeyPair().getAddress());
return client;
}
catch (Exception ex) {
log.error(ex.getMessage());
try{
Thread.sleep(5000);
}catch (Exception e) {}
}
}
throw new ConfigException("Failed to connect to peers:" + config.getPeers());
}
/**
* 设置 network
* @param configProperty
*/
public void configNetwork(ConfigProperty configProperty) {
String peerStr = config.getPeers();
List<String> peers = Arrays.stream(peerStr.split(",")).collect(Collectors.toList());
Map<String, Object> networkConfig = new HashMap<>();
networkConfig.put("peers", peers);
configProperty.setNetwork(networkConfig);
}
/**
* 设置 证书
* @param configProperty
* @param certPath
*/
public void configCryptoMaterial(ConfigProperty configProperty,String certPath) {
Map<String, Object> cryptoMaterials = new HashMap<>();
cryptoMaterials.put("certPath", certPath);
configProperty.setCryptoMaterial(cryptoMaterials);
}
/**
* 设置密钥
* @param client
*/
public void configCryptoKeyPair(Client client) {
if (config.getHexPrivateKey() == null || config.getHexPrivateKey().isEmpty()){
client.getCryptoSuite().setCryptoKeyPair(client.getCryptoSuite().createKeyPair());
return;
}
String privateKey;
if (!config.getHexPrivateKey().contains(",")) {
privateKey = config.getHexPrivateKey();
} else {
String[] list = config.getHexPrivateKey().split(",");
privateKey = list[0];
}
if (privateKey.startsWith("0x") || privateKey.startsWith("0X")) {
privateKey = privateKey.substring(2);
config.setHexPrivateKey(privateKey);
}
client.getCryptoSuite().setCryptoKeyPair(client.getCryptoSuite().createKeyPair(privateKey));
}
}
我们读取了SystemConfig中的配置信息,封装到ConfigProperty对象里,并又将其封装到ConfigOption这个对象里面,通过下面这段代码
Client client = new BcosSDK(configOption).getClient(config.getGroupId());
获得了一个Client,通过调用Client的方法我们能与fisco交互,可以获取块高等..。
service
AssetService
package org.example.demo.service;
import java.lang.Exception;
import java.lang.String;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.example.demo.model.bo.AssetBalancesInputBO;
import org.example.demo.model.bo.AssetIssueInputBO;
import org.example.demo.model.bo.AssetSendInputBO;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor;
import org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory;
import org.fisco.bcos.sdk.transaction.model.dto.CallResponse;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* 对应合约的Service,方法 -> 合约变量 和 合约函数
* 发送交易 获取 响应
*/
@Service
@NoArgsConstructor
@Data
public class AssetService {
public static final String ABI = org.example.demo.utils.IOUtil.readResourceAsString("abi/Asset.abi");
public static final String BINARY = org.example.demo.utils.IOUtil.readResourceAsString("bin/ecc/Asset.bin");
public static final String SM_BINARY = org.example.demo.utils.IOUtil.readResourceAsString("bin/ecc/Asset.bin");
@Value("${system.contract.assetAddress}")
private String address;
@Autowired
private Client client;
AssembleTransactionProcessor txProcessor;
@PostConstruct
public void init() throws Exception {
this.txProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(this.client, this.client.getCryptoSuite().getCryptoKeyPair());
}
public TransactionResponse issue(AssetIssueInputBO input) throws Exception {
return this.txProcessor.sendTransactionAndGetResponse(this.address, ABI, "issue", input.toArgs());
}
public TransactionResponse send(AssetSendInputBO input) throws Exception {
return this.txProcessor.sendTransactionAndGetResponse(this.address, ABI, "send", input.toArgs());
}
public CallResponse balances(AssetBalancesInputBO input) throws Exception {
return this.txProcessor.sendCall(this.client.getCryptoSuite().getCryptoKeyPair().getAddress(), this.address, ABI, "balances", input.toArgs());
}
public CallResponse issuer() throws Exception {
return this.txProcessor.sendCall(this.client.getCryptoSuite().getCryptoKeyPair().getAddress(), this.address, ABI, "issuer", Arrays.asList());
}
}
AssertService就是webase-front遵循业务层规则,生成的基于fisco-sdk封装的service类。通过这个service类,我们能与合约进行交互。
看代码,我们都是调用了这个 AssembleTransactionProcessor对象的上方法,
1.调用合约函数,是调用了这个方法。
方法参数为: 调用函数的合约地址、合约abi、被调用合约函数的函数名、函数参数列表
2.获取状态变量,是调用这个方法。
方法参数为:调用者的地址、调用函数的合约地址、合约abi、被调用合约函数的函数名、函数参数列表
raw
Asset
package org.example.demo.raw;
import java.math.BigInteger;
import java.util.ArrayList;
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.Address;
import org.fisco.bcos.sdk.abi.datatypes.Event;
import org.fisco.bcos.sdk.abi.datatypes.Function;
import org.fisco.bcos.sdk.abi.datatypes.Type;
import org.fisco.bcos.sdk.abi.datatypes.generated.Uint256;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple2;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.contract.Contract;
import org.fisco.bcos.sdk.contract.precompiled.crud.TableCRUDService;
import org.fisco.bcos.sdk.crypto.CryptoSuite;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.eventsub.EventCallback;
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;
/**
* 这里就是我们能跟区块链系统中的合约对应交互的java类
* 发送交易 获取凭证
*
* 这两个最终执行逻辑是同一个类上的不同方法
*/
@SuppressWarnings("unchecked")
public class Asset extends Contract {
public static final String[] BINARY_ARRAY = {"608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061044f806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631d1438481461006757806327e235e3146100be578063867904b414610115578063d0679d3414610162575b600080fd5b34801561007357600080fd5b5061007c6101af565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100ca57600080fd5b506100ff600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506101d4565b6040518082815260200191505060405180910390f35b34801561012157600080fd5b50610160600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506101ec565b005b34801561016e57600080fd5b506101ad600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610299565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60016020528060005260406000206000915090505481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561024757610295565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b5050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156102e55761041f565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055507f3990db2d31862302a685e8086b5755072a6e2b5b780af1ee81ece35ee3cd3345338383604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a15b50505600a165627a7a723058204e0edbb0e9bfd782dfaee2a435005414f5f84d50f4ead01144060672399fe6720029"};
public static final String BINARY = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", BINARY_ARRAY);
public static final String[] SM_BINARY_ARRAY = {};
public static final String SM_BINARY = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", SM_BINARY_ARRAY);
public static final String[] ABI_ARRAY = {"[{\"constant\":true,\"inputs\":[],\"name\":\"issuer\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"issue\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"send\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Sent\",\"type\":\"event\"}]"};
public static final String ABI = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", ABI_ARRAY);
// 调用合约对应的函数名 和状态变量名
public static final String FUNC_ISSUER = "issuer";
public static final String FUNC_BALANCES = "balances";
public static final String FUNC_ISSUE = "issue";
public static final String FUNC_SEND = "send";
public static final Event SENT_EVENT = new Event("Sent",
Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}, new TypeReference<Address>() {}, new TypeReference<Uint256>() {}));
/**
* 下面 load函数本质上和这个一样都是根据现有合约地址加载,以调用
* @param contractAddress
* @param client
* @param credential
*/
protected Asset(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 String issuer() throws ContractException {
final Function function = new Function(FUNC_ISSUER,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}));
return executeCallWithSingleValueReturn(function, String.class);
}
public BigInteger balances(String param0) throws ContractException {
final Function function = new Function(FUNC_BALANCES,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(param0)),
Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
return executeCallWithSingleValueReturn(function, BigInteger.class);
}
/**
* 无回调函数的对应和合约调用
*
* @param receiver
* @param amount
* @return TransactionReceipt 交易凭证
*/
public TransactionReceipt issue(String receiver, BigInteger amount) {
final Function function = new Function(
FUNC_ISSUE,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(receiver),
new org.fisco.bcos.sdk.abi.datatypes.generated.Uint256(amount)),
Collections.<TypeReference<?>>emptyList());
return executeTransaction(function);
}
/**
* 调用合约函数,并传入一个回调函数
*
* @param receiver
* @param amount
* @param callback
*/
public void issue(String receiver, BigInteger amount, TransactionCallback callback) {
final Function function = new Function(
FUNC_ISSUE,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(receiver),
new org.fisco.bcos.sdk.abi.datatypes.generated.Uint256(amount)),
Collections.<TypeReference<?>>emptyList());
asyncExecuteTransaction(function, callback);
}
/**
* 获取Issue合约函数的交易签名
* @param receiver
* @param amount
* @return
*/
public String getSignedTransactionForIssue(String receiver, BigInteger amount) {
final Function function = new Function(
FUNC_ISSUE,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(receiver),
new org.fisco.bcos.sdk.abi.datatypes.generated.Uint256(amount)),
Collections.<TypeReference<?>>emptyList());
return createSignedTransaction(function);
}
/**
* 获取 IssurInpt输入的参数
* @param transactionReceipt
* @return 元组(封装输入的数据)
*/
public Tuple2<String, BigInteger> getIssueInput(TransactionReceipt transactionReceipt) {
String data = transactionReceipt.getInput().substring(10);
final Function function = new Function(FUNC_ISSUE,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}, new TypeReference<Uint256>() {}));
List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
return new Tuple2<String, BigInteger>(
(String) results.get(0).getValue(),
(BigInteger) results.get(1).getValue()
);
}
public TransactionReceipt send(String receiver, BigInteger amount) {
final Function function = new Function(
FUNC_SEND,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(receiver),
new org.fisco.bcos.sdk.abi.datatypes.generated.Uint256(amount)),
Collections.<TypeReference<?>>emptyList());
return executeTransaction(function);
}
public void send(String receiver, BigInteger amount, TransactionCallback callback) {
final Function function = new Function(
FUNC_SEND,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(receiver),
new org.fisco.bcos.sdk.abi.datatypes.generated.Uint256(amount)),
Collections.<TypeReference<?>>emptyList());
asyncExecuteTransaction(function, callback);
}
public String getSignedTransactionForSend(String receiver, BigInteger amount) {
final Function function = new Function(
FUNC_SEND,
Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Address(receiver),
new org.fisco.bcos.sdk.abi.datatypes.generated.Uint256(amount)),
Collections.<TypeReference<?>>emptyList());
return createSignedTransaction(function);
}
public Tuple2<String, BigInteger> getSendInput(TransactionReceipt transactionReceipt) {
String data = transactionReceipt.getInput().substring(10);
final Function function = new Function(FUNC_SEND,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}, new TypeReference<Uint256>() {}));
List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
return new Tuple2<String, BigInteger>(
(String) results.get(0).getValue(),
(BigInteger) results.get(1).getValue()
);
}
public List<SentEventResponse> getSentEvents(TransactionReceipt transactionReceipt) {
List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog(SENT_EVENT, transactionReceipt);
ArrayList<SentEventResponse> responses = new ArrayList<SentEventResponse>(valueList.size());
for (Contract.EventValuesWithLog eventValues : valueList) {
SentEventResponse typedResponse = new SentEventResponse();
typedResponse.log = eventValues.getLog();
typedResponse.from = (String) eventValues.getNonIndexedValues().get(0).getValue();
typedResponse.to = (String) eventValues.getNonIndexedValues().get(1).getValue();
typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue();
responses.add(typedResponse);
}
return responses;
}
public void subscribeSentEvent(String fromBlock, String toBlock, List<String> otherTopics, EventCallback callback) {
String topic0 = eventEncoder.encode(SENT_EVENT);
subscribeEvent(ABI,BINARY,topic0,fromBlock,toBlock,otherTopics,callback);
}
public void subscribeSentEvent(EventCallback callback) {
String topic0 = eventEncoder.encode(SENT_EVENT);
subscribeEvent(ABI,BINARY,topic0,callback);
}
/**
* 加载已有的合约地址,返回一个已有的Contract对象
* @param contractAddress
* @param client
* @param credential
* @return
*/
public static Asset load(String contractAddress, Client client, CryptoKeyPair credential) {
return new Asset(contractAddress, client, credential);
}
/**
* 通过此方法,我们可以部署合约,产生一个新的Contract对象
* @param client
* @param credential
* @return
* @throws ContractException
*/
public static Asset deploy(Client client, CryptoKeyPair credential) throws ContractException {
return deploy(Asset.class, client, credential, getBinary(client.getCryptoSuite()), "");
}
public static class SentEventResponse {
public TransactionReceipt.Logs log;
public String from;
public String to;
public BigInteger amount;
}
}
其实对比上述AssetService和Asset上封装调用的方法,发现最终都是调用一类对象上的不同方法。因此,看看就可以了。
AssembleTransactionProcessor 是AssetService层封装调用的。
TransactionProcessor 是Aseet层封装调用的。
abi、bin、conf
- abi:存放合约abi的目录。定义外部调用合约的一种规则,本质上就是json文件,通过他,外部能与合约进行交互。
- bin:bin文件存放目录。合约编译之后,形成的二进制文件、我们部署合约需要用到他。
- conf: 存放fisco证书的目录。
测试文件
package org.example.demo;
import org.example.demo.model.bo.AssetBalancesInputBO;
import org.example.demo.model.bo.AssetIssueInputBO;
import org.example.demo.model.bo.AssetSendInputBO;
import org.example.demo.raw.Asset;
import org.example.demo.service.AssetService;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple2;
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.model.callback.TransactionCallback;
import org.fisco.bcos.sdk.transaction.model.dto.CallResponse;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.fisco.bcos.sdk.transaction.model.exception.ContractException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.math.BigInteger;
@SpringBootTest
public class AssetTest {
@Autowired
private AssetService assetService;
@Test
public void testAssetService() throws Exception {
String testAddress1 = "0xb537616a39a710d7590716c4422977953518c555";
String testAddress2 = "0x7700375543650bd19b716bb2a1d5bf609177ba39";
// 初始化
assetService.init();
// 调用issue
AssetIssueInputBO input = new AssetIssueInputBO();
input.setReceiver(testAddress1);
input.setAmount(new BigInteger("2"));
TransactionResponse issue = assetService.issue(input);
System.out.println(issue.getReturnMessage());
// 调用send
AssetSendInputBO send = new AssetSendInputBO();
send.setAmount(new BigInteger("1"));
send.setReceiver(testAddress2);
TransactionResponse send1 = assetService.send(send);
System.out.println(send1.getReturnMessage());
// 调用balances
AssetBalancesInputBO balancesBo = new AssetBalancesInputBO();
balancesBo.setArg0("0xb537616a39a710d7590716c4422977953518c555"); // 因为balances是一个map,因此我们需要传入一个用户地址,用来获取balance
CallResponse balances = assetService.balances(balancesBo);
System.out.println(1);
}
@Autowired
private Client client;
@Test
public void testAsset() throws ContractException {
String receiver = "0x7700375543650bd19b716bb2a1d5bf609177ba39";
BigInteger amount = new BigInteger("2");
CryptoKeyPair cryptoKeyPair = client.getCryptoSuite().getCryptoKeyPair();
// 1.部署新合约
Asset newInstance = Asset.deploy(client, cryptoKeyPair);
// 2.实现TransactionCallback类
TransactionCallback transactionCallback = new TransactionCallback() { //
@Override
// 这代码有问题,反正成功调用,这代码就是不调用
public void onResponse(TransactionReceipt receipt) {
System.out.println("我被调用了");
// String newAddress = receipt.getContractAddress();
// Asset load = Asset.load(newAddress, client, cryptoKeyPair);// 加载返回新合约的地址
// TransactionReceipt issue = load.issue(receiver, amount);
}
};
// 3.调用issue方法
newInstance.issue(receiver, amount,transactionCallback);
System.out.println(1);
TransactionReceipt transactionReceipt = newInstance.issue(receiver, amount);
// 4.获取issue方法的输入参数
Tuple2<String, BigInteger> issueInput = newInstance.getIssueInput(transactionReceipt);
}
}