Skip to content

Commit

Permalink
add Revert message resolve function (#536)
Browse files Browse the repository at this point in the history
* add RevertResolver

* add RevertResolver UT
  • Loading branch information
ywy2090 authored Mar 20, 2020
1 parent 563de43 commit 6b2aafa
Show file tree
Hide file tree
Showing 8 changed files with 402 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .ci/ci_check_commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -e

scan_code_script="cobra/cobra.py -f json -o /tmp/report.json -t "
ignore_files=(ECCParams.java ECKeyPair.java KeyUtils.java Permission.java Frozen.java ECDSASign.java Constant.java PerformanceOkDSync.java SM2Algorithm.java SM2KeyGenerator.java test)
ignore_files=(RevertResolver.java ECCParams.java ECKeyPair.java KeyUtils.java Permission.java Frozen.java ECDSASign.java Constant.java PerformanceOkDSync.java SM2Algorithm.java SM2KeyGenerator.java test)

LOG_ERROR() {
content=${1}
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ try {
println(" .git directory not exist.");
}

tasks.withType(Test) {
maxParallelForks = 1
}

// 1 dist jar
jar {
destinationDir file('dist/apps')
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/org/fisco/bcos/channel/client/Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
import org.fisco.bcos.web3j.protocol.core.methods.response.Log;
import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
import org.fisco.bcos.web3j.protocol.exceptions.TransactionException;
import org.fisco.bcos.web3j.tuples.generated.Tuple2;
import org.fisco.bcos.web3j.tx.RevertResolver;
import org.fisco.bcos.web3j.tx.txdecode.LogResult;
import org.fisco.bcos.web3j.utils.Strings;
import org.slf4j.Logger;
Expand Down Expand Up @@ -716,13 +718,6 @@ public void run(Timeout timeout) throws Exception {
}
}

/**
* When SDK start, the initial subscribed topics information set by user will be sent to the
* node. User can update subscribed topics again by following steps: 1. Set the topics you want
* to subscribe to again Service service // Servcie object Set<String> topics // topics that
* subscribe again service.setTopics(topics) 2. send update topics message to all nodes
* service.updateTopicsToNode();
*/
public void updateTopicsToNode() {

logger.info(" updateTopicToNode, groupId: {}, topics: {}", groupId, getTopics());
Expand Down Expand Up @@ -1548,6 +1543,12 @@ public void onReceiveTransactionMessage(String seq, TransactionReceipt receipt)
}

try {
Tuple2<Boolean, String> revertMessage =
RevertResolver.tryResolveRevertMessage(receipt);
if (revertMessage.getValue1()) {
logger.debug(" revert message: {}", revertMessage.getValue2());
receipt.setMessage(revertMessage.getValue2());
}
callback.onResponse(receipt);
} catch (Exception e) {
logger.error("Error process transactionMessage: ", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import org.fisco.bcos.web3j.protocol.core.methods.response.SendTransaction;
import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
import org.fisco.bcos.web3j.protocol.exceptions.MessageDecodingException;
import org.fisco.bcos.web3j.tuples.generated.Tuple2;
import org.fisco.bcos.web3j.tx.RevertResolver;
import org.fisco.bcos.web3j.tx.exceptions.ContractCallException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -80,17 +82,26 @@ public <T extends Response> T send(Request request, Class<T> responseType) throw
}
if (t.getResult() instanceof CallOutput) {
CallOutput callResult = (CallOutput) t.getResult();
if (StatusCode.RevertInstruction.equals(callResult.getStatus())) {
throw new ContractCallException(
"The execution of the contract rolled back.");
}
if (StatusCode.CallAddressError.equals(callResult.getStatus())) {
throw new ContractCallException("The contract address is incorrect.");
}

if (!StatusCode.Success.equals(callResult.getStatus())) {
throw new ContractCallException(
StatusCode.getStatusMessage(callResult.getStatus()));
Tuple2<Boolean, String> revertMessage =
RevertResolver.tryResolveRevertMessage(
callResult.getStatus(), callResult.getOutput());
if (revertMessage.getValue1()) {
logger.debug(" revert message: {}", revertMessage.getValue2());
throw new ContractCallException(revertMessage.getValue2());
} else {
if (StatusCode.RevertInstruction.equals(callResult.getStatus())) {
throw new ContractCallException(
"The execution of the contract rolled back.");
}
if (StatusCode.CallAddressError.equals(callResult.getStatus())) {
throw new ContractCallException("The contract address is incorrect.");
}

if (!StatusCode.Success.equals(callResult.getStatus())) {
throw new ContractCallException(
StatusCode.getStatusMessage(callResult.getStatus()));
}
}
}
return t;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.fisco.bcos.web3j.protocol.core;

import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import org.fisco.bcos.channel.client.TransactionSucCallback;
import org.fisco.bcos.web3j.protocol.core.methods.response.BcosBlock;
import org.fisco.bcos.web3j.protocol.core.methods.response.BcosFilter;
import org.fisco.bcos.web3j.protocol.core.methods.response.BcosLog;
Expand Down Expand Up @@ -97,6 +99,9 @@ Request<?, Call> call(

Request<?, SendTransaction> sendRawTransaction(String signedTransactionData);

void sendRawTransaction(String signedTransactionData, TransactionSucCallback callback)
throws IOException;

// generateGroup
Request<?, GenerateGroup> generateGroup(int groupId, int timestamp, List<String> nodeList);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import org.fisco.bcos.channel.client.TransactionSucCallback;
import org.fisco.bcos.web3j.protocol.Web3j;
import org.fisco.bcos.web3j.protocol.Web3jService;
import org.fisco.bcos.web3j.protocol.channel.ChannelEthereumService;
Expand Down Expand Up @@ -318,6 +319,16 @@ public Request<?, SendTransaction> sendRawTransaction(String signedTransactionDa
SendTransaction.class);
}

@Override
public void sendRawTransaction(String signedTransactionData, TransactionSucCallback callback)
throws IOException {
Request<?, SendTransaction> request = sendRawTransaction(signedTransactionData);
request.setNeedTransCallback(true);
request.setTransactionSucCallback(callback);

request.sendOnly();
}

@Override
public Request<?, GroupPeers> getGroupPeers() {
return new Request<>(
Expand Down
113 changes: 113 additions & 0 deletions src/main/java/org/fisco/bcos/web3j/tx/RevertResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.fisco.bcos.web3j.tx;

import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import org.fisco.bcos.web3j.abi.FunctionReturnDecoder;
import org.fisco.bcos.web3j.abi.TypeReference;
import org.fisco.bcos.web3j.abi.datatypes.Function;
import org.fisco.bcos.web3j.abi.datatypes.Type;
import org.fisco.bcos.web3j.abi.datatypes.Utf8String;
import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
import org.fisco.bcos.web3j.tuples.generated.Tuple2;
import org.fisco.bcos.web3j.utils.Numeric;
import org.fisco.bcos.web3j.utils.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
pragma solidity ^0.4.25;
contract Revert {
function Error(string memory s) public {}
}
"08c379a0": "Error(string)" // Not SM Method
"c703cb12": "Error(string)" // SM Method
*/

public class RevertResolver {

private static final Logger logger = LoggerFactory.getLogger(RevertResolver.class);

public static final String RevertMethod = "08c379a0";
public static final String RevertMethodWithHexPrefix = "0x08c379a0";

public static final String SMRevertMethod = "c703cb12";
public static final String SMRevertMethodWithHexPrefix = "0xc703cb12";

// Error(String)
public static final Function revertFunction =
new Function(
"Error",
Collections.<Type>emptyList(),
Collections.singletonList(new TypeReference<Utf8String>() {}));

/**
* Does output start with the code of the Revert method, If so, the output may be error message
*
* @param output
* @return
*/
public static boolean isOutputStartWithRevertMethod(String output) {
return output.startsWith(RevertMethodWithHexPrefix)
|| output.startsWith(SMRevertMethodWithHexPrefix)
|| (output.startsWith(RevertMethod) || output.startsWith(SMRevertMethod));
}

/**
* @param status
* @param output
* @return
*/
public static boolean hasRevertMessage(String status, String output) {
if (Strings.isEmpty(status) || Strings.isEmpty(output)) {
return false;
}
try {
BigInteger statusQuantity = Numeric.decodeQuantity(status);
return !BigInteger.ZERO.equals(statusQuantity) && isOutputStartWithRevertMethod(output);
} catch (Exception e) {
return false;
}
}

/**
* @param status
* @param output
* @return
*/
public static Tuple2<Boolean, String> tryResolveRevertMessage(String status, String output) {
if (!hasRevertMessage(status, output)) {
return new Tuple2<>(false, null);
}

try {
// 00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030497373756572526f6c653a2063616c6c657220646f6573206e6f742068617665207468652049737375657220726f6c6500000000000000000000000000000000
String rawOutput =
Numeric.containsHexPrefix(output)
? output.substring(RevertMethodWithHexPrefix.length())
: output.substring(RevertMethod.length());
List<Type> result =
FunctionReturnDecoder.decode(rawOutput, revertFunction.getOutputParameters());
if (result.get(0) instanceof Utf8String) {
String message = ((Utf8String) result.get(0)).getValue();
if (logger.isDebugEnabled()) {
logger.debug(" ABI: {} , RevertMessage: {}", output, message);
}
return new Tuple2<>(true, message);
}
} catch (Exception e) {
logger.warn(" ABI: {}, e: {}", output, e);
}

return new Tuple2<>(false, null);
}

/**
* @param receipt
* @return
*/
public static Tuple2<Boolean, String> tryResolveRevertMessage(TransactionReceipt receipt) {
return tryResolveRevertMessage(receipt.getStatus(), receipt.getOutput());
}
}
Loading

0 comments on commit 6b2aafa

Please sign in to comment.