【发布时间】:2020-06-24 23:38:27
【问题描述】:
我收到以下错误:
[ERROR] 14:37:15-0400 [Node thread-1] internal.Verifier。 - 验证交易 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_7b96, origin_7b96, -4771-8b40-36a30312fa91, session_timestamp=2020-06-23T18:37:12.984Z, thread-id=188}
当我尝试从 Corda 节点(丰田)终端运行时:
start CarRegistrationFlowInitiator carMake: Toyota , carModel: Rav4 , carYear: 2016 , carMileage: 31424.0 , carVin: asdfghjkloiu76543 , carOwner: "O=AutoSmart,L=New York,C=US"
我有两个派对 Toyota 和 AutoSmart。丰田是发行人,AutoSmart 是所有者。
汽车状态:
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);
}
}
汽车合同:
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- 请分享堆栈跟踪的更多输出以详细了解错误。 2-您的合同
Both issuer and owner of car must sign the contract.中的错误消息与您检查的内容不匹配(您只检查issuer签名:if (!(requiredSigners.contains(issuerKey))))。 3-您应该在签署之前验证交易(即在signInitialTransaction(txB)之前添加txB.verify(),签署可能无效的交易有什么意义?一旦您分享有关错误的更多详细信息并进行修复,我可能可以识别发生了什么事。 -
另外,为什么你不使用
requireThat/require语法而不是if语句?参见示例here。 -
并使用requireSingleCommand 来验证它是单个命令。