Как перехватить сообщение о возврате контракта Solidity с помощью web3j (Java)

Есть ли способ поймать сообщение о возврате солидности, такое как Remix Javascript VM в web3j (Java)

Вернуть сообщение (ремикс)

transact to Erecruitment.issueNewAdmitCard errored: VM error: revert.
revert  The transaction has been reverted to the initial state.
Reason provided by the contract: "Admit card ID already exists in Blockchain".  Debug the transaction to get more information. 

Сообщение о событии (ремикс)

 logs   [
    {
        "from": "0x692a70d2e424a56d2c6c27aa97d1a86395877b3a",
        "topic": "0xbf970614f4ff9483c34fa1a053bc6614e06003a1faef2d0876e9255bfa3167fc",
        "event": "LogIssueNewCard",
        "args": {
            "0": "2",
            "1": "4",
            "2": "New Card is issued in Blockchain",
            "admitCardId": "2",
            "applicantName": "4",
            "message": "New Card is issued in Blockchain",
            "length": 3
        }
    }
]

Я получаю сообщения о событиях для успешных транзакций в web3j. Таким образом, я могу быть уверен, что моя транзакция будет завершена без каких-либо ошибок. Если транзакция будет отменена, события, как обычно, не будет (все в порядке). Но я также не получаю сообщения об отмене для неудачной транзакции.

И я получаю статус транзакции nullкаждый раз, когда использую web3j, поскольку в моем файле генезиса отсутствует byzantiumBlock . Тогда как я могу проверить статус транзакции?

Ответы (4)

В брали 0.4.22 то requireи revertпричина были добавлены. Как видно здесь , они закодированы так, как если бы это был вызов функции «Ошибка (строка)».

Вам нужно будет составить eth_callдоговор.

В этом сообщении блога приводится пример: eth_callдля функции

function myFunction(uint256 input) public view returns (uint256) {
    require(input >= 5, "myFunction only accepts arguments which are greather than or equal to 5");
    return input * input - 25;
}

с недопустимым входным аргументом (меньше 5 в этом примере) вернет

0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000476d7946756e6374696f6e206f6e6c79206163636570747320617267756d656e747320776869636820617265206772656174686572207468616e206f7220657175616c20746f203500000000000000000000000000000000000000000000000000

который

0x08c379a0                                                       // Function selector
0000000000000000000000000000000000000000000000000000000000000020 // Offset of string return value
0000000000000000000000000000000000000000000000000000000000000047 // Length of string return value (the revert reason)
6d7946756e6374696f6e206f6e6c79206163636570747320617267756d656e74 // first 32 bytes of the revert reason
7320776869636820617265206772656174686572207468616e206f7220657175 // next 32 bytes of the revert reason
616c20746f203500000000000000000000000000000000000000000000000000 // last 7 bytes of the revert reason

Таким образом, декодирование возвращаемой строки даст вам причину возврата.

С Web3j это можно сделать так:

public Optional<String> getRevertReason(EthCall ethCall) {
    String errorMethodId = "0x08c379a0"; // Numeric.toHexString(Hash.sha3("Error(string)".getBytes())).substring(0, 10)
    List<TypeReference<Type>> revertReasonTypes = Collections.singletonList(TypeReference.create((Class<Type>) AbiTypes.getType("string")));

    if (!ethCall.hasError() && ethCall.getValue() != null && ethCall.getValue().startsWith(errorMethodId)) {
        String encodedRevertReason = ethCall.getValue().substring(errorMethodId.length());
        List<Type> decoded = FunctionReturnDecoder.decode(encodedRevertReason, revertReasonTypes);
        Utf8String decodedRevertReason = (Utf8String) decoded.get(0);
        return Optional.of(decodedRevertReason.getValue());
    }
    return Optional.empty();
}

Я не уверен, что получение сообщения об отмене возможно с помощью web3j, однако я получаю статус своих транзакций с помощью этой команды (но вам следует подождать, пока транзакция не будет завершена и ваш узел не будет синхронизирован)

EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt("The Hash of your Transaction").send();
    if (transactionReceipt.getResult() != null && !transactionReceipt.hasError()) {
        System.out.println(transactionReceipt.getTransactionReceipt().get().getStatus());
    } else {
      "TRY AGAIN SOMEHOW LATER.. :-)"
    }

Что касается вашего вопроса о событиях, я думаю, что в сгенерированных оболочках смарт-контрактов есть методы событий, которые вы можете легко использовать. см.: https://docs.web3j.io/smart_contracts.html

Я ценю ваш ответ, но вы получите результат для каждой добытой транзакции, но можете не получить поле результата (так как это зависит от поля byzantiumBlock в файле генезиса). Я согласен с тем, что события могут вам помочь, но событие не будет запущено, если оно не будет выполнено, если выполнение достигнет этой строки. что вы думаете?
Да, с событиями вы не можете быть уверены, что ваша транзакция не удалась или просто застряла. Я еще не пробовал, но, возможно, вы можете каким-то образом использовать FailedEvent, который был отправлен, когда (идентификатор допущенной карты уже существует в блокчейне), и отменить то, что произошло до этого выброса.
@sharif2008, возможно, это могло бы как-то помочь: я использовал Ganache с automine, затем этот код выдал мне сообщение об отмене EthSendTransaction transactionTx = web3j.ethSendRawTransaction(sigendTransactionData).sendAsync().get(); если (transactionTx.hasError()) { System.out.println(transactionTx.getError().getMessage()); }

Я сделал пакет NPM только по этой причине: eth-revert-reason


Трудно расшифровать причину возврата в общем виде. Множество различных факторов, таких как Parity и Geth, эфиры и web3.js и т. д., приведут к разным результатам. Вот некоторые проблемы:

  1. Для транзакции Kovan вам нужен собственный поставщик, который предоставляет traceметоды Parity.
  2. Транзакции могут приводить к различным сообщениям в зависимости от контекста блока, из которого они вызываются. Из-за этого вам может потребоваться запустить узел полного архивирования, чтобы получить правильное сообщение об ошибке.

В случае счастливого пути код для получения причины возврата:

const provider = customProvider || ethers.getDefaultProvider(network)
const tx = await provider.getTransaction(txHash)
const code = await provider.call(tx)

В то время как код для создания причины возврата в обычном случае прост, нестандартные случаи удивительно сложны в обработке, однако этот пакет пытается решить эту проблему.

Начиная с web3j 4.6 для этой цели существует класс RevertReasonExtractor.

import org.web3j.utils.RevertReasonExtractor;
...
  String revertReason;
  try {
    revertReason = RevertReasonExtractor.extractRevertReason(
                                              transactionReceipt,
                                              inputData,
                                              web3,
                                              true);
  } catch (IOException e) {
     revertReason="?? could not reach network " + e.getMessage();
  }
  return(revertReason);