我正在流程中创建一个新状态,然后尝试使用引用输入来使用该状态。但每次我在结果中看到未使用的状态,尽管我在交易的输入中提供了引用状态。
public SignedTransaction call() throws FlowException {
//------------------------------------------------------------------------------------------------------------
// STEP-1:
// FIRST FLOW MUST CREATE THE NEW STATE WHICH HAS NO INPUT ( THIS WILL CREATE NEW RECORD-ANCHOR WITH LINEARID )
//
//------------------------------------------------------------------------------------------------------------
// We retrieve the notary identity from the network map.
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// We create the transaction components.
AnchorState outputState = new
AnchorState(ownerId,contentHash,description,classid,timestamp,expiry, getOurIdentity(), otherParty,new UniqueIdentifier());
//required signers
List<PublicKey> requiredSigners = Arrays.asList(getOurIdentity().getOwningKey(),otherParty.getOwningKey());
//send create command with required signer signatures as below
Command command = new Command<>(new AnchorStateContract.Commands.CreateRecAnchorCmd(), requiredSigners);
// We create a transaction builder and add the components.
TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(outputState, AnchorStateContract.ID)
.addCommand(command);
// Verifying the transaction.
txBuilder.verify(getServiceHub());
// Signing the transaction.
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Creating a session with the other party.
FlowSession otherPartySession = initiateFlow(otherParty);
// Obtaining the counterparty's signature.
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, Arrays.asList(otherPartySession), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
SignedTransaction notraizedtransaction = subFlow(new FinalityFlow(fullySignedTx, otherPartySession));
//------------------------------------------------------------------------------------------------------------
// STEP-2:
// SINCE NOW WE HAVE A NEW UNCONSUMED RECORD-ANCHOR SO WE MUST MAKE IT CONSUMED ( BY USING THE PREVIOUS OUTPUT AS AN INPUT)
//
//------------------------------------------------------------------------------------------------------------
StateAndRef oldStateref = getServiceHub().toStateAndRef(new StateRef(notraizedtransaction.getId(),0));
Command storeCommand = new Command<>(new AnchorStateContract.Commands.ApproveRecAnchorCmd(), requiredSigners);
TransactionBuilder txBuilder2 = new TransactionBuilder(notary)
.addInputState(oldStateref)
.addOutputState(outputState, AnchorStateContract.ID)
.addCommand(storeCommand);
txBuilder2.verify(getServiceHub());
// signing
SignedTransaction signedTx2 = getServiceHub().signInitialTransaction(txBuilder2);
// Creating a session with the other party.
FlowSession otherPartySession2 = initiateFlow(otherParty);
// Finalising the transaction.
SignedTransaction fullySignedTx2 = subFlow(new CollectSignaturesFlow(
signedTx2, Arrays.asList(otherPartySession2), CollectSignaturesFlow.Companion.tracker()));
//notarized transaction
return subFlow(new FinalityFlow(fullySignedTx2, otherPartySession2));
}
在我的流程启动器类中,我首先创建哈希的新状态,我将其称为 AnchorState。该状态来自其中一个参与者,然后请求另一个参与者签名。之后签名的记录存储在分类帐中,但其引用用作新状态更改的输入,我只是想让此状态成为已消耗状态而不是未消耗状态。
参与者B的响应流类如下
public SignedTransaction call() throws FlowException
{
//this class is used inside call function for the verification purposes before signed by this party
class SignTxFlow extends SignTransactionFlow
{
private SignTxFlow(FlowSession otherPartySession) {
super(otherPartySession);
}
@Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
ContractState output = stx.getTx().getOutputs().get(0).getData();
require.using("This must be an AnchorState transaction.", output instanceof AnchorState);
AnchorState state = (AnchorState) output;
require.using("The AnchorState's value should be more than 6 characters", state.getContentHash().length() > 6);
return null;
});
}
}
SecureHash expectedTxId = subFlow(new SignTxFlow(otherPartySession)).getId();
return subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId));
}
此流程成功运行并返回交易的唯一 ID,但我尝试了所有方法,但找不到如何将状态从未使用状态更改为已使用状态?
修复后
我意识到 CordaOS 上的VaultQuery默认返回未使用状态。现在很清楚为什么我一开始就无法获得消耗状态。我发现的另一个问题是 CORDA for java 缺乏资源,尽管我发现许多基于 kotlin 的答案用于在单个工作流程中进行“创建和消费”事务,但是将它们转换为 JAVA 需要一些努力。
我观察到 Java 和 Kotlin 方法之间的一些差异
1)当我尝试在第二个事务中使用第一个事务中使用的相同 session 时,我收到此错误
java.util.concurrent.ExecutionException:net.corda.core.flows.UnexpectedFlowEndException:尝试使用空缓冲区访问已结束的 session SessionId(toLong = 1984916257986245538) 在 java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) 在 java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895) 在net.corda.core.internal.concurrent.CordaFutureImpl.get(CordaFutureImpl.kt)这意味着我们每次都必须为新事务创建新 session ,无论它们是否在单个工作流程中。
2) 据我了解,通过查看 Kotlin 解决方案,如果我们只想消耗它,则不需要在事务中添加输出。但是,当我不在第二个事务中添加输出状态时,我会收到以下错误,这意味着即使对于已消耗状态,我也必须在事务中添加相同的输出。否则会再次出现下面的错误。
ava.util.concurrent.ExecutionException:net.corda.core.flows.UnexpectedFlowEndException:逆流错误 在 java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) 在 java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895) 在 net.corda.core.internal.concurrent.CordaFutureImpl.get(CordaFutureImpl.kt) 在 com.etasjil.Client.testFlow(Client.java:92)
因此很明显,与 kotlin 不同,在 java 中,如果我们想在同一工作流程中创建和使用状态,我们需要显式添加输出状态和新 session 。
注意:由于这对我来说是一个新的学习曲线,因此,如果我在上述实现中犯了任何错误,请纠正我。对于想要使用 Java 而不是 Kotlin 进行编码的 Corda 新手来说,这个答案可能会有好处。
最佳答案
状态
@BelongsToContract(AnchorStateContract.class)
public class AnchorState implements LinearState {
public String ownerId,contentHash,description,classid,timestamp,expiry;
public Party initiatorParty, otherParty;
public UniqueIdentifier linearId;
@Override
public List<AbstractParty> getParticipants() {
return Arrays.asList(initiatorParty, otherParty);
}
public AnchorState() {
}
@ConstructorForDeserialization
public AnchorState(String ownerId, String contentHash, String description, String classid, String timestamp, String expiry, Party initiatorParty, Party otherParty, UniqueIdentifier linearId) {
this.ownerId = ownerId;
this.contentHash = contentHash;
this.description = description;
this.classid = classid;
this.timestamp = timestamp;
this.expiry = expiry;
this.initiatorParty = initiatorParty;
this.otherParty = otherParty;
this.linearId = linearId;
}
...
流程测试用例
...
...
@Test
public void test1() {
Future data = a.startFlow(new Initiator("Owner1", "1234567", "Description", "c1", Instant.now().toString(), Instant.MAX.toString(), b.getInfo().getLegalIdentities().get(0).getName().toString()));
network.runNetwork();
try {
System.out.println(data.get());
}catch (Exception e){
System.out.println(e.getMessage());
}
QueryCriteria.VaultQueryCriteria criteria1 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.CONSUMED);
Vault.Page<AnchorState> results1 = a.getServices().getVaultService().queryBy(AnchorState.class, criteria1);
System.out.println("--------------------- "+ results1.getStates().size());
QueryCriteria.VaultQueryCriteria criteria2 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
Vault.Page<AnchorState> results2 = a.getServices().getVaultService().queryBy(AnchorState.class, criteria2);
System.out.println("--------------------- "+ results2.getStates().size());
QueryCriteria.VaultQueryCriteria criteria3 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.CONSUMED);
Vault.Page<AnchorState> results3 = b.getServices().getVaultService().queryBy(AnchorState.class, criteria3);
System.out.println("--------------------- "+ results3.getStates().size());
QueryCriteria.VaultQueryCriteria criteria4 = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL);
Vault.Page<AnchorState> results4 = b.getServices().getVaultService().queryBy(AnchorState.class, criteria4);
System.out.println("--------------------- "+ results4.getStates().size());
}
我得到 1,2,1,2 作为输出,它告诉节点 a 和 b 中的 1 个已消耗状态,节点 a 和 b 中总共有 2 个状态(1 个已消耗,1 个未消耗)。
关于java - CORDA 无法将状态更改为在流中消耗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58299800/