mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Add a test for notary change transaction updates in the vault.
Fix a minor related issue with update type propagation. Add static NoNotaryUpdate object Added isEmpty check to Vault.Update
This commit is contained in:
@ -31,10 +31,7 @@ import net.corda.core.serialization.SerializationDefaults.STORAGE_CONTEXT
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.CoreTransaction
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.transactions.*
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.services.database.RequeryConfiguration
|
||||
import net.corda.node.services.statemachine.FlowStateMachineImpl
|
||||
@ -86,7 +83,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
private val mutex = ThreadBox(InnerState())
|
||||
|
||||
private fun recordUpdate(update: Vault.Update<ContractState>): Vault.Update<ContractState> {
|
||||
if (update != Vault.NoUpdate) {
|
||||
if (!update.isEmpty()) {
|
||||
val producedStateRefs = update.produced.map { it.ref }
|
||||
val producedStateRefsMap = update.produced.associateBy { it.ref }
|
||||
val consumedStateRefs = update.consumed.map { it.ref }
|
||||
@ -248,7 +245,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
fun makeUpdate(tx: NotaryChangeWireTransaction): Vault.Update<ContractState> {
|
||||
// We need to resolve the full transaction here because outputs are calculated from inputs
|
||||
// We also can't do filtering beforehand, since output encumbrance pointers get recalculated based on
|
||||
// input position
|
||||
// input positions
|
||||
val ltx = tx.resolve(services, emptyList())
|
||||
|
||||
val (consumedStateAndRefs, producedStates) = ltx.inputs.
|
||||
@ -263,13 +260,13 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
|
||||
if (consumedStateAndRefs.isEmpty() && producedStateAndRefs.isEmpty()) {
|
||||
log.trace { "tx ${tx.id} was irrelevant to this vault, ignoring" }
|
||||
return Vault.NoUpdate
|
||||
return Vault.NoNotaryUpdate
|
||||
}
|
||||
|
||||
return Vault.Update(consumedStateAndRefs.toHashSet(), producedStateAndRefs.toHashSet())
|
||||
return Vault.Update(consumedStateAndRefs.toHashSet(), producedStateAndRefs.toHashSet(), null, Vault.UpdateType.NOTARY_CHANGE)
|
||||
}
|
||||
|
||||
val netDelta = txns.fold(Vault.NoUpdate) { netDelta, txn -> netDelta + makeUpdate(txn) }
|
||||
val netDelta = txns.fold(Vault.NoNotaryUpdate) { netDelta, txn -> netDelta + makeUpdate(txn) }
|
||||
processAndNotify(netDelta)
|
||||
}
|
||||
|
||||
@ -292,7 +289,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
}
|
||||
|
||||
private fun processAndNotify(update: Vault.Update<ContractState>) {
|
||||
if (update != Vault.NoUpdate) {
|
||||
if (!update.isEmpty()) {
|
||||
recordUpdate(update)
|
||||
mutex.locked {
|
||||
// flowId required by SoftLockManager to perform auto-registration of soft locks for new states
|
||||
|
@ -5,8 +5,10 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||
import net.corda.contracts.getCashBalance
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.crypto.sign
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.NonEmptySet
|
||||
@ -447,7 +449,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
// TODO: Unit test linear state relevancy checks
|
||||
|
||||
@Test
|
||||
fun `make update`() {
|
||||
fun `correct updates are generated for general transactions`() {
|
||||
val service = (services.vaultService as NodeVaultService)
|
||||
val vaultSubscriber = TestSubscriber<Vault.Update<*>>().apply {
|
||||
service.updates.subscribe(this)
|
||||
@ -478,4 +480,56 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
val observedUpdates = vaultSubscriber.onNextEvents
|
||||
assertEquals(observedUpdates, listOf(expectedIssueUpdate, expectedMoveUpdate))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `correct updates are generated when changing notaries`() {
|
||||
val service = (services.vaultService as NodeVaultService)
|
||||
val notary = services.myInfo.legalIdentity
|
||||
|
||||
val vaultSubscriber = TestSubscriber<Vault.Update<*>>().apply {
|
||||
service.updates.subscribe(this)
|
||||
}
|
||||
|
||||
val anonymousIdentity = services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentityAndCert, false)
|
||||
val thirdPartyIdentity = AnonymousParty(generateKeyPair().public)
|
||||
val amount = Amount(1000, Issued(BOC.ref(1), GBP))
|
||||
|
||||
// Issue some cash
|
||||
val issueTx = TransactionBuilder(notary).apply {
|
||||
Cash().generateIssue(this, amount, anonymousIdentity.party, notary)
|
||||
}.toWireTransaction()
|
||||
|
||||
// We need to record the issue transaction so inputs can be resolved for the notary change transaction
|
||||
val signedIssueTx = SignedTransaction(issueTx, listOf(BOC_KEY.sign(issueTx.id)))
|
||||
services.validatedTransactions.addTransaction(signedIssueTx)
|
||||
|
||||
val initialCashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0))
|
||||
|
||||
// Change notary
|
||||
val newNotary = DUMMY_NOTARY
|
||||
val changeNotaryTx = NotaryChangeWireTransaction(listOf(initialCashState.ref), issueTx.notary!!, newNotary)
|
||||
val cashStateWithNewNotary = StateAndRef(initialCashState.state.copy(notary = newNotary), StateRef(changeNotaryTx.id, 0))
|
||||
|
||||
database.transaction {
|
||||
service.notifyAll(listOf(issueTx, changeNotaryTx))
|
||||
}
|
||||
|
||||
// Move cash
|
||||
val moveTx = database.transaction {
|
||||
TransactionBuilder(newNotary).apply {
|
||||
service.generateSpend(this, Amount(1000, GBP), thirdPartyIdentity)
|
||||
}.toWireTransaction()
|
||||
}
|
||||
|
||||
database.transaction {
|
||||
service.notify(moveTx)
|
||||
}
|
||||
|
||||
val expectedIssueUpdate = Vault.Update(emptySet(), setOf(initialCashState), null)
|
||||
val expectedNotaryChangeUpdate = Vault.Update(setOf(initialCashState), setOf(cashStateWithNewNotary), null, Vault.UpdateType.NOTARY_CHANGE)
|
||||
val expectedMoveUpdate = Vault.Update(setOf(cashStateWithNewNotary), emptySet(), null)
|
||||
|
||||
val observedUpdates = vaultSubscriber.onNextEvents
|
||||
assertEquals(observedUpdates, listOf(expectedIssueUpdate, expectedNotaryChangeUpdate, expectedMoveUpdate))
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user