Skip to content

Commit f96305a

Browse files
authored
<fix>(codec): add encode and decode by contract definition interfaces. (#935)
1 parent 78b4588 commit f96305a

File tree

3 files changed

+360
-1
lines changed

3 files changed

+360
-1
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ ext {
3535
// integrationTest.mustRunAfter test
3636
allprojects {
3737
group = 'org.fisco-bcos.java-sdk'
38-
version = '3.7.0'
38+
version = '3.7.1'
3939

4040
apply plugin: 'maven-publish'
4141
apply plugin: 'idea'
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package org.fisco.bcos.sdk.v3.test.demo;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import java.util.UUID;
7+
import org.apache.commons.lang3.tuple.MutablePair;
8+
import org.apache.commons.lang3.tuple.Pair;
9+
import org.fisco.bcos.sdk.jni.common.JniException;
10+
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionData;
11+
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionDataV1;
12+
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionDataV2;
13+
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionStructBuilderJniObj;
14+
import org.fisco.bcos.sdk.jni.utilities.tx.TransactionVersion;
15+
import org.fisco.bcos.sdk.v3.BcosSDK;
16+
import org.fisco.bcos.sdk.v3.client.Client;
17+
import org.fisco.bcos.sdk.v3.codec.ContractCodec;
18+
import org.fisco.bcos.sdk.v3.codec.ContractCodecException;
19+
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIDefinition;
20+
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIDefinitionFactory;
21+
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIObject;
22+
import org.fisco.bcos.sdk.v3.codec.wrapper.ABIObjectFactory;
23+
import org.fisco.bcos.sdk.v3.codec.wrapper.ContractABIDefinition;
24+
import org.fisco.bcos.sdk.v3.codec.wrapper.ContractCodecJsonWrapper;
25+
import org.fisco.bcos.sdk.v3.codec.wrapper.ContractCodecTools;
26+
import org.fisco.bcos.sdk.v3.crypto.CryptoSuite;
27+
import org.fisco.bcos.sdk.v3.crypto.hash.Keccak256;
28+
import org.fisco.bcos.sdk.v3.crypto.signature.SignatureResult;
29+
import org.fisco.bcos.sdk.v3.model.ConstantConfig;
30+
import org.fisco.bcos.sdk.v3.model.CryptoType;
31+
import org.fisco.bcos.sdk.v3.model.TransactionReceipt;
32+
import org.fisco.bcos.sdk.v3.test.transaction.mock.RemoteSignProviderMock;
33+
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.dto.TransactionRequestWithStringParams;
34+
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.utils.TransactionRequestBuilder;
35+
import org.fisco.bcos.sdk.v3.transaction.model.exception.ContractException;
36+
import org.fisco.bcos.sdk.v3.transaction.signer.RemoteSignProviderInterface;
37+
import org.fisco.bcos.sdk.v3.utils.Hex;
38+
import org.fisco.bcos.sdk.v3.utils.Numeric;
39+
40+
class AssembleTransaction {
41+
private String abi;
42+
private TransactionRequestWithStringParams transactionRequest;
43+
private ABIDefinitionFactory abiDefinitionFactory;
44+
private ContractCodec contractCodec;
45+
46+
public AssembleTransaction(TransactionRequestWithStringParams transactionRequest) {
47+
this.transactionRequest = transactionRequest;
48+
this.abi = transactionRequest.getAbi();
49+
this.abiDefinitionFactory = new ABIDefinitionFactory(new Keccak256());
50+
contractCodec = new ContractCodec(new Keccak256(), false);
51+
}
52+
53+
public AssembleTransaction(String abi, TransactionRequestWithStringParams transactionRequest) {
54+
this(transactionRequest);
55+
this.abi = abi;
56+
}
57+
58+
public Pair<String, TransactionData> assembleTransaction()
59+
throws JniException, ContractCodecException {
60+
61+
/// 如果abi固定,可以在初始化时就加载好
62+
ContractABIDefinition contractABIDefinition = abiDefinitionFactory.loadABI(abi);
63+
ContractCodecJsonWrapper contractCodecJsonWrapper = new ContractCodecJsonWrapper();
64+
List<ABIDefinition> abiDefinitions =
65+
contractABIDefinition.getFunctions().get(transactionRequest.getMethod());
66+
if (abiDefinitions == null) {
67+
throw new RuntimeException("Method not found in ABI");
68+
}
69+
ABIDefinition abiDefinition = abiDefinitions.get(0);
70+
71+
/// 下面的代码是替换掉了原来的encodeMethodFromString
72+
// byte[] encode = contractCodec.encodeMethodFromString(abi, transactionRequest.getMethod(),
73+
// transactionRequest.getStringParams());
74+
ABIObject inputABIObject = ABIObjectFactory.createInputObject(abiDefinition);
75+
byte[] encode = null;
76+
try {
77+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
78+
outputStream.write(abiDefinition.getMethodId(new Keccak256()));
79+
outputStream.write(
80+
ContractCodecTools.encode(
81+
contractCodecJsonWrapper.encode(
82+
inputABIObject, transactionRequest.getStringParams()),
83+
false));
84+
encode = outputStream.toByteArray();
85+
} catch (Exception e) {
86+
throw new ContractCodecException("encode input error");
87+
}
88+
89+
TransactionData transactionData =
90+
new TransactionData()
91+
.buildVersion(transactionRequest.getVersion().getValue())
92+
.buildGroupId("group0")
93+
.buildChainId("chain0")
94+
.buildTo(transactionRequest.getTo())
95+
.buildNonce(transactionRequest.getNonce())
96+
.buildInput(encode)
97+
.buildAbi(transactionRequest.getAbi())
98+
.buildBlockLimit(transactionRequest.getBlockLimit().longValue());
99+
if (transactionRequest.getVersion().getValue() >= TransactionVersion.V1.getValue()) {
100+
transactionData =
101+
new TransactionDataV1(transactionData)
102+
.buildGasLimit(0)
103+
.buildGasPrice("")
104+
.buildValue(Numeric.toHexString(transactionRequest.getValue()))
105+
.buildMaxFeePerGas("")
106+
.buildMaxPriorityFeePerGas("");
107+
}
108+
if (transactionRequest.getVersion().getValue() >= TransactionVersion.V2.getValue()) {
109+
transactionData =
110+
new TransactionDataV2((TransactionDataV1) transactionData)
111+
.buildExtension(transactionRequest.getExtension());
112+
}
113+
114+
String transactionDataHash =
115+
TransactionStructBuilderJniObj.calcTransactionDataStructHash(
116+
CryptoType.ECDSA_TYPE, transactionData);
117+
118+
return new MutablePair<>(transactionDataHash, transactionData);
119+
}
120+
121+
public String assembleSignedTx(String signature, String txHash, TransactionData txData)
122+
throws JniException {
123+
return TransactionStructBuilderJniObj.createEncodedTransaction(
124+
txData, signature, txHash, 0, "extraData");
125+
}
126+
}
127+
128+
class SignTransaction {
129+
private final RemoteSignProviderInterface remoteSignProviderMock;
130+
131+
public SignTransaction() {
132+
remoteSignProviderMock = new RemoteSignProviderMock(new CryptoSuite(CryptoType.ECDSA_TYPE));
133+
}
134+
135+
public String sign(String hash) {
136+
SignatureResult signatureResult =
137+
remoteSignProviderMock.requestForSign(Hex.decode(hash), CryptoType.ECDSA_TYPE);
138+
return Hex.toHexString(signatureResult.encode());
139+
}
140+
}
141+
142+
class SendTransaction {
143+
private Client client;
144+
145+
public SendTransaction(Client client) {
146+
this.client = client;
147+
}
148+
149+
public TransactionReceipt sendTx(String signedTx) {
150+
return client.sendTransaction(signedTx, false).getTransactionReceipt();
151+
}
152+
}
153+
154+
public class AssembleTransactionDemo {
155+
public static BcosSDK sdk;
156+
public static Client client;
157+
private static final String configFile =
158+
"src/integration-test/resources/" + ConstantConfig.CONFIG_FILE_NAME;
159+
160+
public static void main(String[] args)
161+
throws ContractException, JniException, ContractCodecException {
162+
sdk = BcosSDK.build(configFile);
163+
client = sdk.getClient("group0");
164+
165+
HelloWorld deployed = HelloWorld.deploy(client, client.getCryptoSuite().getCryptoKeyPair());
166+
167+
TransactionRequestBuilder requestBuilder = new TransactionRequestBuilder();
168+
TransactionRequestWithStringParams set =
169+
requestBuilder
170+
.setAbi(HelloWorld.getABI())
171+
.setBlockLimit(client.getBlockLimit())
172+
.setNonce(UUID.randomUUID().toString().replace("-", ""))
173+
.setTo(deployed.getContractAddress())
174+
.setMethod("set")
175+
.buildStringParamsRequest(Collections.singletonList("Hello, World!"));
176+
AssembleTransaction assembleTransaction = new AssembleTransaction(HelloWorld.getABI(), set);
177+
Pair<String, TransactionData> hashTransactionDataPair =
178+
assembleTransaction.assembleTransaction();
179+
180+
SignTransaction signTransaction = new SignTransaction();
181+
String sign = signTransaction.sign(hashTransactionDataPair.getLeft());
182+
183+
String signedTx =
184+
assembleTransaction.assembleSignedTx(
185+
sign,
186+
hashTransactionDataPair.getLeft(),
187+
hashTransactionDataPair.getRight());
188+
189+
SendTransaction sendTransaction = new SendTransaction(client);
190+
TransactionReceipt transactionReceipt = sendTransaction.sendTx(signedTx);
191+
System.out.println(transactionReceipt);
192+
}
193+
}

src/main/java/org/fisco/bcos/sdk/v3/codec/ContractCodec.java

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,68 @@ public byte[] encodeMethodFromString(String abi, String methodName, List<String>
537537
throw new ContractCodecException(errorMsg);
538538
}
539539

540+
public byte[] encodeMethodFromStringByContractABIDefinition(
541+
ContractABIDefinition contractABIDefinition, String methodName, List<String> params)
542+
throws ContractCodecException {
543+
List<ABIDefinition> methods = contractABIDefinition.getFunctions().get(methodName);
544+
if (methods == null) {
545+
logger.debug(
546+
"Invalid methodName: {}, all the functions are: {}",
547+
methodName,
548+
contractABIDefinition.getFunctions());
549+
throw new ContractCodecException(
550+
"Invalid method "
551+
+ methodName
552+
+ " , supported functions are: "
553+
+ contractABIDefinition.getFunctions().keySet());
554+
}
555+
556+
for (ABIDefinition abiDefinition : methods) {
557+
if (abiDefinition.getInputs().size() == params.size()) {
558+
ABIObject inputObject = ABIObjectFactory.createInputObject(abiDefinition);
559+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
560+
try {
561+
byte[] methodId = abiDefinition.getMethodId(cryptoSuite);
562+
ABIObject abiObject = contractCodecJsonWrapper.encode(inputObject, params);
563+
byte[] encode = abiObject.encode(isWasm);
564+
outputStream.write(methodId);
565+
outputStream.write(encode);
566+
return outputStream.toByteArray();
567+
} catch (Exception e) {
568+
logger.error(" exception in encodeMethodFromString : {}", e.getMessage());
569+
}
570+
}
571+
}
572+
573+
String errorMsg =
574+
" cannot encode in encodeMethodFromString with appropriate interface ABI, make sure params match";
575+
logger.error(errorMsg);
576+
throw new ContractCodecException(errorMsg);
577+
}
578+
579+
public byte[] encodeMethodFromStringByABIDefinition(
580+
ABIDefinition abiDefinition, List<String> params) throws ContractCodecException {
581+
if (abiDefinition.getInputs().size() == params.size()) {
582+
ABIObject inputObject = ABIObjectFactory.createInputObject(abiDefinition);
583+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
584+
try {
585+
byte[] methodId = abiDefinition.getMethodId(cryptoSuite);
586+
ABIObject abiObject = contractCodecJsonWrapper.encode(inputObject, params);
587+
byte[] encode = abiObject.encode(isWasm);
588+
outputStream.write(methodId);
589+
outputStream.write(encode);
590+
return outputStream.toByteArray();
591+
} catch (Exception e) {
592+
logger.error(
593+
" exception in encodeMethodFromStringByAbiDefinition : {}", e.getMessage());
594+
}
595+
}
596+
String errorMsg =
597+
" cannot encode in encodeMethodFromStringByAbiDefinition with appropriate interface ABI, make sure params match";
598+
logger.error(errorMsg);
599+
throw new ContractCodecException(errorMsg);
600+
}
601+
540602
public byte[] encodeMethodByIdFromString(String abi, byte[] methodId, List<String> params)
541603
throws ContractCodecException {
542604
ContractABIDefinition contractABIDefinition = this.abiDefinitionFactory.loadABI(abi);
@@ -1029,6 +1091,57 @@ public List<Object> decodeEvent(String abi, String eventName, EventLog log)
10291091
throw new ContractCodecException(errorMsg);
10301092
}
10311093

1094+
public List<Object> decodeEventByContractABIDefinition(
1095+
ContractABIDefinition contractABIDefinition, String eventName, EventLog log)
1096+
throws ContractCodecException {
1097+
List<ABIDefinition> events = contractABIDefinition.getEvents().get(eventName);
1098+
if (events == null) {
1099+
throw new ContractCodecException(
1100+
"Invalid event "
1101+
+ eventName
1102+
+ ", supported events are: "
1103+
+ contractABIDefinition.getEvents().keySet());
1104+
}
1105+
for (ABIDefinition abiDefinition : events) {
1106+
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
1107+
try {
1108+
List<Object> params = new ArrayList<>();
1109+
if (!log.getData().equals("0x")) {
1110+
params =
1111+
ContractCodecTools.decodeJavaObject(inputObject, log.getData(), isWasm);
1112+
}
1113+
List<String> topics = log.getTopics();
1114+
return this.mergeEventParamsAndTopics(abiDefinition, params, topics);
1115+
} catch (Exception e) {
1116+
logger.error(" exception in decodeEventToObject : {}", e.getMessage());
1117+
}
1118+
}
1119+
1120+
String errorMsg = " cannot decode in decodeEventToObject with appropriate interface ABI";
1121+
logger.error(errorMsg);
1122+
throw new ContractCodecException(errorMsg);
1123+
}
1124+
1125+
public List<Object> decodeEventByAbiDefinition(ABIDefinition abiDefinition, EventLog log)
1126+
throws ContractCodecException {
1127+
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
1128+
try {
1129+
List<Object> params = new ArrayList<>();
1130+
if (!log.getData().equals("0x")) {
1131+
params = ContractCodecTools.decodeJavaObject(inputObject, log.getData(), isWasm);
1132+
}
1133+
List<String> topics = log.getTopics();
1134+
return this.mergeEventParamsAndTopics(abiDefinition, params, topics);
1135+
} catch (Exception e) {
1136+
logger.error(" exception in decodeEventByAbiDefinition : {}", e.getMessage());
1137+
}
1138+
1139+
String errorMsg =
1140+
" cannot decode in decodeEventByAbiDefinition with appropriate interface ABI";
1141+
logger.error(errorMsg);
1142+
throw new ContractCodecException(errorMsg);
1143+
}
1144+
10321145
public List<Object> decodeEventByTopic(String abi, String eventTopic, EventLog log)
10331146
throws ContractCodecException {
10341147
ContractABIDefinition contractABIDefinition = this.abiDefinitionFactory.loadABI(abi);
@@ -1090,6 +1203,59 @@ public List<String> decodeEventToString(String abi, String eventName, EventLog l
10901203
throw new ContractCodecException(errorMsg);
10911204
}
10921205

1206+
public List<String> decodeEventToStringByContractABIDefinition(
1207+
ContractABIDefinition contractABIDefinition, String eventName, EventLog log)
1208+
throws ContractCodecException {
1209+
List<ABIDefinition> events = contractABIDefinition.getEvents().get(eventName);
1210+
if (events == null) {
1211+
throw new ContractCodecException(
1212+
"Invalid event "
1213+
+ eventName
1214+
+ ", current supported events are: "
1215+
+ contractABIDefinition.getEvents().keySet());
1216+
}
1217+
for (ABIDefinition abiDefinition : events) {
1218+
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
1219+
try {
1220+
List<String> params = new ArrayList<>();
1221+
if (!log.getData().equals("0x")) {
1222+
params =
1223+
contractCodecJsonWrapper.decode(
1224+
inputObject, Hex.decode(log.getData()), isWasm);
1225+
}
1226+
List<String> topics = log.getTopics();
1227+
return this.mergeEventParamsAndTopicsToString(abiDefinition, params, topics);
1228+
} catch (Exception e) {
1229+
logger.error(" exception in decodeEventToString : {}", e.getMessage());
1230+
}
1231+
}
1232+
1233+
String errorMsg = " cannot decode in decodeEventToString with appropriate interface ABI";
1234+
logger.error(errorMsg);
1235+
throw new ContractCodecException(errorMsg);
1236+
}
1237+
1238+
public List<String> decodeEventToStringByABIDefinition(
1239+
ABIDefinition abiDefinition, EventLog log) throws ContractCodecException {
1240+
ABIObject inputObject = ABIObjectFactory.createEventInputObject(abiDefinition);
1241+
try {
1242+
List<String> params = new ArrayList<>();
1243+
if (!log.getData().equals("0x")) {
1244+
params =
1245+
contractCodecJsonWrapper.decode(
1246+
inputObject, Hex.decode(log.getData()), isWasm);
1247+
}
1248+
List<String> topics = log.getTopics();
1249+
return this.mergeEventParamsAndTopicsToString(abiDefinition, params, topics);
1250+
} catch (Exception e) {
1251+
logger.error(" exception in decodeEventToString : {}", e.getMessage());
1252+
}
1253+
1254+
String errorMsg = " cannot decode in decodeEventToString with appropriate interface ABI";
1255+
logger.error(errorMsg);
1256+
throw new ContractCodecException(errorMsg);
1257+
}
1258+
10931259
public List<String> decodeEventByTopicToString(String abi, String eventTopic, EventLog log)
10941260
throws ContractCodecException {
10951261
ContractABIDefinition contractABIDefinition = this.abiDefinitionFactory.loadABI(abi);

0 commit comments

Comments
 (0)