超级账本Fabric在Java上调用GetHistoryForKey时出现空指针异常。

huangapple go评论62阅读模式
英文:

Hyperledger Fabric GetHistoryForKey get null pointer exception on java

问题

我有这个织物样品项目。我修改了其中一个示例进行实验。我启用了couchDB,并尝试获取交易历史记录。在core.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;
}

如果我运行使用新方法的合同的应用程序,在IntelliJ终端中会出现org.hyperledger.fabric.gateway.ContractException:error in simulation: transaction returned with failure: Unexpected error。我检查了不同的Docker日志。org1.example.com也出现了相同的错误。但是peer0.org1....的Docker输出了更多的异常信息。

15:41:31:432 SEVERE  org.hyperledger.fabric.Logger error                                              null java.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

看起来历史记录仍然没有保存在任何地方。或者它正在尝试与一些不存在的对等体进行通信。

英文:

I have the fabric samples project. I modified one of the examples there to experiment. I enabled couchDB and try to get the history of transactions. in core.yaml i enabled the history.

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

        Iterator&lt;KeyModification&gt; 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;
}

If i run my app that uses the new method in contract i get org.hyperledger.fabric.gateway.ContractException: error in simulation: transaction returned with failure: Unexpected error in the intellij terminal. I checked different dockers logs. org1.example.com has the same error. But the peer0.org1.... docker has a but more informative exception.

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

Seems like the history is still not saved anywhere. Or its trying to communicate with some peer that does not exist.

答案1

得分: 2

我遇到了相同的问题,并最终通过修改函数的返回值来解决,针对你的情况,从:

public ArrayList&lt;String&gt; 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 &quot;array&quot;:
            ...
        case &quot;string&quot;:
            ...
        case &quot;number&quot;:
        case &quot;integer&quot;:
        case &quot;boolean&quot;:
        default:
            ...
        }
    } else {
        // 此时我们可以断定该值代表了一个复杂数据类型
        // 因此我们可以从类型注册表中获取这个类型,
        // 并获取它应该具有的属性名称列表
        final DataTypeDefinition dtd = this.typeRegistry.getDataType(ts);
        final Set&lt;String&gt; keySet = dtd.getProperties().keySet();
        ...
    }
}

在我的代码中,我没有使用 ArrayList,而是使用了自定义类型,所以它会直接进入 else 分支。但在你的代码中,我不知道 ArrayList 是否会作为数组进入 if 分支,还是会被解释为自定义类型。我猜可能会进入 else 分支。所以在 else 分支中,如果没有通过 @DataType() 注册 ArrayList 作为自定义类型,它会产生 NullPointerException

因此,我建议你可以尝试使用 String,如果适用的话。

英文:

I encountered the same issue and finally solved it by altering the return value of the function, in your case, from

public ArrayList&lt;String&gt; GetAssetHistory(final Context ctx, final String assetID)

to

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

If you look into this class org.hyperledger.fabric.contract.execution.JSONTransactionSerializer at Github, you could find the function toBuffer convert types to json.

Here is a stripped version:

public byte[] toBuffer(final Object value, final TypeSchema ts) {
    final String type = ts.getType();
    if (type != null) {
        switch (type) {
        case &quot;array&quot;:
            ...
        case &quot;string&quot;:
            ...
        case &quot;number&quot;:
        case &quot;integer&quot;:
        case &quot;boolean&quot;:
        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&lt;String&gt; keySet = dtd.getProperties().keySet();
        ...
    }
}

In my code I didn't use ArrayList but a customized type, so it would go to else directly. But in your code I don't know if the ArrayList would go into if as a array or it would be interpreted as a customized type. I guess it may go to else. So in else, ArrayList is not registered as a custom type via @DataType(), it would produce a NullPointerException.

So here I suggest you could just use String instead if works.

答案2

得分: 0

我遇到了相同的问题。您需要为您的链码实现一个自定义序列化程序。请参考我的博文https://thusithathilina.medium.com/how-to-register-a-custom-serializer-for-chaincode-in-hyperledger-fabric-9e865f6d68d0

步骤:

  1. 实现SerializerInterface并为toBuffer方法提供一个实现,将您的ArrayList转换为byte[]
  2. 使用@Contract注解中的transactionSerializer属性注册您的序列化程序
英文:

I faced the same issue. You need to implement a custom serializer for your chaincode. Refer to my blogpost https://thusithathilina.medium.com/how-to-register-a-custom-serializer-for-chaincode-in-hyperledger-fabric-9e865f6d68d0

Steps

  1. Implement the SerializerInterface and provide an implementation for toBuffer method to convert your ArrayList to a byte[]
  2. Register your serializer with the contract using transactionSerializer attribute inside @Contract annotation

huangapple
  • 本文由 发表于 2020年10月20日 00:00:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/64431056.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定