Constructing transactions and sending¶
Tags: “java-sdk” “Send transaction” “Send transaction using interface signature” “Assembly transaction” “Contract invocation” “
Note
The Java SDK also supports converting ‘solidity’ into ‘java’ files, calling the corresponding ‘java’ methods to deploy and invoke contracts, and also supports the way of constructing transactions to deploy and invoke contracts<./contracts_to_java.html>`_
Note
The data structure of the transaction can be found here<./transaction_data_struct.html>`_
1. Concept analysis: contract deployment and invocation¶
Contract operations can be divided into two categories: contract deployment and contract invocation。Among them, contract calls can be distinguished as “transactions” and “queries.”。
Contract DeploymentRefers to the creation and release of a new contract。The incoming data from the transaction creation is converted to EVM bytecode and executed, and the output of the execution is permanently stored as contract code。
contract invocationis a function that calls a deployed contract。Contract calls can be distinguished as “transactions” and “queries.”。
Query: The method modified by the view / pure modifier is generally called “query,” “query” does not need to be synchronized and sent to other nodes across the network consensus。
“Deal”: Only those that are not modified are called “transactions.”。, and the “transaction” needs to be sent to the entire network for the consensus of the chain。
Here is the difference between “transaction” and “query” in more detail。
| Content | Query | Transaction |
|---|---|---|
| Contract Performance | View Modifications | No view modifier |
| ABI Performance | "constant":true | "constant":false |
| Whether signature is required | 否 | Yes |
| rpc type | call | sendTransaction |
| Execution Node | All Nodes | All consensus nodes |
| Whether gas is consumed | 否 | Yes |
| Whether to change the storage status | 否 | Yes |
2. Premise: Prepare abi and binary documents for the contract¶
The console provides a dedicated contract compilation tool that allows developers to compile Solidity / webankblockchain-liquid (hereinafter referred to as WBC-Liquid) contract files to generate Java files, abi, and binary files(./contracts_to_java.html)。
By running the contract2java script, the generated abi and binary files are located in the contracts / sdk / abi and contracts / sdk / bin directories respectively (the files generated by the compilation of the national secret version are located in the contracts / sdk / abi / sm and contracts / sdk / bin / sm folders respectively)。You can copy files to the project directory, such as src / main / resources / abi and src / main / resources / bin。
For demonstration purposes, we used the following Solidity contract for HelloWorld。
pragma solidity ^0.6.0;
contract HelloWorld{
string public name;
constructor() public{
name = "Hello, World!";
}
function set(string memory n) public{
name = n;
}
}
Compile contracts to generate abi and binary:
# Switch to the directory where the console is located
$ cd ~/fisco/console
# Call the sol2java.sh script to compile the HelloWorld contract in the contracts / consolidation directory:
$ bash contract2java.sh solidity -p org.com.fisco -s contracts/solidity/HelloWorld.sol
$ tree . -L1
|-- abi # The abi directory generated by compilation, which stores the abi file compiled by the solidity contract
| |-- HelloWorld.abi
|-- bin # The bin directory generated by compilation, which stores the bin file compiled by the Solidity contract
| |-- HelloWorld.bin
|-- java # Store the compiled package path and Java contract file
| |-- org
| |-- com
| |-- fisco
| |-- HelloWorld.java # Solidity Compiled HelloWorld Java File
The abi and binary files for the ‘HelloWorld’ contract have now been generated
3. Initialize the SDK¶
Initialize the SDK based on the configuration file, such as:
/ / Initialize the BcosSDK object
BcosSDK sdk = BcosSDK.build(configFile);
/ / Obtain the client object. The group name is group0
Client client = sdk.getClient("group0");
/ / To construct an AssembleTransactionProcessor object, you must pass in the client object, the CryptoKeyPair object, and the path where the abi and binary files are stored。The abi and binary files need to be copied to the defined folder in the previous step。
CryptoKeyPair keyPair = client.getCryptoSuite().getCryptoKeyPair();
4. Initialize the AssembleTransactionProcessor object¶
Java SDK provides a way to directly deploy and invoke contracts based on abi and binary files。This scenario applies to the default situation, by creating and using the ‘AssembleTransactionProcessor’ object to complete contract-related deployment, invocation, and query operations。
AssembleTransactionProcessor transactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(client, keyPair, "src/main/resources/abi/", "src/main/resources/bin/");
In particular: If you only trade and query without deploying the contract, you do not need to copy the binary file and do not need to pass in the path of the binary file during construction, for example, the last parameter of the construction method can be passed in an empty string。
AssembleTransactionProcessor transactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(client, keyPair, "src/main/resources/abi/", "");
In particular: You can also not pass in any ABI file directory, and you can manually pass in the abi string for subsequent operations。
AssembleTransactionProcessor transactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(client, keyPair, "", "");
5. Deploy contracts synchronously¶
The deployment contract calls the deployByContractLoader method, passes in the contract name and constructor parameters, links the deployment contract, and obtains the result of the ‘TransactionResponse’。
/ / Deploy the HelloWorld contract。The first parameter is the contract name, and the second parameter is the list of contract constructors, which is List<Object>Type。
TransactionResponse response = transactionProcessor.deployByContractLoader("HelloWorld", new ArrayList<>());
/ / You can also manually pass in bin and abi
TransactionResponse response = transactionProcessor.deployAndGetResponse(abi, bin, new ArrayList<>(), null);
The data structure of ‘TransactionResponse’ is as follows:
returnCode: Response code returned。where 0 is success。
returnMessages: Error message returned。
TransactionReceipt: transaction receipt returned on the chain。
ContractAddress: Address of contract deployed or invoked。
events: If there is a trigger log record, the parsed log return value is returned, and a string in JSON format is returned。
returnObject: Return value Java type。
returnABIObject: Return value ABI type。
receiptMessages: Returns the parsed transaction receipt information。
Summary of corresponding tables of ‘returnCode’ and ‘returnMessages’ see here
6. Send transactions synchronously¶
Calling a contract transaction uses’ sendTransactionAndGetResponseByContractLoader ‘to invoke a contract transaction. Here’s how to call the’ set ‘function in’ HelloWorld’。
/ / Create a parameter to call the transaction function. Here, a parameter is passed in
List<Object> params = new ArrayList<>();
params.add("test");
/ / Call the HelloWorld contract. The contract address is helloWorldAddress, the function name is set, and the function parameter type is params
TransactionResponse transactionResponse = transactionProcessor.sendTransactionAndGetResponseByContractLoader("HelloWorld", helloWorldAddrss, "set", params);
/ / You can also manually pass in ABI file calls
TransactionResponse transactionResponse = transactionProcessor.sendTransactionAndGetResponse(helloWroldAddress, abi, "set", params);
7. Call the contract query interface¶
Query contracts can return results directly by calling the node query function on the chain without consensus;Therefore, all inquiry transactions are communicated in a synchronous manner。Querying a contract uses the ‘sendCallByContractLoader’ function to query the contract. This section shows how to call the ‘name’ function in ‘HelloWorld’ to query the contract。
/ / Query the name function of the HelloWorld contract. The contract address is helloWorldAddress and the parameter is empty
CallResponse callResponse = transactionProcessor.sendCallByContractLoader("HelloWorld", helloWorldAddrss, "name", new ArrayList<>());
/ / You can also manually pass in ABI file calls
CallResponse callResponse = transactionProcessor.sendCall("", helloWorldAddrss, "name", new ArrayList<>());
8. Sending transactions by signing the contract method¶
In addition, for special scenarios, DIY assembly transactions and sending transactions can be signed through the interface。
For example, the signature of the set method defined by the above ‘HelloWorld’ smart contract is’ set ‘(string)`
/ / _ isWasm is true when using the WBC-Liquid contract and false when using the Solidity contract
ContractCodec contractCodec = new ContractCodec(client.getCryptoSuite(), _isWasm);
String setMethodSignature = "set(string)";
byte[] txData = contractCodec.encodeMethodByInterface(setMethodSignature, new Object[]{new String("Hello World")});
Since there is no need to provide abi by constructing the interface signature, you can construct a ‘TransactionProcessor’ to operate on it。You can also use ‘TransactionProcessorFactory’ to construct。
TransactionProcessor transactionProcessor = TransactionProcessorFactory.createTransactionProcessor(client, keyPair);
Send transaction to FISCO BCOS node and receive receipt。
/ / If WBC-Liquid is used, the third parameter should use TransactionAttribute.LIQUID _ SCALE _ CODEC
TransactionReceipt transactionReceipt = transactionProcessor.sendTransactionAndGetReceipt(contractAddress, txData, TransactionAttribute.EVM_ABI_CODEC);
You need to manually parse the result information in the transaction receipt after successful execution。For more detailed usage, please refer to: Transaction Receipt Resolution
TransactionDecoderService txDecoder = new TransactionDecoderService(client.getCryptoSuite(), client.isWASM());
TransactionResponse transactionResponse = txDecoder.decodeReceiptWithValues(abi,"set",transactionReceipt);
9. Operate the contract asynchronously by callback¶
9.1 Define callback class¶
When sending transactions asynchronously, you can customize the callback class, implement and rewrite the callback handler。
The custom callback class needs to inherit the abstract class’ TransactionCallback ‘and implement the’ onResponse ‘method。At the same time, you can decide on demand whether you need to override methods such as’ onError ‘and’ onTimeout ‘。
For example, we define a simple callback class。The callback class implements a reentrant lock-based asynchronous call effect that reduces the thread’s synchronous wait time。
public class TransactionCallbackMock extends TransactionCallback {
private TransactionReceipt transactionReceipt;
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition condition;
public TransactionCallbackMock() {
condition = reentrantLock.newCondition();
}
public TransactionReceipt getResult() {
try {
reentrantLock.lock();
while (transactionReceipt == null) {
condition.awaitUninterruptibly();
}
return transactionReceipt;
} finally {
reentrantLock.unlock();
}
}
@Override
public void onResponse(TransactionReceipt transactionReceipt) {
try {
reentrantLock.lock();
this.transactionReceipt = transactionReceipt;
condition.signal();
} finally {
reentrantLock.unlock();
}
}
}
9.2 Asynchronous deployment of contracts using callback¶
First, create an instance of the callback class。Then use the ‘deployByContractLoaderAsync’ method to deploy the contract asynchronously。
/ / Create an instance of the callback class
TransactionCallbackMock callbackMock = new TransactionCallbackMock();
AssembleTransactionProcessor transactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(client, keyPair, "src/main/resources/abi/", "");
/ / Asynchronous deployment contract
transactionProcessor.deployByContractLoaderAsync("HelloWorld", new ArrayList<>(), callbackMock);
/ / Asynchronously wait for receipt
TransactionReceipt transactionReceipt = callbackMock.getResult();
9.3 Send transaction by callback¶
Reference deployment contract transactions, which can be sent asynchronously。
/ / Create an instance of the callback class
TransactionCallbackMock callbackMock = new TransactionCallbackMock();
/ / Define construction parameters
List<Object> params = Lists.newArrayList("test");
/ / Call contract transaction asynchronously
transactionProcessor.sendTransactionAsync(to, abi, "set", params, callbackMock);
/ / Asynchronously wait for receipt
TransactionReceipt transactionReceipt = callbackMock.getResult();
10. Operate the contract asynchronously using CompletableFuture¶
The SDK also supports asynchronous contract deployment using ‘CompletableFuture’ encapsulation。
/ / Deploy the transaction asynchronously and get CompletableFuture<TransactionReceipt> Object
CompletableFuture<TransactionReceipt> future = transactionProcessor.deployAsync(abi, bin, new ArrayList<>(),"");
/ / Define the business logic returned normally
future.thenAccept(
tr -> {
doSomething(tr);
});
/ / Define the business logic returned by the exception
future.exceptionally(
e -> {
doSomething(e);
return null;
});
11. Detailed API function introduction¶
‘AssembleTransactionProcessor ‘supports sending transactions with custom parameters, sending transactions asynchronously, and returning results in multiple encapsulation methods。
Reference Java doc: AssembleTransactionProcessor
The detailed API functions are as follows。
public void deployOnly(String abi, String bin, List<Object> params): Incoming contract abi, bin, and constructor parameters to deploy the contract without receiving receipt results。
public TransactionResponse deployAndGetResponse(String abi, String bin, List<Object> params) : Pass in contract abi, bin, and constructor parameters to deploy the contract and receive the receipt result
TransactionResponse deployAndGetResponseWithStringParams(String abi, String bin, List<String> params): The list of contract abi and String is passed in as constructor parameters to deploy the contract and receive the TransactionResponse result。
void deployAsync(String abi, String bin, List<Object> params, TransactionCallback callback): Incoming contract abi, constructed contract constructor, and callback to deploy the contract asynchronously
CompletableFuture<TransactionReceipt> deployAsync(String abi, String bin, List<Object> params): Enter the contract abi, bin, and constructor parameters to deploy the contract and receive the receipt result encapsulated by CompletableFuture
TransactionResponse deployByContractLoader(String contractName, List<Object> params): Pass in the contract name and the constructed contract constructor to receive the TransactionResponse result。
void deployByContractLoaderAsync(String contractName, List<Object> args, TransactionCallback callback): Asynchronously deploys the contract by passing in the contract name, contract constructor parameters, and callback
TransactionReceipt sendTransactionAndGetReceiptByContractLoader(String contractName, String contractAddress, String functionName, List<Object> params): Incoming call contract name, contract address, function name, and function parameters to receive transaction receipts
TransactionResponse sendTransactionAndGetResponse(String to, String abi, String functionName, List<Object> params): Enter the calling contract address, contract abi, function name, and function parameters, and receive the TransactionResponse result
TransactionResponse sendTransactionWithStringParamsAndGetResponse(String to, String abi, String functionName, List<String> params): Enter the calling contract address, contract abi, function name, and function parameters of the String type List, and receive the TransactionResponse result
void sendTransactionAsync(String to, String abi, String functionName, List<Object> params, TransactionCallback callback): Incoming call contract address, contract abi, function name, function parameter, callback, asynchronous send transaction。
void sendTransactionAndGetReceiptByContractLoaderAsync(String contractName,String contractAddress, String functionName, List<Object> args, TransactionCallback callback): Incoming call contract name, contract address, function name, function parameter, callback, asynchronous send transaction。
TransactionResponse sendTransactionAndGetResponseByContractLoader(String contractName, String contractAddress, String functionName, List<Object> funcParams): Pass in the call contract name, contract address, function name, function parameters, and receive the TransactionResponse result。
CallResponse sendCallByContractLoader(String contractName, String contractAddress, String functionName, List<Object> params): Pass in the call contract name, contract address, function name, function parameters, and receive the CallResponse result。
CallResponse sendCall(String from, String to, String abi, String functionName, List<Object> args): Enter the caller address, contract address, contract abi, function name, function parameters, and receive the CallResponse result。
CallResponse sendCall(CallRequest callRequest): Incoming a CallRequest and receiving a CallResponse result。
CallResponse sendCallWithStringParams(String from, String to, String abi, String functionName, List<String> paramsList): Enter the caller address, contract address, contract abi, function name, and function parameters of the String type List, and receive the CallResponse result。