accounts icon indicating copy to clipboard operation
accounts copied to clipboard

PublicKeys Are Not Tied To AccountInfo

Open opticyclic opened this issue 5 years ago • 7 comments

My state defines the participants via PublicKey.

data class MyState(
    val id: String,
    val partyA: PublicKey,
    val partyB: PublicKey,
    val status: Status,
    override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState {

    override val participants: List<AbstractParty> get() = listOf(partyA, partyB).map { AnonymousParty(it) }

The contract checks that they aren't the same identity:

    val outputState = tx.outputsOfType<MyState>().single()
    "PartyA and PartyB cannot be the same identity." using (outputState.partyA != outputState.partyB)

I get the key during the flow with:

    val partyAKey = subFlow(RequestKeyForAccount(partyAAccountInfo.state.data)).owningKey
    val partyBKey = subFlow(RequestKeyForAccount(partyBAccountInfo.state.data)).owningKey

If I pass the same account info to the flow for PartyA and PartyB you would expect that partyAKey would be the same as partyBKey and the contract would pick up the issue.

However, what happens is that a fresh key is generated every single time meaning that different keys are created for the same account and the contract can't tell if they are the same.

The RequestKeyForAccount flow does:

    // The account is hosted on the initiating node. So we can generate a key and register it with the identity
    // service locally.
    return if (hostSession.counterparty == ourIdentity) {
        createKeyForAccount(accountInfo, serviceHub)

Which then goes to the confidential identity utils which generates the fresh key every single time:

@CordaInternal
fun createKeyForAccount(accountInfo: AccountInfo, serviceHub: ServiceHub) : AnonymousParty {
    val newKey = serviceHub.keyManagementService.freshKey(accountInfo.identifier.id)
    registerKeyToParty(newKey, serviceHub.ourIdentity, serviceHub)
    return AnonymousParty(newKey)
}

The key should be cached and tied to the account.

opticyclic avatar Sep 18 '19 02:09 opticyclic

This may be added in the future. But for now, this is left to the user to implement.

If you wish to do this, you should use a hibernate entity to store this data off ledger.

roastario avatar Sep 18 '19 06:09 roastario

This is by design for the time being. Cheers

roger-that-dev avatar Sep 18 '19 13:09 roger-that-dev

Can you explain a use case where the current design works and linking a single public key to an account does not work?

opticyclic avatar Sep 18 '19 15:09 opticyclic

Yes, imagine that account ABC has been deactivated by the hosting node, and you do not contact them, you run the risk of moving states to a key that is no longer valid.

Or the situation where a private key has been deleted or lost.

On Wed, 18 Sep 2019, 16:09 opticyclic, [email protected] wrote:

Can you explain a use case where the current design works and linking a single public key to an account does not work?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/corda/accounts/issues/50?email_source=notifications&email_token=AAVWIPRNXYO63CS4D4H6EKLQKJAEPA5CNFSM4IXYJSO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7AM4QY#issuecomment-532729411, or mute the thread https://github.com/notifications/unsubscribe-auth/AAVWIPVZAC5CUWGFCAQOIDLQKJAEPANCNFSM4IXYJSOQ .

roastario avatar Sep 18 '19 15:09 roastario

The code above says // The account is hosted on the initiating node. so you don't run that risk.

In the other logic branch in that function (when the account is on another node) don't you request the key anyway? Then in your flow don't you need to send the transaction to the other node that is hosting the account to sign?

So I can't see how you run the risk of moving states to a key that is invalid since the hosting node is always involved so will know if the key is invalid.

I don't really see how the private key be deleted or lost either. Isn't it stored in the database? Are you talking about database corruption? In that case, wouldn't you just mark that key as invalid and generate a new one?

Assuming you still disagree with my point, how do you propose checking that the accounts involved are not the same in the contract if we can't rely on comparing the public key?

opticyclic avatar Sep 18 '19 18:09 opticyclic

I believe the OP's use-case is actually a common requirement - i also landed here looking for a similar pattern, i.e. one with the ability to apply (in)equality checks on identities of ContractState.participants within Contract.verify.

I also realise this can only be an application-level concern for the time being and considering the extra plumbing required, but couldn't this repo document the steps or possible approach needed to attach the necessary information in a state and let the community find reusable implementation patterns for it?

manosbatsis avatar Mar 15 '20 11:03 manosbatsis

What is the pattern then if you want to make the state implement QueryableState and then query on one of the accounts?

If you only have the name of the account you will always get a different key back and it wont be queryable.

opticyclic avatar Apr 05 '21 05:04 opticyclic