英文:
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<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;
}
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<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 {
// 此时我们可以断定该值代表了一个复杂数据类型
// 因此我们可以从类型注册表中获取这个类型,
// 并获取它应该具有的属性名称列表
final DataTypeDefinition dtd = this.typeRegistry.getDataType(ts);
final Set<String> 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<String> 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 "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();
...
}
}
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
步骤:
- 实现
SerializerInterface
并为toBuffer
方法提供一个实现,将您的ArrayList
转换为byte[]
- 使用
@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
- Implement the
SerializerInterface
and provide an implementation fortoBuffer
method to convert yourArrayList
to abyte[]
- Register your serializer with the contract using
transactionSerializer
attribute inside@Contract
annotation
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论