CORDA Невозможно изменить состояние на потребляемое в потоке - PullRequest
0 голосов
/ 09 октября 2019

Я создаю новое состояние в потоке, а затем пытаюсь использовать это состояние, используя эталонный ввод. Но каждый раз, когда я вижу в результате неиспользованное состояние, хотя я предоставлял эталонное состояние на входе транзакции.

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));

}

Этот поток успешно выполняется и возвращает мне уникальный идентификатор для транзакции, но я перепробовал все и не смог найти, как изменить состояниеот неиспользованного к потребленному?

ПОСЛЕ ИСПРАВЛЕНИЯ

Я понял, что vaultQuery на CordaOS по умолчанию возвращает неизрасходованное состояние. Теперь понятно, почему я не смог получить состояние потребления в первую очередь. Еще одна проблема, которую я обнаружил, была нехватка ресурсов в CORDA для Java, хотя я нашел много основанных на kotlin ответов для транзакции с «созданием и потреблением» в одном рабочем процессе, однако преобразование их в JAVA потребовало некоторых усилий.

Ответ на основе Kotlin

Некоторые различия, которые я наблюдал между подходом Java и Kotlin

1) Когда я пытался использоватьтот же сеанс в моей второй транзакции, который использовался в первой транзакции, затем я получаю эту ошибку

java.util.concurrent.ExecutionException: net.corda.core.flows.UnexpectedFlowEndException: попытался получить доступ к завершенной сессии SessionId (toLong = 1984916257986245538) с пустым буфером в java.util.concurrent.CompletableFuture.reportGet (CompletableFuture.java:357) в java.util.concurrent.CompletableFuture.get (CompletableFuture.java:1895) в net.corda.core. concurrent.CordaFutureImpl.get (CordaFutureImpl.kt)

Это означает, что мы должны каждый раз создавать новую сессию для новой транзакции независимо от того, находятся ли они в одном рабочем процессе.

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 нам нужно явно добавить состояние вывода и новый сеанс, если мы хотим создать и использовать состояние в одном и том же рабочем процессе.

Примечание. Поскольку для меня это новая кривая обучения, поэтому, если я допустил какую-либо ошибку в приведенной выше реализации, пожалуйста, поправьте меня. Этот ответ может быть полезен для новичков в Corda, которые хотят писать код на Java, а не на Kotlin.

1 Ответ

0 голосов
/ 09 октября 2019

Состояние

@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;
    }
...

Случай FlowTest

...
...
@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 в качестве выходных данных, которые сообщают 1 потребленное состояние в узле a & b, всего 2 состоянияв узлах a и b (1 израсходовано и 1 не израсходовано).

...