1
votes

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!";
    }
}
1
1- Please share more output from the stack trace to read about the error in detail. 2- Your error message in your contract Both issuer and owner of car must sign the contract. doesn't match with what you check (you only check that the issuer signed: if (!(requiredSigners.contains(issuerKey)))). 3- You should verify the transaction before signing it (i.e. add txB.verify() before signInitialTransaction(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 Rustum
Also, why you're not using the requireThat / require syntax instead of if statements? See example here.Adel Rustum
And use requireSingleCommand to verify that it's a single command.Adel Rustum

1 Answers

0
votes

I see a problem with this part of you code in the contract.

    // shape constraints
    if (!(tx.getInputStates().size()!=0)){
        throw new IllegalArgumentException("Registration contract should have no input states.");
    }

The input size should be zero. But then you are negating the condition. This means you would get an exception if the input size is actually zero.

And you have no inputs in your transaction, so this should throw a contract verification exception