How to use java scaffolding to call contract interface

How to use java scaffolding to call the contract interface (taking HelloWorld as an example to provide a demonstration)

Prerequisites

Deploy the fisco-bcos blockchain network. Here is a one-click deployment of the blockchain network through the webase dependency package.
Deploy the blockchain network on the virtual machine or cloud server and open the corresponding port. When using the Windows side to import the Java project, please use the intranet IP or public IP to export the project.

1. Write the contract HelloWorld.sol

pragma solidity ^0.4.25;
contract HelloWorld{
    string str;
    
    constructor(){
        str = "Hello World";
    }
    
    function setStr(string memory _str)public {
        str = _str;
    }
    
    function getStr()public view returns(string memory){
        return str;
    }
}

2. Deploy and compile contracts

Insert image description here

3. Export java project

The first step is to click on the Java project everywhere
Insert image description here

The second step, select the node, select the user, select the compiled HelloWorld contract
Insert image description here
Insert image description here
The third step, import the downloaded Java project into IDEA
Open IDEA, click on the file in the upper left corner, click Open, and then find the downloaded project to import. It takes a certain amount of time to build the project to download the gradel package manager and dependencies
and build successfully after importing, as shown below:

Insert image description here

4. Analyze the java files under the raw package and service package in the project source code directoryInsert image description here

HelloWorld.java

HelloWorld.java is a Java wrapper class of a contract, used to interact with HelloWorl.sol.
The main functions of this class include:

  1. Define the binary representation of the contract: define the bytecode representation of the contract throughBINARY_ARRAY, BINARYdefine the binary code of the contract in non-SM mode,SM_BINARY Defines the contract binary code of SM mode.
  2. Define the contract'sABI (Application Binary Interface): Define the contract's throughABI_ARRAY, that is, the description of the contract's functions and parameters. ABI
  3. defines the function name of the contract: FUNC_SETSTR indicates the function name to set the string variable, FUNC_GETSTR indicates the function name to obtain the string variable.
  4. Implement the deployment and loading methods of the contract:
    • deployThe method is used to deploy the HelloWorld contract to the blockchain and return an instance of HelloWorld.
    • loadThe method is used to load the deployedHelloWorld contract. You need to provide the contract address, blockchain client and key pair.
  5. Specific operation methods to implement contract functions:
    • setStrThe method is used to set the value of the string variable in the HelloWorld contract.
    • getStrThe method is used to obtain the value of the string variable in the HelloWorld contract.
  6. Initialize the contract in the constructor:
    • By passing in the contract address, blockchain client and key pair, call the construction method of the parent classContract in the constructor to initialize.

In general, HelloWorld.java defines a package class that interacts with HelloWorld contracts, providing methods for deploying and loading contracts, as well as specific Methods to operate contract functions. It manages the interaction logic with the contract and provides a simplified interface for applications to interact and operate with the contract.

The code looks like this:

package org.example.HelloWorld.raw;
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 = {
    
    
            // 合约的字节码...
    };
    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);
    // 合约的ABI(应用程序二进制接口)
    public static final String[] ABI_ARRAY = [
            // 合约的ABI...
    ];
    public static final String ABI = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", ABI_ARRAY);
    // 函数名称
    public static final String FUNC_SETSTR = "setStr";
    public static final String FUNC_GETSTR = "getStr";
    // 合约的构造函数
    protected HelloWorld(String contractAddress, Client client, CryptoKeyPair credential) {
    
    
        super(getBinary(client.getCryptoSuite()), contractAddress, client, credential);
    }
    // 根据CryptoSuite选择正确的二进制表示
    public static String getBinary(CryptoSuite cryptoSuite) {
    
    
        return (cryptoSuite.getCryptoTypeConfig() == CryptoType.ECDSA_TYPE ? BINARY : SM_BINARY);
    }
    // 设置合约中字符串变量的值
    public TransactionReceipt setStr(String _str) {
    
    
        final Function function = new Function(
                FUNC_SETSTR, 
                Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(_str)), 
                Collections.<TypeReference<?>>emptyList());
        return executeTransaction(function);
    }
    // 异步方式设置合约中字符串变量的值,并附带回调函数
    public void setStr(String _str, TransactionCallback callback) {
    
    
        final Function function = new Function(
                FUNC_SETSTR, 
                Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(_str)), 
                Collections.<TypeReference<?>>emptyList());
        asyncExecuteTransaction(function, callback);
    }
    // 获取设置合约中字符串变量的签名交易
    public String getSignedTransactionForSetStr(String _str) {
    
    
        final Function function = new Function(
                FUNC_SETSTR, 
                Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(_str)), 
                Collections.<TypeReference<?>>emptyList());
        return createSignedTransaction(function);
    }
    // 从交易回执中获取setStr函数的输入值
    public Tuple1<String> getSetStrInput(TransactionReceipt transactionReceipt) {
    
    
        String data = transactionReceipt.getInput().substring(10);
        final Function function = new Function(FUNC_SETSTR, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {
    
    }));
        List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
        return new Tuple1<String>((String) results.get(0).getValue());
    }
    // 获取合约中字符串变量的值
    public String getStr() throws ContractException {
    
    
        final Function function = new Function(FUNC_GETSTR, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {
    
    }));
        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()), "");
    }
}
HelloWorldService.java

HelloWorldServiceThe role of the class is to provide operations and services for theHelloWorld contract.
Specifically, the main functions of this class include:

  1. Set the value of the contract string variable: By calling the setStr method, you can set a new value to the string variable in the HelloWorld contract.
  2. Get the value of the contract string variable: By calling the getStr method, you can get the current string variable value from the HelloWorld contract.
  3. InitializationAssembleTransactionProcessor: In the initialization methodinit, create an instanceAssembleTransactionProcessor for sending transactions and calling contract methods .
  4. Configure contract address and blockchain client: By injecting SystemConfig and Client instances, you can set the contract address and blockchain client.

In general, HelloWorldService类 encapsulates the operations and services of the HelloWorld contract, provides methods for setting and obtaining contract string variables, and is responsible for Initializes and manages the transaction handlerTransactionProcessor that interacts with the contract. As part of an application, it can be used to interact with the blockchain and accessHelloWorldthe state and functionality of the contract

The code looks like this:

package org.example.HelloWorld.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.HelloWorld.model.bo.HelloWorldSetStrInputBO;
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
@NoArgsConstructor
@Data
public class HelloWorldService {
    
    
  // 合约的ABI定义
  public static final String ABI = org.example.HelloWorld.utils.IOUtil.readResourceAsString("abi/HelloWorld.abi");
  // 合约的二进制代码定义
  public static final String BINARY = org.example.HelloWorld.utils.IOUtil.readResourceAsString("bin/ecc/HelloWorld.bin");
  public static final String SM_BINARY = org.example.HelloWorld.utils.IOUtil.readResourceAsString("bin/sm/HelloWorld.bin");
  // 合约的地址
  @Value("${system.contract.helloWorldAddress}")
  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 setStr(HelloWorldSetStrInputBO input) throws Exception {
    
    
    return this.txProcessor.sendTransactionAndGetResponse(this.address, ABI, "setStr", input.toArgs());
  }
  // 调用合约中的getStr函数获取字符串变量的值
  public CallResponse getStr() throws Exception {
    
    
    return this.txProcessor.sendCall(this.client.getCryptoSuite().getCryptoKeyPair().getAddress(), this.address, ABI, "getStr", Arrays.asList());
  }
}
ServiceManager.java

The purpose of this classServiceManager is to manage multiple instances ofHelloWorldService and dynamically create and configure these instances based on the private key list.
Specifically, the main functions of this class include:

  1. Read the system configuration and private key list: In the initialization methodinit, get the private key list from the system configurationSystemConfig and save it to the private key list (hexPrivateKeyList).

  2. CreateHelloWorldService instance: In the @Bean annotated methodinitHelloWorldServiceManager, loop through according to the size of the private key list List of private keys. For each private key, create a cipher suiteCryptoSuiteand key pairCryptoKeyPair. Then, create a based on the HelloWorldcontract address, blockchain client instance and transaction processorTransactionProcessor in the system configuration instance and set the address, client, and transaction processor to the corresponding values. Finally, add the instance to , using the user address as the key. HelloWorldServiceHelloWorldServiceConcurrentHashMap

  3. ManageHelloWorldService instances: Inject instances into the Spring container through @Bean annotations, which can be used annotation to obtain the instance. Combined withHelloWorldService@Qualifier("HelloWorldService")@Autowired

    • The advantage of this is that multipleHelloWorldService instances can be dynamically created and configured based on the user's private key, each with a separate user address and transaction processor.
    • Instances managed through can be used by other components in the application to facilitate contract operations and data access. ServiceManagerHelloWorldService

In general, the ServiceManager class acts as the manager of the HelloWorldService instance, dynamically creating and configuring multiple based on the private key list HelloWorldService instance, so that each instance can correspond to different user addresses and transaction processors, facilitating blockchain interactive operations.

The code looks like this:

package org.example.HelloWorld.service;
import java.lang.Exception;
import java.lang.String;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.example.HelloWorld.config.SystemConfig;
import org.fisco.bcos.sdk.client.Client;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
@Slf4j
public class ServiceManager {
    
    
  // 系统配置
  @Autowired
  private SystemConfig config;
  // 区块链客户端实例
  @Autowired
  private Client client;
  // 私钥列表
  List<String> hexPrivateKeyList;
  // 初始化方法,在构造后执行
  @PostConstruct
  public void init() {
    
    
    hexPrivateKeyList = Arrays.asList(this.config.getHexPrivateKey().split(","));
  }
  /**
   * @notice: 必须使用 @Qualifier("HelloWorldService") 结合 @Autowired 才能获取到该 Bean
   */
  @Bean("HelloWorldService")
  public Map<String, HelloWorldService> initHelloWorldServiceManager() throws Exception {
    
    
    Map<String, HelloWorldService> serviceMap = new ConcurrentHashMap<>(this.hexPrivateKeyList.size());
    for (int i = 0; i < this.hexPrivateKeyList.size(); i++) {
    
    
      String privateKey = this.hexPrivateKeyList.get(i);
      if (privateKey.startsWith("0x") || privateKey.startsWith("0X")) {
    
    
        privateKey = privateKey.substring(2);
      }
      if (privateKey.isEmpty()) {
    
    
        continue;
      }
      // 创建密码套件和密钥对
      org.fisco.bcos.sdk.crypto.CryptoSuite cryptoSuite = new org.fisco.bcos.sdk.crypto.CryptoSuite(this.client.getCryptoType());
      org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair cryptoKeyPair = cryptoSuite.createKeyPair(privateKey);
      // 获取用户地址
      String userAddress = cryptoKeyPair.getAddress();
      log.info("++++++++hexPrivateKeyList[{}]:{},userAddress:{}", i, privateKey, userAddress);
      // 创建HelloWorldService实例,并设置地址、客户端和交易处理器
      HelloWorldService helloWorldService = new HelloWorldService();
      helloWorldService.setAddress(this.config.getContract().getHelloWorldAddress());
      helloWorldService.setClient(this.client);
      org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor txProcessor = 
        org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory.createAssembleTransactionProcessor(this.client, cryptoKeyPair);
      helloWorldService.setTxProcessor(txProcessor);
      // 将Service添加到Map中,以用户地址为键
      serviceMap.put(userAddress, helloWorldService);
    }
    log.info("++++++++HelloWorldService map:{}", serviceMap);
    return serviceMap;
  }
}

5. Call the contract interface through the test class

HelloWordTest, it is a test class, mainly used to test the methods in HelloWorldService.
The annotations in the class are explained as follows:

  • @RunWith(SpringRunner.class): Specifies to use SpringRunner to run tests, which is a JUnit runner used to run Spring Boot integration tests.
  • @SpringBootTest(classes = Application.class): Indicates that this is a Spring Boot integration test class, and the Application class is specified as the startup class to load the Spring Boot context.
  • @WebAppConfiguration: Marks this as a configuration class for a web application and provides a web context for the test class.
    This class contains a test method testHello(), which is used to test the execution results of some methods in HelloWorldService. The specific test logic is as follows:
  1. Through the @Autowired and @Qualifier annotations, a Map<String, HelloWorldService> is injected to obtain the corresponding to the specified user address. a>HelloWorldService instance.
  2. Create a HelloWorldSetStrInputBO object and set the value of the _str field to "333".
  3. Call the method of helloWorldService and pass the set input parameters in to execute method. setStr()setStr
  4. Call the method of helloWorldService to obtain the return result of the method in the contract. getStr()getStr
  5. Print the test results, output the value of helloWorldService and the value of callResponse.getValues().

Through this test class, verify the correctness of the methods in HelloWorldService when calling the contract interface, and check whether the returned results are as expected.

The test class code is as follows:

package org.example.HelloWorld;
import org.example.HelloWorld.model.bo.HelloWorldSetStrInputBO;
import org.example.HelloWorld.service.HelloWorldService;
import org.fisco.bcos.sdk.transaction.model.dto.CallResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class HelloWordTest {
    
    
    @Autowired
    @Qualifier("HelloWorldService")
    private Map<String, HelloWorldService> testServiceMap;
    @Test
    public void testHello() throws Exception {
    
    
        // 用户地址
        String user = "0xf6d9791f45c124ad528793aaa559b7bb2c93e190";
        // 根据用户地址获取对应的HelloWorldService实例
        HelloWorldService helloWorldService = testServiceMap.get(user);
        // 创建HelloWorldSetStrInputBO对象并设置值
        HelloWorldSetStrInputBO helloWorldSetStrInputBO = new HelloWorldSetStrInputBO();
        helloWorldSetStrInputBO.set_str("333");
        // 调用合约的setStr方法
        helloWorldService.setStr(helloWorldSetStrInputBO);
        // 调用合约的getStr方法
        CallResponse callResponse = helloWorldService.getStr();
        // 打印调用结果
        System.out.println("使用java——sdk脚手架通过channel_port:20200方式调用合约接口");
        System.out.println("helloWorldService = " + helloWorldService);
        System.out.println("callResponse.getValues() = " + callResponse.getValues());
    }
}

The test results are as follows:
Insert image description here
Verify whether the call has been successful on the webase platform
Insert image description here

Called successfully! ! !

Guess you like

Origin blog.csdn.net/weixin_63706760/article/details/132036812