【发布时间】:2020-07-19 23:44:43
【问题描述】:
我正在使用 Corda 的帐户库,目前无法将状态从一个所有者(帐户)转移到托管在不同节点上的另一个(帐户)。在不同的时间得到不同的错误。但我很确定这与收集签名有关。
可以从所有者帐户(匿名方)收集签名还是必须是拥有密钥的节点。此外,是否必须从州级参与者列表中涉及的所有各方收集签名。例如在下面的示例中,是否有必要在传输过程中从 issue 中收集签名。
发行者使用注册流程向所有者发布状态。 所有者可以使用 Transfer Flow 转让给其他所有者。
我目前在传输流程方面遇到问题。
传输流程
package com.template.flows;
import co.paralleluniverse.fibers.Suspendable;
import com.r3.corda.lib.accounts.contracts.states.AccountInfo;
import com.r3.corda.lib.accounts.workflows.UtilitiesKt;
import com.r3.corda.lib.accounts.workflows.flows.RequestKeyForAccount;
import com.template.accountutilities.NewKeyForAccount;
import com.template.contracts.CarContract;
import com.template.states.CarState;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.StateAndRef;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.flows.*;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.Party;
import net.corda.core.node.services.Vault;
import net.corda.core.node.services.vault.QueryCriteria;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import static com.template.contracts.CarContract.CID;
import java.lang.reflect.Array;
import java.security.PublicKey;
import java.util.*;
import java.util.stream.Collectors;
@InitiatingFlow
@StartableByRPC
public class CarTransferFlowInitiator extends FlowLogic<String> {
private final String carVin;
private final String oldCarOwner;
private final String newCarOwner;
private int input;
public CarTransferFlowInitiator(String carVin,String oldCarOwner, String newCarOwner){
this.carVin = carVin;
this.oldCarOwner = oldCarOwner;
this.newCarOwner = newCarOwner;
}
private final ProgressTracker.Step RETRIEVING_NOTARY = new ProgressTracker.Step("Retrieving Notary");
private final ProgressTracker.Step CREATE_TRANSACTION_INPUT= new ProgressTracker.Step("Creating Transaction Input");
private final ProgressTracker.Step CREATE_TRANSACTION_OUTPUT= new ProgressTracker.Step("Creating Transaction Output");
private final ProgressTracker.Step CREATE_TRANSACTION_BUILDER= new ProgressTracker.Step("Creating transaction Builder");
private final ProgressTracker.Step SIGN_TRANSACTION = new ProgressTracker.Step("Signing Transaction");
private final ProgressTracker.Step INITIATE_SESSION = new ProgressTracker.Step("Initiating session with counterparty");
private final ProgressTracker.Step FINALIZE_FLOW = new ProgressTracker.Step("Finalizing the flow");
private final ProgressTracker progressTracker = new ProgressTracker(
RETRIEVING_NOTARY,
CREATE_TRANSACTION_OUTPUT,
CREATE_TRANSACTION_BUILDER,
SIGN_TRANSACTION,
INITIATE_SESSION,
FINALIZE_FLOW
);
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
public StateAndRef<CarState> checkForCarStates(UUID accountId) throws FlowException {
// This returns the old owners unconsumed state
QueryCriteria.VaultQueryCriteria criteria = new QueryCriteria.VaultQueryCriteria().withExternalIds(Arrays.asList(accountId)).withStatus(Vault.StateStatus.UNCONSUMED);
//QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
//QueryCriteria generalCriteria = new QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED);
List<StateAndRef<CarState>> CarStates = getServiceHub().getVaultService().queryBy(CarState.class, criteria).getStates();
boolean inputFound = false;
int t = CarStates.size();
input = 0;
for (int x = 0; x < t; x++) {
if (CarStates.get(x).getState().getData().getCarVIN().equals(carVin)) {
// if (CarStates.get(x).getState().getData().getLinearId().getExternalId().equals(linearId.getExternalId())) {
input = x;
inputFound = true;
}
}
if (inputFound) {
System.out.println("\n Input Found");
} else {
System.out.println("\n Input not found");
throw new FlowException();
}
return CarStates.get(input);
}
@Suspendable
public String call() throws FlowException {
//Retrieve the notary identity from the network map
progressTracker.setCurrentStep(RETRIEVING_NOTARY);
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
//<-------------------------------------- NEW CODE ------------------------------>
// Get the oldOwnerAccount info here
AccountInfo oldOwnerAccountInfo = UtilitiesKt.getAccountService(this).accountInfo(oldCarOwner).get(0).getState().getData();
//Get the party here
AnonymousParty oldOwnerAccount = subFlow(new RequestKeyForAccount(oldOwnerAccountInfo));
// Get the newOwnerAccount info here
AccountInfo newOwnerAccountInfo = UtilitiesKt.getAccountService(this).accountInfo(newCarOwner).get(0).getState().getData();
//Get the party here
AnonymousParty newOwnerAccount = subFlow(new RequestKeyForAccount(oldOwnerAccountInfo));
//Create transaction components both input and output for this application
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
StateAndRef<CarState> inputState = null;
inputState = checkForCarStates(oldOwnerAccountInfo.getIdentifier().getId());
System.out.println(inputState.getState().getData().getCarMake()+" "+inputState.getState().getData().getCarVIN());
//Issuer is Toyota
//Owner is AutoSmart
//AnonymousParty issuer = inputState.getState().getData().getIssuer();
PublicKey issuerKey = inputState.getState().getData().getIssuer().getOwningKey();
PublicKey newOwnerKey = newOwnerAccount.getOwningKey();
//Create transaction components both input and output for this application
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
//CarState outputState = new CarState(carMake,carModel,carYear,carMileage,carVin,issuer,carOwner);
// String carMake = inputState.getState().getData().getCarMake();
// String carModel = inputState.getState().getData().getCarModel();
// int carYear = inputState.getState().getData().getCarYear();
// double carMile = inputState.getState().getData().getCarMileAge();
// String carVIN = inputState.getState().getData().getCarVIN();
// AnonymousParty carIssuer = inputState.getState().getData().getIssuer();
// This creating a new output ?
// CarState outputState = new CarState(carMake,carModel,carYear,carMile, carVin,carIssuer,newOwnerAccount);
// System.out.println(outputState.getParticipants());
// Thi swill collect signature from Issuer, Old owner and New Owner
// check what to provide here
//List<PublicKey> requiresSigners = Arrays.asList(getOurIdentity().getOwningKey());
// List<PublicKey> requiredSigners = inputState.getState().getData().getParticipants()
// .stream().map(AbstractParty::getOwningKey)
// .collect(Collectors.toList());
// requiredSigners.add(newOwnerAccount.getOwningKey());
List<PublicKey> requiredSigners = Arrays.asList(issuerKey,oldOwnerAccount.getOwningKey(),newOwnerKey);
System.out.println(issuerKey+","+oldOwnerAccount.getOwningKey()+","+newOwnerKey);
final Command<CarContract.Transfer> txCommand = new Command<>(
new CarContract.Transfer(),
requiredSigners
);
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addInputState(inputState)
.addOutputState(inputState.getState().getData().withNewOwner(newOwnerAccount), CID)
.addCommand(txCommand);
// Create the transaction builder here and add compenents to it
progressTracker.setCurrentStep(CREATE_TRANSACTION_BUILDER);
// Sign the transaction
progressTracker.setCurrentStep(SIGN_TRANSACTION);
txBuilder.verify(getServiceHub());
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder, getOurIdentity().getOwningKey());
System.out.println("Current nodes identity: " +getOurIdentity().getName().getOrganisation());
System.out.println("Signed transaction by old owner"+": "+signedTx);
// <----------------------------------------------------------------------->
// Code stops here
List<FlowSession> sessions = new ArrayList<>();
AccountInfo issuerAccount = UtilitiesKt.getAccountService(this).accountInfo("account1").get(0).getState().getData();
sessions.add(initiateFlow(issuerAccount.getHost()));
sessions.add(initiateFlow(newOwnerAccountInfo.getHost()));
// Create session with counterparty
progressTracker.setCurrentStep(INITIATE_SESSION);
System.out.println("Owner Host name is: "+newOwnerAccountInfo.getHost().getName().getOrganisation()+" Account is: "+newOwnerAccountInfo.getName());
System.out.println("Started session with new owner");
SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
signedTx, sessions, Collections.singleton(getOurIdentity().getOwningKey())));
//
// final SignedTransaction fullySignedTx = subFlow(
// new CollectSignaturesFlow(signedTx, sessions, CollectSignaturesFlow.Companion.tracker()));
//
// SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
// signedTx, sessions, CollectSignaturesFlow.tracker()));
// SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
// signedTx, sessions));
System.out.println("Passed the fullySignedTx section of the code.");
//Finalizing the transaction
progressTracker.setCurrentStep(FINALIZE_FLOW);
SignedTransaction sTx = subFlow(new FinalityFlow(fullySignedTx,sessions));
System.out.println("Getting sign from initiator");
return "Transfer Completed";
}
}
州
package com.template.states;
import com.template.contracts.CarContract;
import net.corda.core.contracts.BelongsToContract;
import net.corda.core.contracts.ContractState;
import net.corda.core.contracts.LinearState;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.serialization.ConstructorForDeserialization;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@BelongsToContract(CarContract.class)
public class CarState implements ContractState, LinearState {
private String carMake;
private String carModel;
private int carYear;
private double carMileAge;
private String carVIN;
private AnonymousParty issuer;
private AnonymousParty owner;
private UniqueIdentifier linearId;
private List<AbstractParty> participants;
public CarState(String carMake, String carModel, int carYear, double carMileAge, String carVIN, AnonymousParty issuer, AnonymousParty owner) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileAge = carMileAge;
this.carVIN = carVIN;
this.issuer = issuer;
this.owner = owner;
this.linearId = new UniqueIdentifier();
}
@ConstructorForDeserialization
public CarState(String carMake, String carModel, int carYear, double carMileAge, String carVIN, AnonymousParty issuer, AnonymousParty owner, UniqueIdentifier linearId) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileAge = carMileAge;
this.carVIN = carVIN;
this.issuer = issuer;
this.owner = owner;
this.linearId = linearId;
}
public String getCarMake() {
return carMake;
}
public String getCarModel() {
return carModel;
}
public int getCarYear() {
return carYear;
}
public double getCarMileAge() {
return carMileAge;
}
public String getCarVIN() {
return carVIN;
}
public AnonymousParty getIssuer() {
return issuer;
}
public AnonymousParty getOwner() {
return owner;
}
@NotNull
@Override
public List<AbstractParty> getParticipants() {
return Arrays.asList(issuer,owner);
}
@NotNull
@Override
public UniqueIdentifier getLinearId() {
return linearId;
}
public CarState withNewOwner(AnonymousParty newOwner){
return new CarState(carMake,carModel,carYear,carMileAge, carVIN,issuer,newOwner, linearId);
}
}
【问题讨论】: