Я создал пример приложения в Corda, а также создал два потока.
Из первого потока я создаю состояние, а затем потребляю то же состояние в другом созданном мною потоке.
Ниже первый поток
@InitiatingFlow
@StartableByRPC
public class Initiator extends FlowLogic<SignedTransaction> {
private final String batchId;
private final int numTomato;
private final Party toUser;
private final ProgressTracker.Step GENERATING_TRANSACTION = new ProgressTracker.Step("Generating transaction based on new BatchTransaction.");
private final ProgressTracker.Step VERIFYING_TRANSACTION = new ProgressTracker.Step("Verifying contract constraints.");
private final ProgressTracker.Step SIGNING_TRANSACTION = new ProgressTracker.Step("Signing transaction with our private key.");
private final ProgressTracker.Step GATHERING_SIGS = new ProgressTracker.Step("Gathering the counterparty's signature.") {
@Override
public ProgressTracker childProgressTracker() {
return CollectSignaturesFlow.Companion.tracker();
}
};
private final ProgressTracker.Step FINALISING_TRANSACTION = new ProgressTracker.Step("Obtaining notary signature and recording transaction.") {
@Override
public ProgressTracker childProgressTracker() {
return FinalityFlow.Companion.tracker();
}
};
private final ProgressTracker progressTracker = new ProgressTracker(
GENERATING_TRANSACTION,
VERIFYING_TRANSACTION,
SIGNING_TRANSACTION,
GATHERING_SIGS,
FINALISING_TRANSACTION
);
public Initiator(String batchId, int numTomato, Party toUser) {
this.batchId = batchId;
this.numTomato = numTomato;
this.toUser = toUser;
}
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
ServiceHub serviceHub = getServiceHub();
List<StateAndRef<TemplateState>> statesFromVault = serviceHub.getVaultService().queryBy(TemplateState.class).getStates();
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
//Stage 1
// Generating Unsigned Transaction
Party me = getOurIdentity();
TemplateState tempState = new TemplateState(batchId, numTomato, me, toUser, new UniqueIdentifier());
// LedgerTransaction tx = new LedgerTransaction();
// Command command = tx.getCommand(0);
final Command<TemplateContract.Commands.Register> txCommand = new Command<>(
new TemplateContract.Commands.Register(),
ImmutableList.of(tempState.getFromUser().getOwningKey(), tempState.getToUser().getOwningKey()));
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(tempState)
.addCommand(txCommand);
// Stage 2.
progressTracker.setCurrentStep(VERIFYING_TRANSACTION);
// Verify that the transaction is valid.
txBuilder.verify(getServiceHub());
// Stage 3.
progressTracker.setCurrentStep(SIGNING_TRANSACTION);
//sign the transaction
final SignedTransaction partSignedTx = getServiceHub().signInitialTransaction(txBuilder);
// Stage 4.
progressTracker.setCurrentStep(GATHERING_SIGS);
// Send the state to the counterparty, and receive it back with their signature.
FlowSession ownerSession = initiateFlow(toUser);
final SignedTransaction fullySignedTx = subFlow(
new CollectSignaturesFlow(partSignedTx, ImmutableSet.of(ownerSession), CollectSignaturesFlow.Companion.tracker()));
// Stage 5.
progressTracker.setCurrentStep(FINALISING_TRANSACTION);
// Notarise and record the transaction in both parties' vaults.
return subFlow(new FinalityFlow(fullySignedTx, ImmutableSet.of(ownerSession)));
}
@InitiatedBy(Initiator.class)
public static class Acceptor extends FlowLogic<SignedTransaction> {
private final FlowSession ownerSession;
public Acceptor(FlowSession ownerSession) {
this.ownerSession = ownerSession;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
class SignTxFlow extends SignTransactionFlow {
private SignTxFlow(FlowSession ownerPartyFlow, ProgressTracker progressTracker) {
super(ownerPartyFlow, progressTracker);
}
@Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
ContractState output = stx.getTx().getOutputs().get(0).getData();
require.using("This must be an TomatoBatch transaction.", output instanceof TemplateState);
return null;
});
}
}
final SignTxFlow signTxFlow = new SignTxFlow(ownerSession, SignTransactionFlow.Companion.tracker());
final SecureHash txId = subFlow(signTxFlow).getId();
return subFlow(new ReceiveFinalityFlow(ownerSession, txId));
}
}
}
Ниже находится второй поток
package com.template.flows;
@InitiatingFlow
@StartableByRPC
public class TransferBatch extends FlowLogic<SignedTransaction>{
private final String batchId;
private final Party toUser;
private final ProgressTracker.Step GENERATING_TRANSACTION = new ProgressTracker.Step("Generating transaction based on new BatchTransaction.");
private final ProgressTracker.Step VERIFYING_TRANSACTION = new ProgressTracker.Step("Verifying contract constraints.");
private final ProgressTracker.Step SIGNING_TRANSACTION = new ProgressTracker.Step("Signing transaction with our private key.");
private final ProgressTracker.Step GATHERING_SIGS = new ProgressTracker.Step("Gathering the counterparty's signature.") {
@Override
public ProgressTracker childProgressTracker() {
return CollectSignaturesFlow.Companion.tracker();
}
};
private final ProgressTracker.Step FINALISING_TRANSACTION = new ProgressTracker.Step("Obtaining notary signature and recording transaction.") {
@Override
public ProgressTracker childProgressTracker() {
return FinalityFlow.Companion.tracker();
}
};
private final ProgressTracker progressTracker = new ProgressTracker(
GENERATING_TRANSACTION,
VERIFYING_TRANSACTION,
SIGNING_TRANSACTION,
GATHERING_SIGS,
FINALISING_TRANSACTION
);
public TransferBatch(String batchId, Party toUser) throws NoSuchFieldException {
this.batchId = batchId;
this.toUser = toUser;
}
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
ServiceHub serviceHub = getServiceHub();
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
Party me = getOurIdentity();
QueryCriteria generalCriteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
FieldInfo attributeCurrency = null;
try {
attributeCurrency = getField("batchId", TemplateSchemaV1.PersistentBatch.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
CriteriaExpression currencyIndex = Builder.equal(attributeCurrency, this.batchId);
QueryCriteria customCriteria1 = new VaultCustomQueryCriteria(currencyIndex);
QueryCriteria criteria = generalCriteria.and(customCriteria1);
StateAndRef<TemplateState> state = serviceHub.getVaultService().queryBy(TemplateState.class, criteria).getStates().get(0);
TemplateState dataState = state.getState().getData();
TemplateState outState = new TemplateState(dataState.getBatchId(), dataState.getNumTomato(), me, toUser, dataState.getLinearId());
final Command<TemplateContract.Commands.Register> txCommand = new Command<>(
new TemplateContract.Commands.Register(),
ImmutableList.of(me.getOwningKey(), outState.getToUser().getOwningKey()));
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addInputState(state)
.addOutputState(outState)
.addCommand(txCommand);
progressTracker.setCurrentStep(VERIFYING_TRANSACTION);
// Verify that the transaction is valid.
txBuilder.verify(getServiceHub());
progressTracker.setCurrentStep(SIGNING_TRANSACTION);
//sign the transaction
final SignedTransaction partSignedTx = getServiceHub().signInitialTransaction(txBuilder);
progressTracker.setCurrentStep(GATHERING_SIGS);
// Send the state to the counterparty, and receive it back with their signature.
FlowSession ownerSession = initiateFlow(toUser);
FlowSession oldOwnerSession = initiateFlow(dataState.getFromUser());
FlowSession fromUserSession = initiateFlow(outState.getFromUser());
final SignedTransaction fullySignedTx = subFlow(
new CollectSignaturesFlow(partSignedTx, ImmutableSet.of(ownerSession), CollectSignaturesFlow.Companion.tracker()));
// Stage 5.
progressTracker.setCurrentStep(FINALISING_TRANSACTION);
List<FlowSession> partySessions = Arrays.asList(ownerSession, oldOwnerSession);
// Notarise and record the transaction in both parties' vaults.
return subFlow(new FinalityFlow(fullySignedTx, partySessions));
}
@InitiatedBy(TransferBatch.class)
public static class Acceptor extends FlowLogic<SignedTransaction> {
private final FlowSession ownerSession;
public Acceptor(FlowSession ownerSession) {
this.ownerSession = ownerSession;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
class SignTxFlow extends SignTransactionFlow {
private SignTxFlow(FlowSession ownerPartyFlow, ProgressTracker progressTracker) {
super(ownerPartyFlow, progressTracker);
}
@Override
protected void checkTransaction(SignedTransaction stx) {
requireThat(require -> {
ContractState output = stx.getTx().getOutputs().get(0).getData();
require.using("This must be an TomatoBatch transaction.", output instanceof TemplateState);
return null;
});
}
}
final SignTxFlow signTxFlow = new SignTxFlow(ownerSession, SignTransactionFlow.Companion.tracker());
final SecureHash txId = subFlow(signTxFlow).getId();
return subFlow(new ReceiveFinalityFlow(ownerSession, txId));
}
}
}
Если вы видите в потоках, я искал существующее состояние из хранилища и смог создать другое состояние из существующего, добавив состояние ввода и вывода во второй поток.
Все операции работают нормально. Но когда я проверяю состояние в первом узле, я все равно вижу его как неизрасходованное.
Пожалуйста, дайте мне знать, что мне здесь не хватает.