Merge remote-tracking branch 'open/master' into colljos-merge-171117

This commit is contained in:
josecoll 2017-11-18 11:03:36 +00:00
commit ea1cd0035a
18 changed files with 283 additions and 167 deletions

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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 = {

View File

@ -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)

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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)
}
} }

View File

@ -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)
}
} }

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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))
} }

View File

@ -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].
*/ */

View File

@ -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)

View File

@ -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)
} }