CORDA-2665: Updated OwnableState relevancy check put back to V3 version (#4819)

* CORDA-2665: Updated OwnableState relevancy check is now gated on target version 4

https://github.com/corda/corda/pull/3789 changed the relevancy check of OwnableState to include the participants list in addition to the owner. This however breaks existing apps which assume (in their vault query) an OwnableState is recorded to the vault if-and-only-if the owner matches.

* CORDA-2665 Don't switch to new behaviour on target version upgrade.
This commit is contained in:
Shams Asari 2019-02-27 12:05:39 +00:00 committed by Rick Parker
parent 2ff7860e4b
commit f52d158f2d
2 changed files with 28 additions and 23 deletions

View File

@ -20,11 +20,7 @@ import net.corda.node.services.api.SchemaService
import net.corda.node.services.api.VaultServiceInternal import net.corda.node.services.api.VaultServiceInternal
import net.corda.node.services.schema.PersistentStateService import net.corda.node.services.schema.PersistentStateService
import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.*
import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit
import net.corda.nodeapi.internal.persistence.currentDBSession
import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction
import net.corda.nodeapi.internal.persistence.contextTransactionOrNull
import org.hibernate.Session import org.hibernate.Session
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
@ -71,9 +67,7 @@ class NodeVaultService(
*/ */
fun isRelevant(state: ContractState, myKeys: Set<PublicKey>): Boolean { fun isRelevant(state: ContractState, myKeys: Set<PublicKey>): Boolean {
val keysToCheck = when (state) { val keysToCheck = when (state) {
// Sometimes developers forget to add the owning key to participants for OwnableStates. is OwnableState -> listOf(state.owner.owningKey)
// TODO: This logic should probably be moved to OwnableState so we can just do a simple intersection here.
is OwnableState -> (state.participants.map { it.owningKey } + state.owner.owningKey).toSet()
else -> state.participants.map { it.owningKey } else -> state.participants.map { it.owningKey }
} }
return keysToCheck.any { it.containsAny(myKeys) } return keysToCheck.any { it.containsAny(myKeys) }

View File

@ -22,8 +22,8 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.toNonEmptySet
import net.corda.finance.* import net.corda.finance.*
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.schemas.CashSchemaV1
import net.corda.finance.contracts.utils.sumCash import net.corda.finance.contracts.utils.sumCash
import net.corda.finance.schemas.CashSchemaV1
import net.corda.finance.workflows.asset.CashUtils import net.corda.finance.workflows.asset.CashUtils
import net.corda.finance.workflows.getCashBalance import net.corda.finance.workflows.getCashBalance
import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.IdentityServiceInternal
@ -42,6 +42,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.* import org.junit.*
import rx.observers.TestSubscriber import rx.observers.TestSubscriber
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey
import java.util.* import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -98,8 +99,8 @@ class NodeVaultServiceTest {
vaultFiller = VaultFiller(services, dummyNotary) vaultFiller = VaultFiller(services, dummyNotary)
// This is safe because MockServices only ever have a single identity // This is safe because MockServices only ever have a single identity
identity = services.myInfo.singleIdentityAndCert() identity = services.myInfo.singleIdentityAndCert()
issuerServices = MockServices(cordappPackages, dummyCashIssuer, mock<IdentityService>(), parameters) issuerServices = MockServices(cordappPackages, dummyCashIssuer, mock(), parameters)
bocServices = MockServices(cordappPackages, bankOfCorda, mock<IdentityService>(), parameters) bocServices = MockServices(cordappPackages, bankOfCorda, mock(), parameters)
services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY) services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY)
services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY) services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY)
} }
@ -129,6 +130,7 @@ class NodeVaultServiceTest {
} }
class FungibleFoo(override val amount: Amount<Currency>, override val participants: List<AbstractParty>) : FungibleState<Currency> class FungibleFoo(override val amount: Amount<Currency>, override val participants: List<AbstractParty>) : FungibleState<Currency>
@Test @Test
fun `fungible state selection test`() { fun `fungible state selection test`() {
val issuerParty = services.myInfo.legalIdentities.first() val issuerParty = services.myInfo.legalIdentities.first()
@ -552,20 +554,29 @@ class NodeVaultServiceTest {
@Test @Test
fun `is ownable state relevant`() { fun `is ownable state relevant`() {
val amount = Amount(1000, Issued(BOC.ref(1), GBP)) val myAnonymousIdentity = services.keyManagementService.freshKeyAndCert(identity, false)
val wellKnownCash = Cash.State(amount, identity.party) val myKeys = services.keyManagementService.filterMyKeys(listOf(identity.owningKey, myAnonymousIdentity.owningKey)).toSet()
val myKeys = services.keyManagementService.filterMyKeys(listOf(wellKnownCash.owner.owningKey))
assertTrue { NodeVaultService.isRelevant(wellKnownCash, myKeys.toSet()) }
val anonymousIdentity = services.keyManagementService.freshKeyAndCert(identity, false) // Well-known owner
val anonymousCash = Cash.State(amount, anonymousIdentity.party) assertTrue { myKeys.isOwnableStateRelevant(identity.party, participants = emptyList()) }
val anonymousKeys = services.keyManagementService.filterMyKeys(listOf(anonymousCash.owner.owningKey)) // Anonymous owner
assertTrue { NodeVaultService.isRelevant(anonymousCash, anonymousKeys.toSet()) } assertTrue { myKeys.isOwnableStateRelevant(myAnonymousIdentity.party, participants = emptyList()) }
// Unknown owner
assertFalse { myKeys.isOwnableStateRelevant(createUnknownIdentity(), participants = emptyList()) }
// Under target version 3 only the owner is relevant. This is to preserve backwards compatibility
assertFalse { myKeys.isOwnableStateRelevant(createUnknownIdentity(), participants = listOf(identity.party)) }
}
val thirdPartyIdentity = AnonymousParty(generateKeyPair().public) private fun createUnknownIdentity() = AnonymousParty(generateKeyPair().public)
val thirdPartyCash = Cash.State(amount, thirdPartyIdentity)
val thirdPartyKeys = services.keyManagementService.filterMyKeys(listOf(thirdPartyCash.owner.owningKey)) private fun Set<PublicKey>.isOwnableStateRelevant(owner: AbstractParty, participants: List<AbstractParty>): Boolean {
assertFalse { NodeVaultService.isRelevant(thirdPartyCash, thirdPartyKeys.toSet()) } class TestOwnableState : OwnableState {
override val owner: AbstractParty get() = owner
override val participants: List<AbstractParty> get() = participants
override fun withNewOwner(newOwner: AbstractParty): CommandAndState = throw AbstractMethodError()
}
return NodeVaultService.isRelevant(TestOwnableState(), this)
} }
// TODO: Unit test linear state relevancy checks // TODO: Unit test linear state relevancy checks