java - CORDA 无法将状态更改为在流中消耗

标签 java blockchain corda

我正在流程中创建一个新状态,然后尝试使用引用输入来使用该状态。但每次我在结果中看到未使用的状态,尽管我在交易的输入中提供了引用状态。

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 需要一些努力。

Kotlin Based answer

我观察到 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/

相关文章:

java - 胶囊异常(exception)

java - org.apache.xbean.asm5.ClassReader 中的 Spark Java IllegalArgumentException

hyperledger-fabric - Hyperledger Explorer 配置错误

java - 如果aspectj可以单独使用,那么在Spring配置中使用Aspectj有什么好处

javascript - 使用 Bitcore 进行简单的一对一比特币交易

hyperledger-fabric - 如何在 Hyperledger Fabric 中添加读/写访问权限?

java - 通过 IntelliJ 运行客户端

mysql - 缓冲区长度小于 mysql percona 集群中的预期有效负载长度

java - 从另一个子JFrame发送数据到JFrame而不打开新的JFrame

java - 用于乘法矩阵的同步线程