Skip to content

Commit

Permalink
<fix>(codec): add encode and decode by contract definition interfaces. (
Browse files Browse the repository at this point in the history
  • Loading branch information
kyonRay authored Jan 14, 2025
1 parent 78b4588 commit f96305a
Show file tree
Hide file tree
Showing 3 changed files with 360 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ ext {
// integrationTest.mustRunAfter test
allprojects {
group = 'org.fisco-bcos.java-sdk'
version = '3.7.0'
version = '3.7.1'

apply plugin: 'maven-publish'
apply plugin: 'idea'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package org.fisco.bcos.sdk.v3.test.demo;

import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.fisco.bcos.sdk.jni.common.JniException;
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionData;
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionDataV1;
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionDataV2;
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionStructBuilderJniObj;
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionVersion;
import org.fisco.bcos.sdk.v3.BcosSDK;
import org.fisco.bcos.sdk.v3.client.Client;
import org.fisco.bcos.sdk.v3.codec.ContractCodec;
import org.fisco.bcos.sdk.v3.codec.ContractCodecException;
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIDefinition;
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIDefinitionFactory;
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIObject;
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIObjectFactory;
import org.fisco.bcos.sdk.v3.codec.wrapper.ContractABIDefinition;
import org.fisco.bcos.sdk.v3.codec.wrapper.ContractCodecJsonWrapper;
import org.fisco.bcos.sdk.v3.codec.wrapper.ContractCodecTools;
import org.fisco.bcos.sdk.v3.crypto.CryptoSuite;
import org.fisco.bcos.sdk.v3.crypto.hash.Keccak256;
import org.fisco.bcos.sdk.v3.crypto.signature.SignatureResult;
import org.fisco.bcos.sdk.v3.model.ConstantConfig;
import org.fisco.bcos.sdk.v3.model.CryptoType;
import org.fisco.bcos.sdk.v3.model.TransactionReceipt;
import org.fisco.bcos.sdk.v3.test.transaction.mock.RemoteSignProviderMock;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.dto.TransactionRequestWithStringParams;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.utils.TransactionRequestBuilder;
import org.fisco.bcos.sdk.v3.transaction.model.exception.ContractException;
import org.fisco.bcos.sdk.v3.transaction.signer.RemoteSignProviderInterface;
import org.fisco.bcos.sdk.v3.utils.Hex;
import org.fisco.bcos.sdk.v3.utils.Numeric;

class AssembleTransaction {
private String abi;
private TransactionRequestWithStringParams transactionRequest;
private ABIDefinitionFactory abiDefinitionFactory;
private ContractCodec contractCodec;

public AssembleTransaction(TransactionRequestWithStringParams transactionRequest) {
this.transactionRequest = transactionRequest;
this.abi = transactionRequest.getAbi();
this.abiDefinitionFactory = new ABIDefinitionFactory(new Keccak256());
contractCodec = new ContractCodec(new Keccak256(), false);
}

public AssembleTransaction(String abi, TransactionRequestWithStringParams transactionRequest) {
this(transactionRequest);
this.abi = abi;
}

public Pair<String, TransactionData> assembleTransaction()
throws JniException, ContractCodecException {

/// 如果abi固定,可以在初始化时就加载好
ContractABIDefinition contractABIDefinition = abiDefinitionFactory.loadABI(abi);
ContractCodecJsonWrapper contractCodecJsonWrapper = new ContractCodecJsonWrapper();
List<ABIDefinition> abiDefinitions =
contractABIDefinition.getFunctions().get(transactionRequest.getMethod());
if (abiDefinitions == null) {
throw new RuntimeException("Method not found in ABI");
}
ABIDefinition abiDefinition = abiDefinitions.get(0);

/// 下面的代码是替换掉了原来的encodeMethodFromString
// byte[] encode = contractCodec.encodeMethodFromString(abi, transactionRequest.getMethod(),
// transactionRequest.getStringParams());
ABIObject inputABIObject = ABIObjectFactory.createInputObject(abiDefinition);
byte[] encode = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(abiDefinition.getMethodId(new Keccak256()));
outputStream.write(
ContractCodecTools.encode(
contractCodecJsonWrapper.encode(
inputABIObject, transactionRequest.getStringParams()),
false));
encode = outputStream.toByteArray();
} catch (Exception e) {
throw new ContractCodecException("encode input error");
}

TransactionData transactionData =
new TransactionData()
.buildVersion(transactionRequest.getVersion().getValue())
.buildGroupId("group0")
.buildChainId("chain0")
.buildTo(transactionRequest.getTo())
.buildNonce(transactionRequest.getNonce())
.buildInput(encode)
.buildAbi(transactionRequest.getAbi())
.buildBlockLimit(transactionRequest.getBlockLimit().longValue());
if (transactionRequest.getVersion().getValue() >= TransactionVersion.V1.getValue()) {
transactionData =
new TransactionDataV1(transactionData)
.buildGasLimit(0)
.buildGasPrice("")
.buildValue(Numeric.toHexString(transactionRequest.getValue()))
.buildMaxFeePerGas("")
.buildMaxPriorityFeePerGas("");
}
if (transactionRequest.getVersion().getValue() >= TransactionVersion.V2.getValue()) {
transactionData =
new TransactionDataV2((TransactionDataV1) transactionData)
.buildExtension(transactionRequest.getExtension());
}

String transactionDataHash =
TransactionStructBuilderJniObj.calcTransactionDataStructHash(
CryptoType.ECDSA_TYPE, transactionData);

return new MutablePair<>(transactionDataHash, transactionData);
}

public String assembleSignedTx(String signature, String txHash, TransactionData txData)
throws JniException {
return TransactionStructBuilderJniObj.createEncodedTransaction(
txData, signature, txHash, 0, "extraData");
}
}

class SignTransaction {
private final RemoteSignProviderInterface remoteSignProviderMock;

public SignTransaction() {
remoteSignProviderMock = new RemoteSignProviderMock(new CryptoSuite(CryptoType.ECDSA_TYPE));
}

public String sign(String hash) {
SignatureResult signatureResult =
remoteSignProviderMock.requestForSign(Hex.decode(hash), CryptoType.ECDSA_TYPE);
return Hex.toHexString(signatureResult.encode());
}
}

class SendTransaction {
private Client client;

public SendTransaction(Client client) {
this.client = client;
}

public TransactionReceipt sendTx(String signedTx) {
return client.sendTransaction(signedTx, false).getTransactionReceipt();
}
}

public class AssembleTransactionDemo {
public static BcosSDK sdk;
public static Client client;
private static final String configFile =
"src/integration-test/resources/" + ConstantConfig.CONFIG_FILE_NAME;

public static void main(String[] args)
throws ContractException, JniException, ContractCodecException {
sdk = BcosSDK.build(configFile);
client = sdk.getClient("group0");

HelloWorld deployed = HelloWorld.deploy(client, client.getCryptoSuite().getCryptoKeyPair());

TransactionRequestBuilder requestBuilder = new TransactionRequestBuilder();
TransactionRequestWithStringParams set =
requestBuilder
.setAbi(HelloWorld.getABI())
.setBlockLimit(client.getBlockLimit())
.setNonce(UUID.randomUUID().toString().replace("-", ""))
.setTo(deployed.getContractAddress())
.setMethod("set")
.buildStringParamsRequest(Collections.singletonList("Hello, World!"));
AssembleTransaction assembleTransaction = new AssembleTransaction(HelloWorld.getABI(), set);
Pair<String, TransactionData> hashTransactionDataPair =
assembleTransaction.assembleTransaction();

SignTransaction signTransaction = new SignTransaction();
String sign = signTransaction.sign(hashTransactionDataPair.getLeft());

String signedTx =
assembleTransaction.assembleSignedTx(
sign,
hashTransactionDataPair.getLeft(),
hashTransactionDataPair.getRight());

SendTransaction sendTransaction = new SendTransaction(client);
TransactionReceipt transactionReceipt = sendTransaction.sendTx(signedTx);
System.out.println(transactionReceipt);
}
}
166 changes: 166 additions & 0 deletions src/main/java/org/fisco/bcos/sdk/v3/codec/ContractCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,68 @@ public byte[] encodeMethodFromString(String abi, String methodName, List<String>
throw new ContractCodecException(errorMsg);
}

public byte[] encodeMethodFromStringByContractABIDefinition(
ContractABIDefinition contractABIDefinition, String methodName, List<String> params)
throws ContractCodecException {
List<ABIDefinition> methods = contractABIDefinition.getFunctions().get(methodName);
if (methods == null) {
logger.debug(
"Invalid methodName: {}, all the functions are: {}",
methodName,
contractABIDefinition.getFunctions());
throw new ContractCodecException(
"Invalid method "
+ methodName
+ " , supported functions are: "
+ contractABIDefinition.getFunctions().keySet());
}

for (ABIDefinition abiDefinition : methods) {
if (abiDefinition.getInputs().size() == params.size()) {
ABIObject inputObject = ABIObjectFactory.createInputObject(abiDefinition);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
byte[] methodId = abiDefinition.getMethodId(cryptoSuite);
ABIObject abiObject = contractCodecJsonWrapper.encode(inputObject, params);
byte[] encode = abiObject.encode(isWasm);
outputStream.write(methodId);
outputStream.write(encode);
return outputStream.toByteArray();
} catch (Exception e) {
logger.error(" exception in encodeMethodFromString : {}", e.getMessage());
}
}
}

String errorMsg =
" cannot encode in encodeMethodFromString with appropriate interface ABI, make sure params match";
logger.error(errorMsg);
throw new ContractCodecException(errorMsg);
}

public byte[] encodeMethodFromStringByABIDefinition(
ABIDefinition abiDefinition, List<String> params) throws ContractCodecException {
if (abiDefinition.getInputs().size() == params.size()) {
ABIObject inputObject = ABIObjectFactory.createInputObject(abiDefinition);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
byte[] methodId = abiDefinition.getMethodId(cryptoSuite);
ABIObject abiObject = contractCodecJsonWrapper.encode(inputObject, params);
byte[] encode = abiObject.encode(isWasm);
outputStream.write(methodId);
outputStream.write(encode);
return outputStream.toByteArray();
} catch (Exception e) {
logger.error(
" exception in encodeMethodFromStringByAbiDefinition : {}", e.getMessage());
}
}
String errorMsg =
" cannot encode in encodeMethodFromStringByAbiDefinition with appropriate interface ABI, make sure params match";
logger.error(errorMsg);
throw new ContractCodecException(errorMsg);
}

public byte[] encodeMethodByIdFromString(String abi, byte[] methodId, List<String> params)
throws ContractCodecException {
ContractABIDefinition contractABIDefinition = this.abiDefinitionFactory.loadABI(abi);
Expand Down Expand Up @@ -1029,6 +1091,57 @@ public List<Object> decodeEvent(String abi, String eventName, EventLog log)
throw new ContractCodecException(errorMsg);
}

public List<Object> decodeEventByContractABIDefinition(
ContractABIDefinition contractABIDefinition, String eventName, EventLog log)
throws ContractCodecException {
List<ABIDefinition> events = contractABIDefinition.getEvents().get(eventName);
if (events == null) {
throw new ContractCodecException(
"Invalid event "
+ eventName
+ ", supported events are: "
+ contractABIDefinition.getEvents().keySet());
}
for (ABIDefinition abiDefinition : events) {
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
try {
List<Object> params = new ArrayList<>();
if (!log.getData().equals("0x")) {
params =
ContractCodecTools.decodeJavaObject(inputObject, log.getData(), isWasm);
}
List<String> topics = log.getTopics();
return this.mergeEventParamsAndTopics(abiDefinition, params, topics);
} catch (Exception e) {
logger.error(" exception in decodeEventToObject : {}", e.getMessage());
}
}

String errorMsg = " cannot decode in decodeEventToObject with appropriate interface ABI";
logger.error(errorMsg);
throw new ContractCodecException(errorMsg);
}

public List<Object> decodeEventByAbiDefinition(ABIDefinition abiDefinition, EventLog log)
throws ContractCodecException {
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
try {
List<Object> params = new ArrayList<>();
if (!log.getData().equals("0x")) {
params = ContractCodecTools.decodeJavaObject(inputObject, log.getData(), isWasm);
}
List<String> topics = log.getTopics();
return this.mergeEventParamsAndTopics(abiDefinition, params, topics);
} catch (Exception e) {
logger.error(" exception in decodeEventByAbiDefinition : {}", e.getMessage());
}

String errorMsg =
" cannot decode in decodeEventByAbiDefinition with appropriate interface ABI";
logger.error(errorMsg);
throw new ContractCodecException(errorMsg);
}

public List<Object> decodeEventByTopic(String abi, String eventTopic, EventLog log)
throws ContractCodecException {
ContractABIDefinition contractABIDefinition = this.abiDefinitionFactory.loadABI(abi);
Expand Down Expand Up @@ -1090,6 +1203,59 @@ public List<String> decodeEventToString(String abi, String eventName, EventLog l
throw new ContractCodecException(errorMsg);
}

public List<String> decodeEventToStringByContractABIDefinition(
ContractABIDefinition contractABIDefinition, String eventName, EventLog log)
throws ContractCodecException {
List<ABIDefinition> events = contractABIDefinition.getEvents().get(eventName);
if (events == null) {
throw new ContractCodecException(
"Invalid event "
+ eventName
+ ", current supported events are: "
+ contractABIDefinition.getEvents().keySet());
}
for (ABIDefinition abiDefinition : events) {
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
try {
List<String> params = new ArrayList<>();
if (!log.getData().equals("0x")) {
params =
contractCodecJsonWrapper.decode(
inputObject, Hex.decode(log.getData()), isWasm);
}
List<String> topics = log.getTopics();
return this.mergeEventParamsAndTopicsToString(abiDefinition, params, topics);
} catch (Exception e) {
logger.error(" exception in decodeEventToString : {}", e.getMessage());
}
}

String errorMsg = " cannot decode in decodeEventToString with appropriate interface ABI";
logger.error(errorMsg);
throw new ContractCodecException(errorMsg);
}

public List<String> decodeEventToStringByABIDefinition(
ABIDefinition abiDefinition, EventLog log) throws ContractCodecException {
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
try {
List<String> params = new ArrayList<>();
if (!log.getData().equals("0x")) {
params =
contractCodecJsonWrapper.decode(
inputObject, Hex.decode(log.getData()), isWasm);
}
List<String> topics = log.getTopics();
return this.mergeEventParamsAndTopicsToString(abiDefinition, params, topics);
} catch (Exception e) {
logger.error(" exception in decodeEventToString : {}", e.getMessage());
}

String errorMsg = " cannot decode in decodeEventToString with appropriate interface ABI";
logger.error(errorMsg);
throw new ContractCodecException(errorMsg);
}

public List<String> decodeEventByTopicToString(String abi, String eventTopic, EventLog log)
throws ContractCodecException {
ContractABIDefinition contractABIDefinition = this.abiDefinitionFactory.loadABI(abi);
Expand Down

0 comments on commit f96305a

Please sign in to comment.