mirror of
https://github.com/corda/corda.git
synced 2024-12-28 08:48:57 +00:00
Merge remote-tracking branch 'open/master' into colljos-merge-171117
This commit is contained in:
commit
ea1cd0035a
@ -13,6 +13,9 @@ UNRELEASED
|
|||||||
|
|
||||||
* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows.
|
* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows.
|
||||||
|
|
||||||
|
* ``wellKnownPartyFromAnonymous()`` now always resolve the key to a ``Party``, then the party to the well known party.
|
||||||
|
Previously if it was passed a ``Party`` it would use its name as-is without verifying the key matched that name.
|
||||||
|
|
||||||
* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``.
|
* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``.
|
||||||
This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable.
|
This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable.
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import java.sql.ResultSet
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CashSelectionH2Impl : AbstractCashSelection() {
|
class CashSelectionH2Impl : AbstractCashSelection() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val JDBC_DRIVER_NAME = "H2 JDBC Driver"
|
const val JDBC_DRIVER_NAME = "H2 JDBC Driver"
|
||||||
val log = loggerFor<CashSelectionH2Impl>()
|
val log = loggerFor<CashSelectionH2Impl>()
|
||||||
@ -25,7 +24,6 @@ class CashSelectionH2Impl : AbstractCashSelection() {
|
|||||||
|
|
||||||
override fun toString() = "${this::class.java} for $JDBC_DRIVER_NAME"
|
override fun toString() = "${this::class.java} for $JDBC_DRIVER_NAME"
|
||||||
|
|
||||||
|
|
||||||
// We are using an H2 specific means of selecting a minimum set of rows that match a request amount of coins:
|
// We are using an H2 specific means of selecting a minimum set of rows that match a request amount of coins:
|
||||||
// 1) There is no standard SQL mechanism of calculating a cumulative total on a field and restricting row selection on the
|
// 1) There is no standard SQL mechanism of calculating a cumulative total on a field and restricting row selection on the
|
||||||
// running total of such an accumulator
|
// running total of such an accumulator
|
||||||
@ -34,7 +32,7 @@ class CashSelectionH2Impl : AbstractCashSelection() {
|
|||||||
// 3) H2 does not support JOIN's in FOR UPDATE (hence we are forced to execute 2 queries)
|
// 3) H2 does not support JOIN's in FOR UPDATE (hence we are forced to execute 2 queries)
|
||||||
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
||||||
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet {
|
onlyFromIssuerParties: Set<AbstractParty>, withIssuerRefs: Set<OpaqueBytes>) : ResultSet {
|
||||||
connection.createStatement().execute("CALL SET(@t, 0);")
|
connection.createStatement().execute("CALL SET(@t, CAST(0 AS BIGINT));")
|
||||||
|
|
||||||
val selectJoin = """
|
val selectJoin = """
|
||||||
SELECT vs.transaction_id, vs.output_index, ccs.pennies, SET(@t, ifnull(@t,0)+ccs.pennies) total_pennies, vs.lock_id
|
SELECT vs.transaction_id, vs.output_index, ccs.pennies, SET(@t, ifnull(@t,0)+ccs.pennies) total_pennies, vs.lock_id
|
||||||
|
@ -54,8 +54,7 @@ class CashTests {
|
|||||||
lateinit var database: CordaPersistence
|
lateinit var database: CordaPersistence
|
||||||
private lateinit var vaultStatesUnconsumed: List<StateAndRef<Cash.State>>
|
private lateinit var vaultStatesUnconsumed: List<StateAndRef<Cash.State>>
|
||||||
|
|
||||||
private lateinit var OUR_IDENTITY_1: AbstractParty
|
private lateinit var ourIdentity: AbstractParty
|
||||||
private lateinit var OUR_IDENTITY_AND_CERT: PartyAndCertificate
|
|
||||||
private lateinit var miniCorpAnonymised: AnonymousParty
|
private lateinit var miniCorpAnonymised: AnonymousParty
|
||||||
private val CHARLIE_ANONYMISED = CHARLIE_IDENTITY.party.anonymise()
|
private val CHARLIE_ANONYMISED = CHARLIE_IDENTITY.party.anonymise()
|
||||||
|
|
||||||
@ -65,28 +64,33 @@ class CashTests {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
LogHelper.setLevel(NodeVaultService::class)
|
LogHelper.setLevel(NodeVaultService::class)
|
||||||
megaCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), MEGA_CORP.name, MEGA_CORP_KEY)
|
megaCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), MEGA_CORP.name, MEGA_CORP_KEY)
|
||||||
|
miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), MINI_CORP.name, MINI_CORP_KEY)
|
||||||
|
val notaryServices = MockServices(listOf("net.corda.finance.contracts.asset"), DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val databaseAndServices = makeTestDatabaseAndMockServices(
|
val databaseAndServices = makeTestDatabaseAndMockServices(
|
||||||
cordappPackages = listOf("net.corda.finance.contracts.asset"),
|
cordappPackages = listOf("net.corda.finance.contracts.asset"),
|
||||||
initialIdentityName = CordaX500Name(organisation = "Me", locality = "London", country = "GB"),
|
initialIdentityName = CordaX500Name(organisation = "Me", locality = "London", country = "GB"),
|
||||||
keys = listOf(generateKeyPair()))
|
keys = listOf(generateKeyPair()))
|
||||||
database = databaseAndServices.first
|
database = databaseAndServices.first
|
||||||
miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), MINI_CORP.name, MINI_CORP_KEY)
|
|
||||||
ourServices = databaseAndServices.second
|
ourServices = databaseAndServices.second
|
||||||
OUR_IDENTITY_AND_CERT = ourServices.myInfo.singleIdentityAndCert()
|
|
||||||
OUR_IDENTITY_1 = ourServices.myInfo.singleIdentity()
|
// Set up and register identities
|
||||||
|
ourIdentity = ourServices.myInfo.singleIdentity()
|
||||||
|
miniCorpAnonymised = miniCorpServices.myInfo.singleIdentityAndCert().party.anonymise()
|
||||||
|
(miniCorpServices.myInfo.legalIdentitiesAndCerts + megaCorpServices.myInfo.legalIdentitiesAndCerts + notaryServices.myInfo.legalIdentitiesAndCerts).forEach { identity ->
|
||||||
|
ourServices.identityService.verifyAndRegisterIdentity(identity)
|
||||||
|
}
|
||||||
|
|
||||||
// Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved.
|
// Create some cash. Any attempt to spend >$500 will require multiple issuers to be involved.
|
||||||
database.transaction {
|
database.transaction {
|
||||||
ourServices.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
ourServices.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
ownedBy = OUR_IDENTITY_1, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices)
|
owner = ourIdentity, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices)
|
||||||
ourServices.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
ourServices.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
ownedBy = OUR_IDENTITY_1, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices)
|
owner = ourIdentity, issuedBy = MEGA_CORP.ref(1), issuerServices = megaCorpServices)
|
||||||
ourServices.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
ourServices.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
ownedBy = OUR_IDENTITY_1, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices)
|
owner = ourIdentity, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices)
|
||||||
ourServices.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
ourServices.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
ownedBy = OUR_IDENTITY_1, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices)
|
owner = ourIdentity, issuedBy = MINI_CORP.ref(1), issuerServices = miniCorpServices)
|
||||||
}
|
}
|
||||||
miniCorpAnonymised = miniCorpServices.myInfo.singleIdentityAndCert().party.anonymise()
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
vaultStatesUnconsumed = ourServices.vaultService.queryBy<Cash.State>().states
|
vaultStatesUnconsumed = ourServices.vaultService.queryBy<Cash.State>().states
|
||||||
}
|
}
|
||||||
@ -495,7 +499,7 @@ class CashTests {
|
|||||||
|
|
||||||
private fun makeCash(amount: Amount<Currency>, issuer: AbstractParty, depositRef: Byte = 1) =
|
private fun makeCash(amount: Amount<Currency>, issuer: AbstractParty, depositRef: Byte = 1) =
|
||||||
StateAndRef(
|
StateAndRef(
|
||||||
TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), OUR_IDENTITY_1), Cash.PROGRAM_ID, DUMMY_NOTARY),
|
TransactionState(Cash.State(amount `issued by` issuer.ref(depositRef), ourIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY),
|
||||||
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -509,12 +513,14 @@ class CashTests {
|
|||||||
return tx.toWireTransaction(serviceHub)
|
return tx.toWireTransaction(serviceHub)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeSpend(amount: Amount<Currency>, dest: AbstractParty): WireTransaction {
|
private fun makeSpend(services: ServiceHub, amount: Amount<Currency>, dest: AbstractParty): WireTransaction {
|
||||||
|
val ourIdentity = services.myInfo.singleIdentityAndCert()
|
||||||
|
val changeIdentity = services.keyManagementService.freshKeyAndCert(ourIdentity, false)
|
||||||
val tx = TransactionBuilder(DUMMY_NOTARY)
|
val tx = TransactionBuilder(DUMMY_NOTARY)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
Cash.generateSpend(ourServices, tx, amount, OUR_IDENTITY_AND_CERT, dest)
|
Cash.generateSpend(services, tx, amount, changeIdentity, dest)
|
||||||
}
|
}
|
||||||
return tx.toWireTransaction(miniCorpServices)
|
return tx.toWireTransaction(services)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -588,7 +594,7 @@ class CashTests {
|
|||||||
fun generateExitWithEmptyVault() {
|
fun generateExitWithEmptyVault() {
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
val tx = TransactionBuilder(DUMMY_NOTARY)
|
val tx = TransactionBuilder(DUMMY_NOTARY)
|
||||||
Cash().generateExit(tx, Amount(100, Issued(CHARLIE.ref(1), GBP)), emptyList(), OUR_IDENTITY_1)
|
Cash().generateExit(tx, Amount(100, Issued(CHARLIE.ref(1), GBP)), emptyList(), ourIdentity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,22 +602,23 @@ class CashTests {
|
|||||||
fun generateSimpleDirectSpend() {
|
fun generateSimpleDirectSpend() {
|
||||||
val wtx =
|
val wtx =
|
||||||
database.transaction {
|
database.transaction {
|
||||||
makeSpend(100.DOLLARS, miniCorpAnonymised)
|
makeSpend(ourServices, 100.DOLLARS, miniCorpAnonymised)
|
||||||
}
|
}
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||||
assertEquals(vaultState.ref, wtx.inputs[0])
|
assertEquals(vaultState.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState.state.data.copy(owner = miniCorpAnonymised), wtx.getOutput(0))
|
assertEquals(vaultState.state.data.copy(owner = miniCorpAnonymised), wtx.getOutput(0))
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(ourIdentity.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun generateSimpleSpendWithParties() {
|
fun generateSimpleSpendWithParties() {
|
||||||
|
val changeIdentity = ourServices.keyManagementService.freshKeyAndCert(ourServices.myInfo.singleIdentityAndCert(), false)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val tx = TransactionBuilder(DUMMY_NOTARY)
|
val tx = TransactionBuilder(DUMMY_NOTARY)
|
||||||
Cash.generateSpend(ourServices, tx, 80.DOLLARS, OUR_IDENTITY_AND_CERT, ALICE, setOf(MINI_CORP))
|
Cash.generateSpend(ourServices, tx, 80.DOLLARS, changeIdentity, ALICE, setOf(MINI_CORP))
|
||||||
|
|
||||||
assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0])
|
assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0])
|
||||||
}
|
}
|
||||||
@ -621,7 +628,7 @@ class CashTests {
|
|||||||
fun generateSimpleSpendWithChange() {
|
fun generateSimpleSpendWithChange() {
|
||||||
val wtx =
|
val wtx =
|
||||||
database.transaction {
|
database.transaction {
|
||||||
makeSpend(10.DOLLARS, miniCorpAnonymised)
|
makeSpend(ourServices, 10.DOLLARS, miniCorpAnonymised)
|
||||||
}
|
}
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||||
@ -638,7 +645,7 @@ class CashTests {
|
|||||||
assertEquals(vaultState.ref, wtx.inputs[0])
|
assertEquals(vaultState.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState.state.data.copy(owner = miniCorpAnonymised, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
assertEquals(vaultState.state.data.copy(owner = miniCorpAnonymised, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
||||||
assertEquals(vaultState.state.data.copy(amount = changeAmount, owner = changeOwner), wtx.outputs[1].data)
|
assertEquals(vaultState.state.data.copy(amount = changeAmount, owner = changeOwner), wtx.outputs[1].data)
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(ourIdentity.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,7 +653,7 @@ class CashTests {
|
|||||||
fun generateSpendWithTwoInputs() {
|
fun generateSpendWithTwoInputs() {
|
||||||
val wtx =
|
val wtx =
|
||||||
database.transaction {
|
database.transaction {
|
||||||
makeSpend(500.DOLLARS, miniCorpAnonymised)
|
makeSpend(ourServices, 500.DOLLARS, miniCorpAnonymised)
|
||||||
}
|
}
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val vaultState0 = vaultStatesUnconsumed.elementAt(0)
|
val vaultState0 = vaultStatesUnconsumed.elementAt(0)
|
||||||
@ -654,7 +661,7 @@ class CashTests {
|
|||||||
assertEquals(vaultState0.ref, wtx.inputs[0])
|
assertEquals(vaultState0.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||||
assertEquals(vaultState0.state.data.copy(owner = miniCorpAnonymised, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.getOutput(0))
|
assertEquals(vaultState0.state.data.copy(owner = miniCorpAnonymised, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.getOutput(0))
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(ourIdentity.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,7 +669,7 @@ class CashTests {
|
|||||||
fun generateSpendMixedDeposits() {
|
fun generateSpendMixedDeposits() {
|
||||||
val wtx =
|
val wtx =
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val wtx = makeSpend(580.DOLLARS, miniCorpAnonymised)
|
val wtx = makeSpend(ourServices, 580.DOLLARS, miniCorpAnonymised)
|
||||||
assertEquals(3, wtx.inputs.size)
|
assertEquals(3, wtx.inputs.size)
|
||||||
wtx
|
wtx
|
||||||
}
|
}
|
||||||
@ -675,7 +682,7 @@ class CashTests {
|
|||||||
assertEquals(vaultState2.ref, wtx.inputs[2])
|
assertEquals(vaultState2.ref, wtx.inputs[2])
|
||||||
assertEquals(vaultState0.state.data.copy(owner = miniCorpAnonymised, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
assertEquals(vaultState0.state.data.copy(owner = miniCorpAnonymised, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
||||||
assertEquals(vaultState2.state.data.copy(owner = miniCorpAnonymised), wtx.outputs[0].data)
|
assertEquals(vaultState2.state.data.copy(owner = miniCorpAnonymised), wtx.outputs[0].data)
|
||||||
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(ourIdentity.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,12 +691,12 @@ class CashTests {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val e: InsufficientBalanceException = assertFailsWith("balance") {
|
val e: InsufficientBalanceException = assertFailsWith("balance") {
|
||||||
makeSpend(1000.DOLLARS, miniCorpAnonymised)
|
makeSpend(ourServices, 1000.DOLLARS, miniCorpAnonymised)
|
||||||
}
|
}
|
||||||
assertEquals((1000 - 580).DOLLARS, e.amountMissing)
|
assertEquals((1000 - 580).DOLLARS, e.amountMissing)
|
||||||
|
|
||||||
assertFailsWith(InsufficientBalanceException::class) {
|
assertFailsWith(InsufficientBalanceException::class) {
|
||||||
makeSpend(81.SWISS_FRANCS, miniCorpAnonymised)
|
makeSpend(ourServices, 81.SWISS_FRANCS, miniCorpAnonymised)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -821,7 +828,7 @@ class CashTests {
|
|||||||
fun multiSpend() {
|
fun multiSpend() {
|
||||||
val tx = TransactionBuilder(DUMMY_NOTARY)
|
val tx = TransactionBuilder(DUMMY_NOTARY)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val changeIdentity = ourServices.keyManagementService.freshKeyAndCert(OUR_IDENTITY_AND_CERT, false)
|
val changeIdentity = ourServices.keyManagementService.freshKeyAndCert(ourServices.myInfo.singleIdentityAndCert(), false)
|
||||||
val payments = listOf(
|
val payments = listOf(
|
||||||
PartyAndAmount(miniCorpAnonymised, 400.DOLLARS),
|
PartyAndAmount(miniCorpAnonymised, 400.DOLLARS),
|
||||||
PartyAndAmount(CHARLIE_ANONYMISED, 150.DOLLARS)
|
PartyAndAmount(CHARLIE_ANONYMISED, 150.DOLLARS)
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package net.corda.finance.contracts.asset
|
package net.corda.finance.contracts.asset.cash.selection
|
||||||
|
|
||||||
|
import net.corda.core.internal.concurrent.transpose
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
|
import net.corda.finance.POUNDS
|
||||||
import net.corda.finance.flows.CashException
|
import net.corda.finance.flows.CashException
|
||||||
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.finance.flows.CashPaymentFlow
|
import net.corda.finance.flows.CashPaymentFlow
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import net.corda.testing.node.MockNodeParameters
|
import net.corda.testing.node.MockNodeParameters
|
||||||
@ -10,8 +14,9 @@ import net.corda.testing.startFlow
|
|||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.util.Collections.nCopies
|
||||||
|
|
||||||
class CashSelectionH2Test {
|
class CashSelectionH2ImplTest {
|
||||||
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
|
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -19,6 +24,19 @@ class CashSelectionH2Test {
|
|||||||
mockNet.stopNodes()
|
mockNet.stopNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `selecting pennies amount larger than max int, which is split across multiple cash states`() {
|
||||||
|
val node = mockNet.createNode()
|
||||||
|
// The amount has to split across at least two states, probably to trigger the H2 accumulator variable during the
|
||||||
|
// spend operation below.
|
||||||
|
// Issuing Integer.MAX_VALUE will not cause an exception since PersistentCashState.pennies is a long
|
||||||
|
nCopies(2, Integer.MAX_VALUE).map { issueAmount ->
|
||||||
|
node.services.startFlow(CashIssueFlow(issueAmount.POUNDS, OpaqueBytes.of(1), mockNet.defaultNotaryIdentity)).resultFuture
|
||||||
|
}.transpose().getOrThrow()
|
||||||
|
// The spend must be more than the size of a single cash state to force the accumulator onto the second state.
|
||||||
|
node.services.startFlow(CashPaymentFlow((Integer.MAX_VALUE + 1L).POUNDS, node.info.legalIdentities[0])).resultFuture.getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `check does not hold connection over retries`() {
|
fun `check does not hold connection over retries`() {
|
||||||
val bankA = mockNet.createNode(MockNodeParameters(configOverrides = {
|
val bankA = mockNet.createNode(MockNodeParameters(configOverrides = {
|
@ -41,26 +41,30 @@ class NodeStatePersistenceTests : IntegrationTest() {
|
|||||||
|
|
||||||
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
||||||
val message = Message("Hello world!")
|
val message = Message("Hello world!")
|
||||||
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
val stateAndRef: StateAndRef<MessageState>? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||||
val nodeName = {
|
val nodeName = {
|
||||||
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||||
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name
|
||||||
|
// Ensure the notary node has finished starting up, before starting a flow that needs a notary
|
||||||
|
defaultNotaryNode.getOrThrow()
|
||||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
nodeHandle.stop()
|
nodeHandle.stop()
|
||||||
nodeName
|
nodeName
|
||||||
}()
|
}()
|
||||||
|
|
||||||
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
val nodeHandle = startNode(providedName = nodeName, rpcUsers = listOf(user)).getOrThrow()
|
||||||
nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
val result = nodeHandle.rpcClientToNode().start(user.username, user.password).use {
|
||||||
val page = it.proxy.vaultQuery(MessageState::class.java)
|
val page = it.proxy.vaultQuery(MessageState::class.java)
|
||||||
val stateAndRef = page.states.singleOrNull()
|
page.states.singleOrNull()
|
||||||
assertNotNull(stateAndRef)
|
|
||||||
val retrievedMessage = stateAndRef!!.state.data.message
|
|
||||||
assertEquals(message, retrievedMessage)
|
|
||||||
}
|
}
|
||||||
|
nodeHandle.stop()
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
assertNotNull(stateAndRef)
|
||||||
|
val retrievedMessage = stateAndRef!!.state.data.message
|
||||||
|
assertEquals(message, retrievedMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +131,7 @@ open class MessageContract : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class SendMessageFlow(private val message: Message) : FlowLogic<SignedTransaction>() {
|
class SendMessageFlow(private val message: Message, private val notary: Party) : FlowLogic<SignedTransaction>() {
|
||||||
companion object {
|
companion object {
|
||||||
object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction based on the message.")
|
object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction based on the message.")
|
||||||
object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying contract constraints.")
|
object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying contract constraints.")
|
||||||
@ -143,8 +147,6 @@ class SendMessageFlow(private val message: Message) : FlowLogic<SignedTransactio
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): SignedTransaction {
|
override fun call(): SignedTransaction {
|
||||||
val notary = serviceHub.networkMapCache.notaryIdentities.first()
|
|
||||||
|
|
||||||
progressTracker.currentStep = GENERATING_TRANSACTION
|
progressTracker.currentStep = GENERATING_TRANSACTION
|
||||||
|
|
||||||
val messageState = MessageState(message = message, by = ourIdentity)
|
val messageState = MessageState(message = message, by = ourIdentity)
|
||||||
|
@ -99,14 +99,14 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
|||||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
||||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party
|
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party
|
||||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||||
// Expand the anonymous party to a full party (i.e. has a name) if possible
|
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||||
val candidate = party as? Party ?: keyToParties[party.owningKey]?.party
|
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
||||||
|
// into a party, and from there figure out the well known party.
|
||||||
|
val candidate = partyFromKey(party.owningKey)
|
||||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||||
// Look up the well known identity for that name
|
|
||||||
return if (candidate != null) {
|
return if (candidate != null) {
|
||||||
// If we have a well known identity by that name, use it in preference to the candidate. Otherwise default
|
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party ${candidate} does not match expected ${party}" }
|
||||||
// back to the candidate.
|
wellKnownPartyFromX500Name(candidate.name)
|
||||||
principalToParties[candidate.name]?.party ?: candidate
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -164,15 +164,13 @@ class PersistentIdentityService(identities: Iterable<PartyAndCertificate> = empt
|
|||||||
override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
||||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
|
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
|
||||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||||
// Expand the anonymous party to a full party (i.e. has a name) if possible
|
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||||
val candidate = party as? Party ?: partyFromKey(party.owningKey)
|
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
||||||
|
// into a party, and from there figure out the well known party.
|
||||||
|
val candidate = partyFromKey(party.owningKey)
|
||||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||||
// Look up the well known identity for that name
|
|
||||||
return if (candidate != null) {
|
return if (candidate != null) {
|
||||||
// If we have a well known identity by that name, use it in preference to the candidate. Otherwise default
|
wellKnownPartyFromX500Name(candidate.name)
|
||||||
// back to the candidate.
|
|
||||||
val res = wellKnownPartyFromX500Name(candidate.name) ?: candidate
|
|
||||||
res
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityServic
|
|||||||
if (party != null) {
|
if (party != null) {
|
||||||
val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString()
|
val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString()
|
||||||
if (partyName != null) return partyName
|
if (partyName != null) return partyName
|
||||||
log.warn("Identity service unable to resolve AbstractParty: $party")
|
log.warn("Identity service unable to resolve AbstractParty: $party")
|
||||||
}
|
}
|
||||||
return null // non resolvable anonymous parties
|
return null // non resolvable anonymous parties
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,57 @@
|
|||||||
package net.corda.node.services.vault;
|
package net.corda.node.services.vault;
|
||||||
|
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import kotlin.*;
|
import kotlin.Pair;
|
||||||
|
import kotlin.Triple;
|
||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.*;
|
||||||
import net.corda.core.identity.*;
|
import net.corda.core.identity.AbstractParty;
|
||||||
import net.corda.core.messaging.*;
|
import net.corda.core.messaging.DataFeed;
|
||||||
import net.corda.core.node.services.*;
|
import net.corda.core.node.services.IdentityService;
|
||||||
|
import net.corda.core.node.services.Vault;
|
||||||
|
import net.corda.core.node.services.VaultQueryException;
|
||||||
|
import net.corda.core.node.services.VaultService;
|
||||||
import net.corda.core.node.services.vault.*;
|
import net.corda.core.node.services.vault.*;
|
||||||
import net.corda.core.node.services.vault.QueryCriteria.*;
|
import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria;
|
||||||
import net.corda.core.utilities.*;
|
import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria;
|
||||||
import net.corda.finance.contracts.*;
|
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria;
|
||||||
import net.corda.finance.contracts.asset.*;
|
import net.corda.core.utilities.EncodingUtils;
|
||||||
import net.corda.finance.schemas.*;
|
import net.corda.core.utilities.OpaqueBytes;
|
||||||
import net.corda.node.utilities.*;
|
import net.corda.finance.contracts.DealState;
|
||||||
import net.corda.testing.*;
|
import net.corda.finance.contracts.asset.Cash;
|
||||||
import net.corda.testing.contracts.*;
|
import net.corda.finance.contracts.asset.CashUtilities;
|
||||||
import net.corda.testing.node.*;
|
import net.corda.finance.schemas.CashSchemaV1;
|
||||||
import org.junit.*;
|
import net.corda.node.utilities.CordaPersistence;
|
||||||
|
import net.corda.node.utilities.DatabaseTransaction;
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule;
|
||||||
|
import net.corda.testing.TestConstants;
|
||||||
|
import net.corda.testing.contracts.DummyLinearContract;
|
||||||
|
import net.corda.testing.contracts.VaultFiller;
|
||||||
|
import net.corda.testing.node.MockServices;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.Field;
|
||||||
import java.security.*;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.*;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import static net.corda.core.node.services.vault.QueryCriteriaUtils.*;
|
import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM;
|
||||||
import static net.corda.core.utilities.ByteArrays.*;
|
import static net.corda.core.node.services.vault.QueryCriteriaUtils.MAX_PAGE_SIZE;
|
||||||
|
import static net.corda.core.utilities.ByteArrays.toHexString;
|
||||||
import static net.corda.finance.contracts.asset.CashUtilities.*;
|
import static net.corda.finance.contracts.asset.CashUtilities.*;
|
||||||
import static net.corda.testing.CoreTestUtils.*;
|
import static net.corda.testing.CoreTestUtils.*;
|
||||||
import static net.corda.testing.TestConstants.*;
|
import static net.corda.testing.TestConstants.*;
|
||||||
import static net.corda.testing.node.MockServices.*;
|
import static net.corda.testing.node.MockServices.makeTestDatabaseAndMockServices;
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static net.corda.testing.node.MockServices.makeTestIdentityService;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public class VaultQueryJavaTests {
|
public class VaultQueryJavaTests {
|
||||||
@Rule
|
@Rule
|
||||||
@ -42,7 +62,7 @@ public class VaultQueryJavaTests {
|
|||||||
private CordaPersistence database;
|
private CordaPersistence database;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() throws CertificateException, InvalidAlgorithmParameterException {
|
||||||
List<String> cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName());
|
List<String> cordappPackages = Arrays.asList("net.corda.testing.contracts", "net.corda.finance.contracts.asset", CashSchemaV1.class.getPackage().getName());
|
||||||
ArrayList<KeyPair> keys = new ArrayList<>();
|
ArrayList<KeyPair> keys = new ArrayList<>();
|
||||||
keys.add(getMEGA_CORP_KEY());
|
keys.add(getMEGA_CORP_KEY());
|
||||||
@ -54,6 +74,8 @@ public class VaultQueryJavaTests {
|
|||||||
database = databaseAndServices.getFirst();
|
database = databaseAndServices.getFirst();
|
||||||
services = databaseAndServices.getSecond();
|
services = databaseAndServices.getSecond();
|
||||||
vaultService = services.getVaultService();
|
vaultService = services.getVaultService();
|
||||||
|
services.getIdentityService().verifyAndRegisterIdentity(getDUMMY_CASH_ISSUER_IDENTITY());
|
||||||
|
services.getIdentityService().verifyAndRegisterIdentity(getDUMMY_NOTARY_IDENTITY());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -169,9 +169,23 @@ class InMemoryIdentityServiceTests {
|
|||||||
* Ensure if we feed in a full identity, we get the same identity back.
|
* Ensure if we feed in a full identity, we get the same identity back.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `deanonymising a well known identity`() {
|
fun `deanonymising a well known identity should return the identity`() {
|
||||||
|
val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||||
val expected = ALICE
|
val expected = ALICE
|
||||||
val actual = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT).wellKnownPartyFromAnonymous(expected)
|
service.verifyAndRegisterIdentity(ALICE_IDENTITY)
|
||||||
|
val actual = service.wellKnownPartyFromAnonymous(expected)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we don't blindly trust what an anonymous identity claims to be.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `deanonymising a false well known identity should return null`() {
|
||||||
|
val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||||
|
val notAlice = Party(ALICE.name, generateKeyPair().public)
|
||||||
|
service.verifyAndRegisterIdentity(ALICE_IDENTITY)
|
||||||
|
val actual = service.wellKnownPartyFromAnonymous(notAlice)
|
||||||
|
assertNull(actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,11 +269,23 @@ class PersistentIdentityServiceTests {
|
|||||||
* Ensure if we feed in a full identity, we get the same identity back.
|
* Ensure if we feed in a full identity, we get the same identity back.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `deanonymising a well known identity`() {
|
fun `deanonymising a well known identity should return the identity`() {
|
||||||
|
val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||||
val expected = ALICE
|
val expected = ALICE
|
||||||
val actual = database.transaction {
|
service.verifyAndRegisterIdentity(ALICE_IDENTITY)
|
||||||
identityService.wellKnownPartyFromAnonymous(expected)
|
val actual = service.wellKnownPartyFromAnonymous(expected)
|
||||||
}
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we don't blindly trust what an anonymous identity claims to be.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `deanonymising a false well known identity should return null`() {
|
||||||
|
val service = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT)
|
||||||
|
val notAlice = Party(ALICE.name, generateKeyPair().public)
|
||||||
|
service.verifyAndRegisterIdentity(ALICE_IDENTITY)
|
||||||
|
val actual = service.wellKnownPartyFromAnonymous(notAlice)
|
||||||
|
assertNull(actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,26 @@
|
|||||||
package net.corda.node.services.persistence
|
package net.corda.node.services.persistence
|
||||||
|
|
||||||
import net.corda.core.contracts.ContractState
|
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.generateKeyPair
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.StatesToRecord
|
import net.corda.core.node.StatesToRecord
|
||||||
import net.corda.core.utilities.toBase58String
|
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.schemas.CommonSchemaV1
|
import net.corda.core.schemas.CommonSchemaV1
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentStateRef
|
import net.corda.core.schemas.PersistentStateRef
|
||||||
import net.corda.core.serialization.deserialize
|
|
||||||
import net.corda.core.serialization.SerializationDefaults
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.core.utilities.toBase58String
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.POUNDS
|
import net.corda.finance.POUNDS
|
||||||
import net.corda.finance.SWISS_FRANCS
|
import net.corda.finance.SWISS_FRANCS
|
||||||
import net.corda.finance.contracts.asset.*
|
import net.corda.finance.contracts.asset.Cash
|
||||||
|
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
||||||
|
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER_NAME
|
||||||
|
import net.corda.finance.contracts.asset.DummyFungibleContract
|
||||||
import net.corda.finance.schemas.CashSchemaV1
|
import net.corda.finance.schemas.CashSchemaV1
|
||||||
import net.corda.finance.schemas.SampleCashSchemaV2
|
import net.corda.finance.schemas.SampleCashSchemaV2
|
||||||
import net.corda.finance.schemas.SampleCashSchemaV3
|
import net.corda.finance.schemas.SampleCashSchemaV3
|
||||||
@ -54,7 +56,9 @@ class HibernateConfigurationTest {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
lateinit var services: MockServices
|
lateinit var services: MockServices
|
||||||
|
lateinit var bankServices: MockServices
|
||||||
lateinit var issuerServices: MockServices
|
lateinit var issuerServices: MockServices
|
||||||
|
lateinit var notaryServices: MockServices
|
||||||
lateinit var database: CordaPersistence
|
lateinit var database: CordaPersistence
|
||||||
val vault: VaultService get() = services.vaultService
|
val vault: VaultService get() = services.vaultService
|
||||||
|
|
||||||
@ -65,19 +69,27 @@ class HibernateConfigurationTest {
|
|||||||
lateinit var entityManager: EntityManager
|
lateinit var entityManager: EntityManager
|
||||||
lateinit var criteriaBuilder: CriteriaBuilder
|
lateinit var criteriaBuilder: CriteriaBuilder
|
||||||
|
|
||||||
|
// Identities used
|
||||||
|
private lateinit var identity: Party
|
||||||
|
private lateinit var issuer: Party
|
||||||
|
private lateinit var notary: Party
|
||||||
|
|
||||||
// test States
|
// test States
|
||||||
lateinit var cashStates: List<StateAndRef<Cash.State>>
|
lateinit var cashStates: List<StateAndRef<Cash.State>>
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset")
|
val cordappPackages = listOf("net.corda.testing.contracts", "net.corda.finance.contracts.asset")
|
||||||
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY, BOB_KEY, BOC_KEY)
|
bankServices = MockServices(cordappPackages, BOC.name, BOC_KEY)
|
||||||
|
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY)
|
||||||
|
notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val dataSourceProps = makeTestDataSourceProperties()
|
val dataSourceProps = makeTestDataSourceProperties()
|
||||||
val defaultDatabaseProperties = makeTestDatabaseProperties()
|
val defaultDatabaseProperties = makeTestDatabaseProperties()
|
||||||
database = configureDatabase(dataSourceProps, defaultDatabaseProperties, ::makeTestIdentityService)
|
database = configureDatabase(dataSourceProps, defaultDatabaseProperties, ::makeTestIdentityService)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
hibernateConfig = database.hibernateConfig
|
hibernateConfig = database.hibernateConfig
|
||||||
services = object : MockServices(cordappPackages, BOB_NAME, BOB_KEY, BOC_KEY, DUMMY_NOTARY_KEY) {
|
// `consumeCash` expects we can self-notarise transactions
|
||||||
|
services = object : MockServices(cordappPackages, BOB_NAME, generateKeyPair(), DUMMY_NOTARY_KEY) {
|
||||||
override val vaultService = makeVaultService(database.hibernateConfig)
|
override val vaultService = makeVaultService(database.hibernateConfig)
|
||||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||||
for (stx in txs) {
|
for (stx in txs) {
|
||||||
@ -91,7 +103,17 @@ class HibernateConfigurationTest {
|
|||||||
}
|
}
|
||||||
hibernatePersister = services.hibernatePersister
|
hibernatePersister = services.hibernatePersister
|
||||||
}
|
}
|
||||||
setUpDb()
|
|
||||||
|
identity = services.myInfo.singleIdentity()
|
||||||
|
issuer = issuerServices.myInfo.singleIdentity()
|
||||||
|
notary = notaryServices.myInfo.singleIdentity()
|
||||||
|
|
||||||
|
database.transaction {
|
||||||
|
val numStates = 10
|
||||||
|
cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, numStates, numStates, Random(0L), issuedBy = issuer.ref(1))
|
||||||
|
.states.toList()
|
||||||
|
}
|
||||||
|
|
||||||
sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3)
|
sessionFactory = sessionFactoryForSchemas(VaultSchemaV1, CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3)
|
||||||
entityManager = sessionFactory.createEntityManager()
|
entityManager = sessionFactory.createEntityManager()
|
||||||
criteriaBuilder = sessionFactory.criteriaBuilder
|
criteriaBuilder = sessionFactory.criteriaBuilder
|
||||||
@ -104,12 +126,6 @@ class HibernateConfigurationTest {
|
|||||||
database.close()
|
database.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpDb() {
|
|
||||||
database.transaction {
|
|
||||||
cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)).states.toList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `count rows`() {
|
fun `count rows`() {
|
||||||
// structure query
|
// structure query
|
||||||
@ -125,7 +141,7 @@ class HibernateConfigurationTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `consumed states`() {
|
fun `consumed states`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(50.DOLLARS, notary = notary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -206,11 +222,11 @@ class HibernateConfigurationTest {
|
|||||||
fun `with sorting by state ref desc and asc`() {
|
fun `with sorting by state ref desc and asc`() {
|
||||||
// generate additional state ref indexes
|
// generate additional state ref indexes
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.consumeCash(1.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(1.DOLLARS, notary = notary)
|
||||||
services.consumeCash(2.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(2.DOLLARS, notary = notary)
|
||||||
services.consumeCash(3.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(3.DOLLARS, notary = notary)
|
||||||
services.consumeCash(4.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(4.DOLLARS, notary = notary)
|
||||||
services.consumeCash(5.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(5.DOLLARS, notary = notary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -236,11 +252,11 @@ class HibernateConfigurationTest {
|
|||||||
fun `with sorting by state ref index and txId desc and asc`() {
|
fun `with sorting by state ref index and txId desc and asc`() {
|
||||||
// generate additional state ref indexes
|
// generate additional state ref indexes
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.consumeCash(1.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(1.DOLLARS, notary = notary)
|
||||||
services.consumeCash(2.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(2.DOLLARS, notary = notary)
|
||||||
services.consumeCash(3.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(3.DOLLARS, notary = notary)
|
||||||
services.consumeCash(4.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(4.DOLLARS, notary = notary)
|
||||||
services.consumeCash(5.DOLLARS, notary = DUMMY_NOTARY)
|
services.consumeCash(5.DOLLARS, notary = notary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -267,7 +283,7 @@ class HibernateConfigurationTest {
|
|||||||
fun `with pagination`() {
|
fun `with pagination`() {
|
||||||
// add 100 additional cash entries
|
// add 100 additional cash entries
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(1000.POUNDS, issuerServices, DUMMY_NOTARY, 100, 100, Random(0L), issuedBy = DUMMY_CASH_ISSUER)
|
services.fillWithSomeTestCash(1000.POUNDS, issuerServices, notary, 100, 100, Random(0L), issuedBy = issuer.ref(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -369,11 +385,11 @@ class HibernateConfigurationTest {
|
|||||||
fun `calculate cash balances`() {
|
fun `calculate cash balances`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) // +$100 = $200
|
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 10, issuer.ref(1)) // +$100 = $200
|
||||||
services.fillWithSomeTestCash(50.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // £50 = £50
|
services.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50
|
||||||
services.fillWithSomeTestCash(25.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // +£25 = £175
|
services.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175
|
||||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) // CHF500 = CHF500
|
services.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, notary, 10, issuer.ref(1)) // CHF500 = CHF500
|
||||||
services.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // +CHF250 = CHF750
|
services.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, notary, 5, issuer.ref(1)) // +CHF250 = CHF750
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -402,8 +418,8 @@ class HibernateConfigurationTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `calculate cash balance for single currency`() {
|
fun `calculate cash balance for single currency`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(50.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // £50 = £50
|
services.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50
|
||||||
services.fillWithSomeTestCash(25.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // +£25 = £175
|
services.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -432,10 +448,10 @@ class HibernateConfigurationTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `calculate and order by cash balance for owner and currency`() {
|
fun `calculate and order by cash balance for owner and currency`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
val bank = bankServices.myInfo.legalIdentities.single()
|
||||||
services.fillWithSomeTestCash(200.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1))
|
services.fillWithSomeTestCash(200.DOLLARS, bankServices, notary, 2, bank.ref(1))
|
||||||
services.fillWithSomeTestCash(300.POUNDS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER)
|
services.fillWithSomeTestCash(300.POUNDS, issuerServices, notary, 3, issuer.ref(1))
|
||||||
services.fillWithSomeTestCash(400.POUNDS, issuerServices, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2))
|
services.fillWithSomeTestCash(400.POUNDS, bankServices, notary, 4, bank.ref(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
// structure query
|
// structure query
|
||||||
@ -622,9 +638,9 @@ class HibernateConfigurationTest {
|
|||||||
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
||||||
}
|
}
|
||||||
|
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L), ownedBy = ALICE)
|
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L),
|
||||||
val cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L),
|
issuedBy = issuer.ref(1), owner = ALICE)
|
||||||
issuedBy = BOB.ref(0), ownedBy = (BOB)).states
|
val cashStates = services.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, identity.ref(0)).states
|
||||||
// persist additional cash states explicitly with V3 schema
|
// persist additional cash states explicitly with V3 schema
|
||||||
cashStates.forEach {
|
cashStates.forEach {
|
||||||
val cashState = it.state.data
|
val cashState = it.state.data
|
||||||
@ -646,7 +662,7 @@ class HibernateConfigurationTest {
|
|||||||
// search predicate
|
// search predicate
|
||||||
val cashStatesSchema = criteriaQuery.from(SampleCashSchemaV3.PersistentCashState::class.java)
|
val cashStatesSchema = criteriaQuery.from(SampleCashSchemaV3.PersistentCashState::class.java)
|
||||||
|
|
||||||
val queryOwner = BOB.name.toString()
|
val queryOwner = identity.name.toString()
|
||||||
criteriaQuery.where(criteriaBuilder.equal(cashStatesSchema.get<String>("owner"), queryOwner))
|
criteriaQuery.where(criteriaBuilder.equal(cashStatesSchema.get<String>("owner"), queryOwner))
|
||||||
|
|
||||||
val joinVaultStatesToCash = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), cashStatesSchema.get<PersistentStateRef>("stateRef"))
|
val joinVaultStatesToCash = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), cashStatesSchema.get<PersistentStateRef>("stateRef"))
|
||||||
@ -701,8 +717,8 @@ class HibernateConfigurationTest {
|
|||||||
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
||||||
}
|
}
|
||||||
|
|
||||||
val moreCash = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L),
|
val moreCash = services.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, 2, Random(0L),
|
||||||
issuedBy = BOB.ref(0), ownedBy = BOB).states
|
issuedBy = identity.ref(0), owner = identity).states
|
||||||
// persist additional cash states explicitly with V3 schema
|
// persist additional cash states explicitly with V3 schema
|
||||||
moreCash.forEach {
|
moreCash.forEach {
|
||||||
val cashState = it.state.data
|
val cashState = it.state.data
|
||||||
@ -710,7 +726,7 @@ class HibernateConfigurationTest {
|
|||||||
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L), ownedBy = (ALICE)).states
|
val cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L), owner = ALICE, issuedBy = issuer.ref(1)).states
|
||||||
// persist additional cash states explicitly with V3 schema
|
// persist additional cash states explicitly with V3 schema
|
||||||
cashStates.forEach {
|
cashStates.forEach {
|
||||||
val cashState = it.state.data
|
val cashState = it.state.data
|
||||||
|
@ -5,7 +5,8 @@ import net.corda.core.contracts.Amount
|
|||||||
import net.corda.core.contracts.Issued
|
import net.corda.core.contracts.Issued
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.NullKeys
|
||||||
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -77,7 +78,9 @@ class NodeVaultServiceTest {
|
|||||||
identity = services.myInfo.singleIdentityAndCert()
|
identity = services.myInfo.singleIdentityAndCert()
|
||||||
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY)
|
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY)
|
||||||
bocServices = MockServices(cordappPackages, BOC_NAME, BOC_KEY)
|
bocServices = MockServices(cordappPackages, BOC_NAME, BOC_KEY)
|
||||||
|
|
||||||
services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY)
|
services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY)
|
||||||
|
services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -511,10 +514,9 @@ class NodeVaultServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `correct updates are generated for general transactions`() {
|
fun `correct updates are generated for general transactions`() {
|
||||||
val service = vaultService
|
|
||||||
val notary = identity.party
|
val notary = identity.party
|
||||||
val vaultSubscriber = TestSubscriber<Vault.Update<*>>().apply {
|
val vaultSubscriber = TestSubscriber<Vault.Update<*>>().apply {
|
||||||
service.updates.subscribe(this)
|
vaultService.updates.subscribe(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val identity = services.myInfo.singleIdentityAndCert()
|
val identity = services.myInfo.singleIdentityAndCert()
|
||||||
@ -533,15 +535,16 @@ class NodeVaultServiceTest {
|
|||||||
val signedIssuedTx = services.signInitialTransaction(issueBuilder)
|
val signedIssuedTx = services.signInitialTransaction(issueBuilder)
|
||||||
services.validatedTransactions.addTransaction(signedIssuedTx)
|
services.validatedTransactions.addTransaction(signedIssuedTx)
|
||||||
|
|
||||||
database.transaction { service.notify(StatesToRecord.ONLY_RELEVANT, issueTx) }
|
database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) }
|
||||||
val expectedIssueUpdate = Vault.Update(emptySet(), setOf(cashState), null)
|
val expectedIssueUpdate = Vault.Update(emptySet(), setOf(cashState), null)
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val moveBuilder = TransactionBuilder(notary).apply {
|
val moveBuilder = TransactionBuilder(notary).apply {
|
||||||
Cash.generateSpend(services, this, Amount(1000, GBP), thirdPartyIdentity)
|
val changeIdentity = services.keyManagementService.freshKeyAndCert(identity, false)
|
||||||
|
Cash.generateSpend(services, this, Amount(1000, GBP), changeIdentity, thirdPartyIdentity)
|
||||||
}
|
}
|
||||||
val moveTx = moveBuilder.toWireTransaction(services)
|
val moveTx = moveBuilder.toWireTransaction(services)
|
||||||
service.notify(StatesToRecord.ONLY_RELEVANT, moveTx)
|
vaultService.notify(StatesToRecord.ONLY_RELEVANT, moveTx)
|
||||||
}
|
}
|
||||||
val expectedMoveUpdate = Vault.Update(setOf(cashState), emptySet(), null)
|
val expectedMoveUpdate = Vault.Update(setOf(cashState), emptySet(), null)
|
||||||
|
|
||||||
@ -580,6 +583,7 @@ class NodeVaultServiceTest {
|
|||||||
val initialCashState = StateAndRef(issueStx.tx.outputs.single(), StateRef(issueStx.id, 0))
|
val initialCashState = StateAndRef(issueStx.tx.outputs.single(), StateRef(issueStx.id, 0))
|
||||||
|
|
||||||
// Change notary
|
// Change notary
|
||||||
|
services.identityService.verifyAndRegisterIdentity(DUMMY_NOTARY_IDENTITY)
|
||||||
val newNotary = DUMMY_NOTARY
|
val newNotary = DUMMY_NOTARY
|
||||||
val changeNotaryTx = NotaryChangeWireTransaction(listOf(initialCashState.ref), issueStx.notary!!, newNotary)
|
val changeNotaryTx = NotaryChangeWireTransaction(listOf(initialCashState.ref), issueStx.notary!!, newNotary)
|
||||||
val cashStateWithNewNotary = StateAndRef(initialCashState.state.copy(notary = newNotary), StateRef(changeNotaryTx.id, 0))
|
val cashStateWithNewNotary = StateAndRef(initialCashState.state.copy(notary = newNotary), StateRef(changeNotaryTx.id, 0))
|
||||||
|
@ -12,7 +12,10 @@ import net.corda.core.internal.packageName
|
|||||||
import net.corda.core.node.services.*
|
import net.corda.core.node.services.*
|
||||||
import net.corda.core.node.services.vault.*
|
import net.corda.core.node.services.vault.*
|
||||||
import net.corda.core.node.services.vault.QueryCriteria.*
|
import net.corda.core.node.services.vault.QueryCriteria.*
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.NonEmptySet
|
||||||
|
import net.corda.core.utilities.days
|
||||||
|
import net.corda.core.utilities.seconds
|
||||||
|
import net.corda.core.utilities.toHexString
|
||||||
import net.corda.finance.*
|
import net.corda.finance.*
|
||||||
import net.corda.finance.contracts.CommercialPaper
|
import net.corda.finance.contracts.CommercialPaper
|
||||||
import net.corda.finance.contracts.Commodity
|
import net.corda.finance.contracts.Commodity
|
||||||
@ -31,7 +34,6 @@ import net.corda.testing.contracts.*
|
|||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
|
||||||
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
||||||
import org.assertj.core.api.Assertions
|
import org.assertj.core.api.Assertions
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -57,7 +59,7 @@ class VaultQueryTests {
|
|||||||
private lateinit var services: MockServices
|
private lateinit var services: MockServices
|
||||||
private lateinit var notaryServices: MockServices
|
private lateinit var notaryServices: MockServices
|
||||||
private val vaultService: VaultService get() = services.vaultService
|
private val vaultService: VaultService get() = services.vaultService
|
||||||
private val identitySvc: IdentityService = makeTestIdentityService()
|
private lateinit var identitySvc: IdentityService
|
||||||
private lateinit var database: CordaPersistence
|
private lateinit var database: CordaPersistence
|
||||||
|
|
||||||
// test cash notary
|
// test cash notary
|
||||||
@ -68,14 +70,16 @@ class VaultQueryTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
// register additional identities
|
// register additional identities
|
||||||
identitySvc.verifyAndRegisterIdentity(CASH_NOTARY_IDENTITY)
|
|
||||||
identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY)
|
|
||||||
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY),
|
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY),
|
||||||
createIdentityService = { identitySvc },
|
|
||||||
cordappPackages = cordappPackages)
|
cordappPackages = cordappPackages)
|
||||||
database = databaseAndServices.first
|
database = databaseAndServices.first
|
||||||
services = databaseAndServices.second
|
services = databaseAndServices.second
|
||||||
notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, DUMMY_CASH_ISSUER_KEY, BOC_KEY, MEGA_CORP_KEY)
|
notaryServices = MockServices(cordappPackages, DUMMY_NOTARY.name, DUMMY_NOTARY_KEY, DUMMY_CASH_ISSUER_KEY, BOC_KEY, MEGA_CORP_KEY)
|
||||||
|
identitySvc = services.identityService
|
||||||
|
// Register all of the identities we're going to use
|
||||||
|
(notaryServices.myInfo.legalIdentitiesAndCerts + BOC_IDENTITY + CASH_NOTARY_IDENTITY + MINI_CORP_IDENTITY + MEGA_CORP_IDENTITY).forEach { identity ->
|
||||||
|
services.identityService.verifyAndRegisterIdentity(identity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -1388,20 +1392,24 @@ class VaultQueryTests {
|
|||||||
// GBP issuer
|
// GBP issuer
|
||||||
val gbpCashIssuerName = CordaX500Name(organisation = "British Pounds Cash Issuer", locality = "London", country = "GB")
|
val gbpCashIssuerName = CordaX500Name(organisation = "British Pounds Cash Issuer", locality = "London", country = "GB")
|
||||||
val gbpCashIssuerServices = MockServices(cordappPackages, gbpCashIssuerName, generateKeyPair())
|
val gbpCashIssuerServices = MockServices(cordappPackages, gbpCashIssuerName, generateKeyPair())
|
||||||
val gbpCashIssuer = gbpCashIssuerServices.myInfo.singleIdentity().ref(1)
|
val gbpCashIssuer = gbpCashIssuerServices.myInfo.singleIdentityAndCert()
|
||||||
// USD issuer
|
// USD issuer
|
||||||
val usdCashIssuerName = CordaX500Name(organisation = "US Dollars Cash Issuer", locality = "New York", country = "US")
|
val usdCashIssuerName = CordaX500Name(organisation = "US Dollars Cash Issuer", locality = "New York", country = "US")
|
||||||
val usdCashIssuerServices = MockServices(cordappPackages, usdCashIssuerName, generateKeyPair())
|
val usdCashIssuerServices = MockServices(cordappPackages, usdCashIssuerName, generateKeyPair())
|
||||||
val usdCashIssuer = usdCashIssuerServices.myInfo.singleIdentity().ref(1)
|
val usdCashIssuer = usdCashIssuerServices.myInfo.singleIdentityAndCert()
|
||||||
// CHF issuer
|
// CHF issuer
|
||||||
val chfCashIssuerName = CordaX500Name(organisation = "Swiss Francs Cash Issuer", locality = "Zurich", country = "CH")
|
val chfCashIssuerName = CordaX500Name(organisation = "Swiss Francs Cash Issuer", locality = "Zurich", country = "CH")
|
||||||
val chfCashIssuerServices = MockServices(cordappPackages, chfCashIssuerName, generateKeyPair())
|
val chfCashIssuerServices = MockServices(cordappPackages, chfCashIssuerName, generateKeyPair())
|
||||||
val chfCashIssuer = chfCashIssuerServices.myInfo.singleIdentity().ref(1)
|
val chfCashIssuer = chfCashIssuerServices.myInfo.singleIdentityAndCert()
|
||||||
|
listOf(gbpCashIssuer, usdCashIssuer, chfCashIssuer).forEach { identity ->
|
||||||
|
services.identityService.verifyAndRegisterIdentity(identity)
|
||||||
|
}
|
||||||
|
database.transaction {
|
||||||
|
services.fillWithSomeTestCash(100.POUNDS, gbpCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = gbpCashIssuer.party.ref(1))
|
||||||
|
services.fillWithSomeTestCash(100.DOLLARS, usdCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = usdCashIssuer.party.ref(1))
|
||||||
|
services.fillWithSomeTestCash(100.SWISS_FRANCS, chfCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = chfCashIssuer.party.ref(1))
|
||||||
|
}
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(100.POUNDS, gbpCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = gbpCashIssuer)
|
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, usdCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = usdCashIssuer)
|
|
||||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, chfCashIssuerServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = chfCashIssuer)
|
|
||||||
val criteria = FungibleAssetQueryCriteria(issuer = listOf(gbpCashIssuer.party, usdCashIssuer.party))
|
val criteria = FungibleAssetQueryCriteria(issuer = listOf(gbpCashIssuer.party, usdCashIssuer.party))
|
||||||
val results = vaultService.queryBy<FungibleAsset<*>>(criteria)
|
val results = vaultService.queryBy<FungibleAsset<*>>(criteria)
|
||||||
assertThat(results.states).hasSize(2)
|
assertThat(results.states).hasSize(2)
|
||||||
@ -1413,8 +1421,9 @@ class VaultQueryTests {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1))
|
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = BOC.ref(1))
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L),
|
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L),
|
||||||
issuedBy = MEGA_CORP.ref(0), ownedBy = (MINI_CORP))
|
issuedBy = MEGA_CORP.ref(0), owner = (MINI_CORP))
|
||||||
|
}
|
||||||
|
database.transaction {
|
||||||
val criteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP))
|
val criteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP))
|
||||||
val results = vaultService.queryBy<FungibleAsset<*>>(criteria)
|
val results = vaultService.queryBy<FungibleAsset<*>>(criteria)
|
||||||
assertThat(results.states).hasSize(1) // can only be 1 owner of a node (MEGA_CORP in this MockServices setup)
|
assertThat(results.states).hasSize(1) // can only be 1 owner of a node (MEGA_CORP in this MockServices setup)
|
||||||
@ -1426,10 +1435,11 @@ class VaultQueryTests {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 1, 1, Random(0L))
|
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_NOTARY, 1, 1, Random(0L))
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L),
|
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L),
|
||||||
issuedBy = MEGA_CORP.ref(0), ownedBy = (MEGA_CORP))
|
issuedBy = MEGA_CORP.ref(0), owner = (MEGA_CORP))
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L),
|
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, DUMMY_NOTARY, 1, 1, Random(0L),
|
||||||
issuedBy = BOC.ref(0), ownedBy = (MINI_CORP)) // irrelevant to this vault
|
issuedBy = BOC.ref(0), owner = MINI_CORP) // irrelevant to this vault
|
||||||
|
}
|
||||||
|
database.transaction {
|
||||||
// DOCSTART VaultQueryExample5.2
|
// DOCSTART VaultQueryExample5.2
|
||||||
val criteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP, BOC))
|
val criteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP, BOC))
|
||||||
val results = vaultService.queryBy<ContractState>(criteria)
|
val results = vaultService.queryBy<ContractState>(criteria)
|
||||||
|
@ -148,7 +148,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
// A tx that sends us money.
|
// A tx that sends us money.
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L), ownedBy = AnonymousParty(freshKey),
|
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L), owner = AnonymousParty(freshKey),
|
||||||
issuedBy = MEGA_CORP.ref(1))
|
issuedBy = MEGA_CORP.ref(1))
|
||||||
println("Cash balance: ${services.getCashBalance(USD)}")
|
println("Cash balance: ${services.getCashBalance(USD)}")
|
||||||
}
|
}
|
||||||
@ -298,7 +298,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = AnonymousParty(freshKey))
|
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), owner = AnonymousParty(freshKey))
|
||||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L))
|
services.fillWithSomeTestCash(100.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||||
services.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L))
|
services.fillWithSomeTestCash(100.POUNDS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ open class MockServices(
|
|||||||
vararg val keys: KeyPair
|
vararg val keys: KeyPair
|
||||||
) : ServiceHub, StateLoader by stateLoader {
|
) : ServiceHub, StateLoader by stateLoader {
|
||||||
companion object {
|
companion object {
|
||||||
|
private val MOCK_IDENTITIES = listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor")
|
val MOCK_VERSION_INFO = VersionInfo(1, "Mock release", "Mock revision", "Mock Vendor")
|
||||||
@ -126,7 +127,7 @@ open class MockServices(
|
|||||||
/**
|
/**
|
||||||
* Makes database and mock services appropriate for unit tests.
|
* Makes database and mock services appropriate for unit tests.
|
||||||
* @param keys a list of [KeyPair] instances to be used by [MockServices]. Defaults to [MEGA_CORP_KEY]
|
* @param keys a list of [KeyPair] instances to be used by [MockServices]. Defaults to [MEGA_CORP_KEY]
|
||||||
* @param createIdentityService a lambda function returning an instance of [IdentityService]. Defauts to [InMemoryIdentityService].
|
* @param createIdentityService a lambda function returning an instance of [IdentityService]. Defaults to [InMemoryIdentityService].
|
||||||
*
|
*
|
||||||
* @return a pair where the first element is the instance of [CordaPersistence] and the second is [MockServices].
|
* @return a pair where the first element is the instance of [CordaPersistence] and the second is [MockServices].
|
||||||
*/
|
*/
|
||||||
|
@ -87,9 +87,6 @@ val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, AL
|
|||||||
|
|
||||||
val DUMMY_CASH_ISSUER_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_CASH_ISSUER.party as Party)
|
val DUMMY_CASH_ISSUER_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(DUMMY_CASH_ISSUER.party as Party)
|
||||||
|
|
||||||
val MOCK_IDENTITIES = listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)
|
|
||||||
val MOCK_IDENTITY_SERVICE: IdentityService get() = InMemoryIdentityService(MOCK_IDENTITIES, emptySet(), DEV_CA.certificate.cert)
|
|
||||||
|
|
||||||
val MOCK_HOST_AND_PORT = NetworkHostAndPort("mockHost", 30000)
|
val MOCK_HOST_AND_PORT = NetworkHostAndPort("mockHost", 30000)
|
||||||
|
|
||||||
fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
|
fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
|
||||||
|
@ -96,9 +96,26 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
|
|||||||
return Vault(states)
|
return Vault(states)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a random set of cash states that add up to the given amount and adds them to the vault. This is intended for
|
||||||
|
* unit tests. The cash is owned by the legal identity key from the storage service.
|
||||||
|
*
|
||||||
|
* The service hub needs to provide at least a key management service and a storage service.
|
||||||
|
*
|
||||||
|
* @param issuerServices service hub of the issuer node, which will be used to sign the transaction.
|
||||||
|
* @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary.
|
||||||
|
* @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!).
|
||||||
|
*/
|
||||||
|
fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
|
||||||
|
issuerServices: ServiceHub,
|
||||||
|
outputNotary: Party,
|
||||||
|
states: Int,
|
||||||
|
issuedBy: PartyAndReference): Vault<Cash.State>
|
||||||
|
= fillWithSomeTestCash(howMuch, issuerServices, outputNotary, states, states, issuedBy = issuedBy)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them
|
* Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them
|
||||||
* to the vault. This is intended for unit tests. The cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal
|
* to the vault. This is intended for unit tests. By default the cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal
|
||||||
* identity key from the storage service.
|
* identity key from the storage service.
|
||||||
*
|
*
|
||||||
* The service hub needs to provide at least a key management service and a storage service.
|
* The service hub needs to provide at least a key management service and a storage service.
|
||||||
@ -113,18 +130,15 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
|
|||||||
atLeastThisManyStates: Int = 3,
|
atLeastThisManyStates: Int = 3,
|
||||||
atMostThisManyStates: Int = 10,
|
atMostThisManyStates: Int = 10,
|
||||||
rng: Random = Random(),
|
rng: Random = Random(),
|
||||||
ownedBy: AbstractParty? = null,
|
owner: AbstractParty? = null,
|
||||||
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault<Cash.State> {
|
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault<Cash.State> {
|
||||||
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
|
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
|
||||||
|
|
||||||
val myKey = ownedBy?.owningKey ?: myInfo.chooseIdentity().owningKey
|
|
||||||
val anonParty = AnonymousParty(myKey)
|
|
||||||
|
|
||||||
// We will allocate one state to one transaction, for simplicities sake.
|
// We will allocate one state to one transaction, for simplicities sake.
|
||||||
val cash = Cash()
|
val cash = Cash()
|
||||||
val transactions: List<SignedTransaction> = amounts.map { pennies ->
|
val transactions: List<SignedTransaction> = amounts.map { pennies ->
|
||||||
val issuance = TransactionBuilder(null as Party?)
|
val issuance = TransactionBuilder(null as Party?)
|
||||||
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)), anonParty, outputNotary)
|
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)),owner ?: myInfo.singleIdentity(), outputNotary)
|
||||||
|
|
||||||
return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey)
|
return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user