I am getting the following error:
[ERROR] 14:37:15-0400 [Node thread-1] internal.Verifier. - Error validating transaction 95C242529D07CCC5F657909F7A1D40EF8F5BD5D748D81E97C3E4F2534BC54334. [errorCode=1oup47m, moreInformationAt=https://errors.corda.net/OS/4.4/1oup47m] {actor_id=internalShell, actor_owning_identity=O=Toyota, L=London, C=GB, actor_store_id=NODE_CONFIG, fiber-id=10000001, flow-id=b716ec51-d40e-4a9e-a7bc-d3e917ea6dd8, invocation_id=a317a00a-af20-454a-bcc7-02d131be67c7, invocation_timestamp=2020-06-23T18:37:13.897Z, origin=internalShell, session_id=90c87b96-97a4-4771-8b40-36a30312fa91, session_timestamp=2020-06-23T18:37:12.984Z, thread-id=188}
when I try to run from Corda node (Toyota) terminal:
start CarRegistrationFlowInitiator carMake: Toyota , carModel: Rav4 , carYear: 2016 , carMileage: 31424.0 , carVin: asdfghjkloiu76543 , carOwner: "O=AutoSmart,L=New York,C=US"
I have two partys Toyota and AutoSmart. Toyota is the Issuer and AutoSmart is the Owner.
CarState:
import com.template.contracts.CarContract;
import net.corda.core.contracts.BelongsToContract;
import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
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 {
private String carMake;
private String carModel;
private int carYear;
private float carMileAge;
private String carVIN;
private Party issuer;
private Party owner;
public CarState(String carMake, String carModel, int carYear, float carMileAge, String carVIN, Party issuer, Party owner) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileAge = carMileAge;
this.carVIN = carVIN;
this.issuer = issuer;
this.owner = owner;
}
public String getCarMake() {
return carMake;
}
public String getCarModel() {
return carModel;
}
public int getCarYear() {
return carYear;
}
public float getCarMileAge() {
return carMileAge;
}
public String getCarVIN() {
return carVIN;
}
public Party getIssuer(){
return issuer;
}
public Party getOwner(){
return owner;
}
@NotNull
@Override
public List<AbstractParty> getParticipants() {
return Arrays.asList(issuer,owner);
}
}
CarContract:
import net.corda.core.contracts.Command;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract;
import net.corda.core.contracts.ContractState;
import net.corda.core.identity.Party;
import net.corda.core.transactions.LedgerTransaction;
import org.jetbrains.annotations.NotNull;
import com.template.states.CarState;
import java.awt.*;
import java.security.PublicKey;
import java.util.List;
public class CarContract implements Contract {
public static final String CID = "com.template.contracts.CarContract";
@Override
public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
// shape constraints
if (tx.getCommands().size()!=1){
throw new IllegalArgumentException("Should contain only one command");
}
Command command = tx.getCommand(0);
CommandData commandType = command.getValue();
if(commandType instanceof Register){
// shape constraints
if (!(tx.getInputStates().size()!=0)){
throw new IllegalArgumentException("Registration contract should have no input states.");
}
if (tx.getOutputStates().size() != 1){
throw new IllegalArgumentException("Registration contract should only have single output state");
}
// content constraints
ContractState outputState = tx.getOutput(0);
if (!(outputState instanceof CarState)){
throw new IllegalArgumentException("Not a car state");
}
if (!((CarState) outputState).getCarMake().equals("Toyota") ||
((CarState) outputState).getCarMake().equals("Honda") ||
((CarState) outputState).getCarMake().equals("Subaru")){
throw new IllegalArgumentException("Car must be either a Honda, Toyota or a Subaru");
}
if (((CarState) outputState).getCarYear()<2015){
throw new IllegalArgumentException("Car must not be older than 2015 year model.");
}
if (((CarState) outputState).getCarVIN().length()!=17){
throw new IllegalArgumentException("VIN must be 17 characters in length and valid.");
}
//signers constraints
Party issuer = ((CarState) outputState).getIssuer();
PublicKey issuerKey = issuer.getOwningKey();
List<PublicKey> requiredSigners = command.getSigners();
if (!(requiredSigners.contains(issuerKey))){
throw new IllegalArgumentException("Both issuer and owner of car must sign the contract.");
}
}
else{
throw new IllegalArgumentException("Commnd type not recognixed");
}
//
}
public static class Register implements CommandData{};
}
CarRegistrationFlowInitiator
import co.paralleluniverse.fibers.Suspendable;
import com.template.contracts.CarContract;
import com.template.states.CarState;
import net.corda.core.contracts.Command;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import java.security.PublicKey;
import java.util.ArrayList;
@InitiatingFlow
@StartableByRPC
public class CarRegistrationFlowInitiator extends FlowLogic<String> {
private String carMake;
private String carModel;
private int carYear;
private String carVin;
private float carMileage;
private Party carOwner;
public CarRegistrationFlowInitiator(String carMake, String carModel, int carYear, float carMileage, String carVin, Party carOwner) {
this.carMake = carMake;
this.carModel = carModel;
this.carYear = carYear;
this.carMileage = carMileage;
this.carVin = carVin;
this.carOwner = carOwner;
}
private final ProgressTracker.Step RETRIEVING_NOTARY = new ProgressTracker.Step("Retrieving Notary");
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
);
private Party counterParty;
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
@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);
//Create transaction components inputs and outputs
progressTracker.setCurrentStep(CREATE_TRANSACTION_OUTPUT);
CarState outputState = new CarState(carMake,carModel,carYear,carMileage,carVin,getOurIdentity(),carOwner);
// Create the transaction builder here and add compenents to it
progressTracker.setCurrentStep(CREATE_TRANSACTION_BUILDER);
TransactionBuilder txB = new TransactionBuilder(notary);
// PublicKey issuerKey = getServiceHub().getMyInfo().getLegalIdentitiesAndCerts().get(0).getOwningKey();
// PublicKey ownerKey = carOwner.getOwningKey();
//List<PublicKey> requiredSigners = ImmutableList.of(issuerKey,ownerKey);
//ArrayList<PublicKey> requiredSigners = new ArrayList<PublicKey>();
//requiredSigners.add(issuerKey);
//requiredSigners.add(ownerKey);
Command cmd = new Command(new CarContract.Register(), getOurIdentity().getOwningKey());
txB.addOutputState(outputState,"com.template.contracts.CarContract")
.addCommand(cmd);
// Sign the transaction
progressTracker.setCurrentStep(SIGN_TRANSACTION);
SignedTransaction signedTx = getServiceHub().signInitialTransaction(txB);
// Create session with counterparty
progressTracker.setCurrentStep(INITIATE_SESSION);
FlowSession otherPartySession = initiateFlow(carOwner);
//Finalizing the transaction
progressTracker.setCurrentStep(FINALIZE_FLOW);
subFlow(new FinalityFlow(signedTx,otherPartySession));
return "Registration Completed";
}
}
CarRegistrationFlowResponder
import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.flows.*;
@InitiatedBy(CarRegistrationFlowInitiator.class)
public class CarRegistrationFlowResponder extends FlowLogic<String> {
private FlowSession otherPartySession;
public CarRegistrationFlowResponder(FlowSession otherPartySession) {
this.otherPartySession = otherPartySession;
}
@Suspendable
@Override
public String call() throws FlowException {
// Responder flow logic goes here.
subFlow(new ReceiveFinalityFlow(otherPartySession));
return "Registration received!";
}
}
Both issuer and owner of car must sign the contract.
doesn't match with what you check (you only check that theissuer
signed:if (!(requiredSigners.contains(issuerKey)))
). 3- You should verify the transaction before signing it (i.e. addtxB.verify()
beforesignInitialTransaction(txB)
, what's the point of signing a transaction that might be invalid? Once you share more details about the error and do the fixes, I can probably identify what's happening. – Adel RustumrequireThat
/require
syntax instead ofif
statements? See example here. – Adel Rustum