问题
I am getting this error in one of the flows while using accounts in my cordapp, net.corda.core.CordaRuntimeException: java.lang.IllegalArgumentException: Could not find Party for Anonymous(DLBBYFJYhpPQXEMcTZtuzkg8ixptu92L3dqiN4boD1o2FN) This error comes at the Finality Flow line
Here is error log
java.lang.IllegalArgumentException: Could not find Party for Anonymous(DLKiQpwdw3E7CewLhUatjRpKvCMkHDhEoEYXfAUrp33z7)
at net.corda.core.identity.IdentityUtils.groupAbstractPartyByWellKnownParty(IdentityUtils.kt:47) ~[corda-core-4.3.jar:?]
at net.corda.core.identity.IdentityUtils.groupAbstractPartyByWellKnownParty(IdentityUtils.kt:63) ~[corda-core-4.3.jar:?]
at net.corda.core.flows.FinalityFlow.extractExternalParticipants(FinalityFlow.kt:224) ~[corda-core-4.3.jar:?]
at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:134) ~[corda-core-4.3.jar:?]
at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:39) ~[corda-core-4.3.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330) ~[corda-node-4.3.jar:?]
at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326) ~[corda-core-4.3.jar:?]
at com.template.flows.LoanTransferFlow.call(LoanTransferFlow.java:129) ~[?:?]
at com.template.flows.LoanTransferFlow.call(LoanTransferFlow.java:29) ~[?:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270) ~[corda-node-4.3.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46) ~[corda-node-4.3.jar:?]
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_212]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_212]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:1.8.0_212]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:1.8.0_212]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_212]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_212]
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63) ~[corda-node-4.3.jar:?]
the flow takes in a input state & 3 output state.
Here is the flow code:
@InitiatingFlow
@StartableByRPC
public class LoanTransferFlow extends FlowLogic<SignedTransaction> {
final AccountInfo borrower;
final AccountInfo lender;
UniqueIdentifier loanId = null;
StateAndRef<InfoState> inputBorrowerState = null;
private final static Logger logger = LoggerFactory.getLogger(LoanTransferFlow.class);
public LoanTransferFlow(AccountInfo borrower, AccountInfo lender, UniqueIdentifier loanId, StateAndRef<InfoState> inputBorrowerState) {
this.borrower = borrower;
this.lender = lender;
this.loanId = loanId;
this.inputBorrowerState = inputBorrowerState;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
List<StateAndRef<InfoState>> matchingLenderStates = null;
StateAndRef<InfoState> inputInfoLenderState = null;
StateAndRef<LoanState> inputLoanState = null;
//
// PublicKey myKey = subFlow(new RequestKeyForAccount(lender.getHost(), lender.getIdentifier().getId())).getOwningKey();
// PublicKey borrowerKey = subFlow(new RequestKeyForAccount(borrower.getHost(), borrower.getIdentifier().getId())).getOwningKey();
try {
Vault.Page<InfoState> lenderStateresults = (Vault.Page<InfoState>) subFlow(new CheckAccountInfoFlow(lender.getName()));
matchingLenderStates = lenderStateresults.getStates();
} catch (Exception e) {
throw new FlowException(e);
}
if ((matchingLenderStates == null) || (matchingLenderStates.isEmpty())) {
throw new FlowException("No Initial Borrower State Exists");
} else {
inputInfoLenderState = matchingLenderStates.get(matchingLenderStates.size() - 1);
}
QueryCriteria criteriaForLoanVault = new QueryCriteria.LinearStateQueryCriteria(
null,
ImmutableList.of(loanId),
Vault.StateStatus.UNCONSUMED,
null);
List<StateAndRef<LoanState>> matchingLoanStates = getServiceHub().getVaultService().queryBy(LoanState.class, criteriaForLoanVault).getStates();
if ((matchingLoanStates == null) || (matchingLoanStates.isEmpty())) {
throw new FlowException("No Initial Loan State Exists");
} else {
inputLoanState = matchingLoanStates.get(0);
}
subFlow(new ShareStateAndSyncAccounts(inputLoanState, borrower.getHost()));
subFlow(new ShareStateAndSyncAccounts(inputLoanState, lender.getHost()));
PublicKey myKey = inputLoanState.getState().getData().getLender();
PublicKey borrowerKey = inputLoanState.getState().getData().getBorrower();
logger.info("inputState" + inputLoanState.getState().getData().getBorrower() + inputLoanState.getState().getData().getLender());
logger.info("mykey" + myKey);
logger.info("borrowerKey" + borrowerKey);
float updatedBorrowerCreditscore = inputLoanState.getState().getData().getCreditScore() - 15;
float updatedLenderCreditscore = inputInfoLenderState.getState().getData().getCreditScore() + 30;
float amount = inputLoanState.getState().getData().getAmount();
float borrowerBalance = inputBorrowerState.getState().getData().getBalance();
float lenderBalance = inputInfoLenderState.getState().getData().getBalance();
if (amount > lenderBalance) {
throw new FlowException("Insufficient Lender Balance");
}
float updatedBorrowerBalance = borrowerBalance + amount;
float updatedLenderBalance = lenderBalance - amount;
final Command<LendingContract.Commands.Transfer> command = new Command<LendingContract.Commands.Transfer>(new LendingContract.Commands.Transfer(), Arrays.asList(myKey, borrowerKey));
LoanState outputLoanState = new LoanState(borrowerKey, myKey, amount, updatedBorrowerCreditscore, "approved", new UniqueIdentifier());
logger.info("outputState" + outputLoanState.getLender() + outputLoanState.getBorrower());
InfoState outputBorrowerState = new InfoState(new AnonymousParty(borrowerKey), updatedBorrowerCreditscore, updatedBorrowerBalance, new UniqueIdentifier());
InfoState outputLenderState = new InfoState(new AnonymousParty(myKey), updatedLenderCreditscore, updatedLenderBalance, new UniqueIdentifier());
TransactionBuilder txBuilder = new TransactionBuilder(notary);
txBuilder.addInputState(inputLoanState);
txBuilder.addCommand(command);
txBuilder.addOutputState(outputBorrowerState, LendingContract.ID);
txBuilder.addOutputState(outputLenderState, LendingContract.ID);
txBuilder.addOutputState(outputLoanState, LendingContract.ID);
txBuilder.verify(getServiceHub());
SignedTransaction signedInitialTransaction = getServiceHub().signInitialTransaction(txBuilder, Arrays.asList(getOurIdentity().getOwningKey(), myKey));
FlowSession counterPartySession = initiateFlow(borrower.getHost());
List<? extends TransactionSignature> accountToMoveToSignature = subFlow(new CollectSignatureFlow(signedInitialTransaction, counterPartySession, borrowerKey));
SignedTransaction fullySignedTx = signedInitialTransaction.withAdditionalSignature(accountToMoveToSignature.get(0));
return subFlow(new FinalityFlow(fullySignedTx));
Gone into the source code, error seems to be coming from this --->
@Throws(IllegalArgumentException::class)
fun groupAbstractPartyByWellKnownParty(serviceHub: ServiceHub, parties: Collection<AbstractParty>, ignoreUnrecognisedParties: Boolean): Map<Party, List<AbstractParty>> {
val partyToPublicKey: Iterable<Pair<Party, AbstractParty>> = parties.mapNotNull {
(serviceHub.identityService.wellKnownPartyFromAnonymous(it) ?: if (ignoreUnrecognisedParties) return@mapNotNull null else throw IllegalArgumentException("Could not find Party for $it")) to it
}
return partyToPublicKey.toMultiMap()
}
I have a similar flow without an input state that is working.
State Class:
private PublicKey borrower;
private PublicKey lender;
private float amount;
private float creditScore;
private String status;
private final UniqueIdentifier linearId;
FYI I have used PublicKey Data Type as well for borrower & lender in this state
FYI The flow works fine without using input states Account creation flow
@Suspendable
override fun call(): AccountInfo {
//Create a new account
try {
val existingAccount = accountService.accountInfo(name = acctName)
if (existingAccount.size >= 1) {
throw FlowException("Account Already Exists")
}
val newAccount = accountService.createAccount(name = acctName).toCompletableFuture().getOrThrow()
val acct = newAccount.state.data
return acct
} catch (e: Exception) {
throw FlowException(e)
}
}
Account sharing flow
@Suspendable
override fun call(): Boolean {
//Create a new account
val AllmyAccounts = accountService.ourAccounts()
val SharedAccount = AllmyAccounts.single { it.state.data.name == acctNameShared }.state.data.identifier.id
accountService.shareAccountInfoWithParty(SharedAccount,shareTo)
return true
}
FYI - There are two accounts both on different nodes
Adding the input state flow as well.
public LoanRequestFlow(AccountInfo borrower, AccountInfo lender, float amount) {
this.borrower = borrower;
this.amount = amount;
this.lender = lender;
}
private final ProgressTracker progressTracker = new ProgressTracker();
private final static Logger logger = LoggerFactory.getLogger(LoanRequestFlow.class);
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
List<StateAndRef<InfoState>> matchingBorrowerStates = null;
StateAndRef<InfoState> inputInfoBorrowerState = null;
PublicKey myKey = subFlow(new NewKeyForAccount(borrower.getIdentifier().getId())).getOwningKey();
PublicKey lenderKey = subFlow(new RequestKeyForAccount(lender)).getOwningKey();
try {
Vault.Page<InfoState> borrowerStateresults = (Vault.Page<InfoState>) subFlow(new CheckAccountInfoFlow(borrower.getName()));
matchingBorrowerStates = borrowerStateresults.getStates();
} catch (Exception e) {
throw new FlowException(e);
}
if ((matchingBorrowerStates == null) || (matchingBorrowerStates.isEmpty())) {
throw new FlowException("No Initial Borrower State Exists");
} else {
inputInfoBorrowerState = matchingBorrowerStates.get(matchingBorrowerStates.size() - 1);
}
final Command<LendingContract.Commands.Request> command = new Command<LendingContract.Commands.Request>(new LendingContract.Commands.Request(), Arrays.asList(myKey, lenderKey));
LoanState loanOutputState = new LoanState(myKey, lenderKey, amount, inputInfoBorrowerState.getState().getData().getCreditScore(), "pending", new UniqueIdentifier());
TransactionBuilder txBuilder = new TransactionBuilder(notary);
txBuilder.addCommand(command);
txBuilder.addOutputState(loanOutputState, LendingContract.ID);
txBuilder.verify(getServiceHub());
SignedTransaction signedInitialTransaction = getServiceHub().signInitialTransaction(txBuilder, Arrays.asList(getOurIdentity().getOwningKey(), myKey));
FlowSession counterPartySession = initiateFlow(lender.getHost());
List<? extends TransactionSignature> accountToMoveToSignature = subFlow(new CollectSignatureFlow(signedInitialTransaction, counterPartySession, lenderKey));
SignedTransaction fullySignedTx = signedInitialTransaction.withAdditionalSignature(accountToMoveToSignature.get(0));
return subFlow(new FinalityFlow(fullySignedTx));
回答1:
The reason you get this error is because the Finality flow is looking at your output states and input states and retrieving the participants from them.
It then tries to resolve those participants to parties via the PublicKeys.
If the node hasn't seen those Public Keys before the error you see will be thrown.
Since you have only fragments of code in the question, it isn't very clear where you are causing the issue.
However, the following example will replicate the issue:
public class SaveLoanFlow extends FlowLogic<SignedTransaction> {
private AnonymousParty borrower;
private AnonymousParty lender;
private float amount;
private float creditScore;
public SaveLoanFlow(AnonymousParty borrower, AnonymousParty lender, float amount, float creditScore) {
this.borrower = borrower;
this.lender = lender;
this.amount = amount;
this.creditScore = creditScore;
}
@Suspendable
@Override
public SignedTransaction call() throws FlowException {
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
List<PublicKey> signingPubKeys = Arrays.asList(getOurIdentity().getOwningKey());
TransactionBuilder txBuilder = new TransactionBuilder(notary);
LoanState loanState = new LoanState(borrower, lender, amount, creditScore, "NEW", new UniqueIdentifier());
txBuilder.addOutputState(loanState);
txBuilder.addCommand(new LendingContract.Commands.Transfer(), signingPubKeys);
SignedTransaction signedInitialTransaction = getServiceHub().signInitialTransaction(txBuilder, signingPubKeys);
return subFlow(new FinalityFlow(signedInitialTransaction));
}
}
public class LendingContract implements Contract {
public static final Party ID = null;
@Override
public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
}
/**
* This contract only implements one command, Transfer.
*/
public interface Commands extends CommandData {
class Transfer implements Commands {}
}
}
@BelongsToContract(LendingContract.class)
public class LoanState implements LinearState {
private AnonymousParty borrower;
private AnonymousParty lender;
private float amount;
private float creditScore;
private String status;
private UniqueIdentifier linearId;
public LoanState(AnonymousParty borrower, AnonymousParty lender, float amount, float creditScore, String status, UniqueIdentifier linearId) {
this.borrower = borrower;
this.lender = lender;
this.amount = amount;
this.creditScore = creditScore;
this.status = status;
this.linearId = linearId;
}
@NotNull
@Override
public List<AbstractParty> getParticipants() {
return ImmutableList.of(borrower, lender);
}
@NotNull
@Override
public UniqueIdentifier getLinearId() {
return linearId;
}
//
//other getters/setters
//
}
If you run this flow in a test (kotlin just because I was testing this in an existing project) you will get the error:
@Test
fun `Find party`() {
val borrowerAccountService = borrowerNode.services.cordaService(KeyManagementBackedAccountService::class.java)
val lenderAccountService = lenderNode.services.cordaService(KeyManagementBackedAccountService::class.java)
val borrowerAccount = borrowerAccountService.createAccount("Borrower-Account").getOrThrow().state.data
val lenderAccount = lenderAccountService.createAccount("Lender-Account").getOrThrow().state.data
network.runNetwork()
borrowerAccountService.shareAccountInfoWithParty(borrowerAccount.identifier.id, lenderNode.info.legalIdentities[0])
lenderAccountService.shareAccountInfoWithParty(lenderAccount.identifier.id, borrowerNode.info.legalIdentities[0])
network.runNetwork()
val borrowerPartyFuture = borrowerNode.startFlow(RequestKeyForAccount(borrowerAccount))
val lenderPartyFuture = lenderNode.startFlow(RequestKeyForAccount(lenderAccount))
network.runNetwork()
val borrowerParty = borrowerPartyFuture.getOrThrow()
val lenderParty = lenderPartyFuture.getOrThrow()
val newLoanFuture = lenderNode.startFlow(SaveLoanFlow(borrowerParty, lenderParty, 2F, 4F))
network.runNetwork()
val savedLoan = newLoanFuture.getOrThrow()
Assert.notEmpty(savedLoan.tx.outputStates)
}
}
However, if you change these 2 lines:
val borrowerPartyFuture = borrowerNode.startFlow(RequestKeyForAccount(borrowerAccount))
val lenderPartyFuture = lenderNode.startFlow(RequestKeyForAccount(lenderAccount))
So that the keys are requested from the same node the flow will run and the test will pass.
val borrowerPartyFuture = lenderNode.startFlow(RequestKeyForAccount(borrowerAccount))
val lenderPartyFuture = lenderNode.startFlow(RequestKeyForAccount(lenderAccount))
The reason is that now the lender is aware of the keys for both accounts.
So somewhere in the code that you haven't shown you have a similar issue in that the account keys are not available on both nodes.
It looks like you run another flow before the one you posted in the question because you do a vault lookup and use it as an input state.
One of the keys from the saved state probably don't exist on one of the nodes.
For (unforeseen?) design reasons, when you save the state in a flow the generated keys are not saved and you have to run an extra flow to make sure all the generated keys are synchronised.
You can do this as a subflow at the end of your not shown flow and it might fix the problem.
//If you don't do this you can't lookup accounts by PublicKey on the other host
subFlow(ShareStateAndSyncAccounts(state, lenderAccount.state.data.host))
Track this issue for further info.
来源:https://stackoverflow.com/questions/59303708/could-not-find-party-for-anonymousdlc2idbltxfabxk2ugfjzej8rbx6huqucjybdjatyjyaj