Hyperledger Fabric GetHistoryForKey получает исключение нулевого указателя на java

#java #hyperledger-fabric

#java #hyperledger-fabric

Вопрос:

У меня есть проект fabric samples. Я изменил один из примеров, чтобы поэкспериментировать. Я включил CouchDB и пытаюсь получить историю транзакций. в ядре.yaml я включил историю.

    @Transaction(intent = Transaction.TYPE.EVALUATE)
public ArrayList<String> GetAssetHistory(final Context ctx, final String assetID) {
    ChaincodeStub stub = ctx.getStub();
    ArrayList<String> results = new ArrayList<>();
    try {
        QueryResultsIterator<KeyModification> history = stub.getHistoryForKey(assetID);

        Iterator<KeyModification> iter = history.iterator();
        while(iter.hasNext()){
                results.add(iter.next().getStringValue());
        }
    }
    catch(Exception e){
        results.add(e.getMessage());
        results.add(e.getCause().getMessage());
        results.add(e.getStackTrace().toString());
    }
    return results;
}
  

Если я запускаю свое приложение, которое использует новый метод в контракте, я попадаю org.hyperledger.fabric.gateway.ContractException: error in simulation: transaction returned with failure: Unexpected error в терминал intellij. Я проверил разные журналы docker. org1.example.com имеет ту же ошибку. Но у peer0.org1 …. docker есть исключение, но более информативное.

 15:41:31:432 SEVERE  org.hyperledger.fabric.Logger error                                              nulljava.lang.NullPointerException
    at org.hyperledger.fabric.contract.execution.JSONTransactionSerializer.toBuffer(JSONTransactionSerializer.java:84)
    at org.hyperledger.fabric.contract.execution.impl.ContractExecutionService.convertReturn(ContractExecutionService.java:89)
    at org.hyperledger.fabric.contract.execution.impl.ContractExecutionService.executeRequest(ContractExecutionService.java:67)
    at org.hyperledger.fabric.contract.ContractRouter.processRequest(ContractRouter.java:115)
    at org.hyperledger.fabric.contract.ContractRouter.invoke(ContractRouter.java:126)
    at org.hyperledger.fabric.shim.impl.ChaincodeInvocationTask.call(ChaincodeInvocationTask.java:91)
    at org.hyperledger.fabric.shim.impl.InvocationTaskManager.lambda$newTask$17(InvocationTaskManager.java:225)
    at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)

15:41:31:436 SEVERE  org.hyperledger.fabric.shim.impl.ChaincodeInvocationTask call                    [e0aa10de] Invoke failed with error code 500. Sending ERROR
  

Похоже, что история по-прежнему нигде не сохраняется. Или он пытается связаться с каким-либо узлом, которого не существует.

Ответ №1:

Я столкнулся с той же проблемой и, наконец, решил ее, изменив возвращаемое значение функции, в вашем случае, из

 public ArrayList<String> GetAssetHistory(final Context ctx, final String assetID)
  

Для

 public String GetAssetHistory(final Context ctx, final String assetID)
  

Если вы посмотрите на этот класс org.hyperledger.fabric.contract.execution.JSONTransactionSerializer на Github, вы можете найти функцию toBuffer преобразования типов в json.

Вот урезанная версия:

 public byte[] toBuffer(final Object value, final TypeSchema ts) {
    final String type = ts.getType();
    if (type != null) {
        switch (type) {
        case "array":
            ...
        case "string":
            ...
        case "number":
        case "integer":
        case "boolean":
        default:
            ...
        }
    } else {
        // at this point we can assert that the value is
        // representing a complex data type
        // so we can get this from
        // the type registry, and get the list of propertyNames
        // it should have
        final DataTypeDefinition dtd = this.typeRegistry.getDataType(ts);
        final Set<String> keySet = dtd.getProperties().keySet();
        ...
    }
}
  

В моем коде я не использовал ArrayList , но настраиваемый тип, поэтому он будет else использоваться напрямую. Но в вашем коде я не знаю, ArrayList войдет ли он в if массив или будет интерпретироваться как настраиваемый тип. Я думаю, это может пойти на else . Таким образом, in else , ArrayList не зарегистрирован как пользовательский тип via @DataType() , это привело NullPointerException бы к.

Итак, здесь я предлагаю вам просто использовать String вместо этого, если работает.

Ответ №2:

Я столкнулся с той же проблемой. Вам необходимо реализовать пользовательский сериализатор для вашего цепного кода. Обратитесь к моему сообщению в блоге https://thusithathilina.medium.com/how-to-register-a-custom-serializer-for-chaincode-in-hyperledger-fabric-9e865f6d68d0

Шаги

  1. Реализуйте SerializerInterface и предоставьте реализацию для toBuffer метода для преобразования вашего ArrayList в byte[]
  2. Зарегистрируйте свой сериализатор в контракте, используя transactionSerializer атрибут внутри @Contract аннотации