mirror of
https://github.com/corda/corda.git
synced 2024-12-27 16:28:56 +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.
|
||||
|
||||
* ``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``.
|
||||
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.*
|
||||
|
||||
class CashSelectionH2Impl : AbstractCashSelection() {
|
||||
|
||||
companion object {
|
||||
const val JDBC_DRIVER_NAME = "H2 JDBC Driver"
|
||||
val log = loggerFor<CashSelectionH2Impl>()
|
||||
@ -25,7 +24,6 @@ class CashSelectionH2Impl : AbstractCashSelection() {
|
||||
|
||||
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:
|
||||
// 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
|
||||
@ -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)
|
||||
override fun executeQuery(connection: Connection, amount: Amount<Currency>, lockId: UUID, notary: Party?,
|
||||
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 = """
|
||||
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
|
||||
private lateinit var vaultStatesUnconsumed: List<StateAndRef<Cash.State>>
|
||||
|
||||
private lateinit var OUR_IDENTITY_1: AbstractParty
|
||||
private lateinit var OUR_IDENTITY_AND_CERT: PartyAndCertificate
|
||||
private lateinit var ourIdentity: AbstractParty
|
||||
private lateinit var miniCorpAnonymised: AnonymousParty
|
||||
private val CHARLIE_ANONYMISED = CHARLIE_IDENTITY.party.anonymise()
|
||||
|
||||
@ -65,28 +64,33 @@ class CashTests {
|
||||
fun setUp() {
|
||||
LogHelper.setLevel(NodeVaultService::class)
|
||||
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(
|
||||
cordappPackages = listOf("net.corda.finance.contracts.asset"),
|
||||
initialIdentityName = CordaX500Name(organisation = "Me", locality = "London", country = "GB"),
|
||||
keys = listOf(generateKeyPair()))
|
||||
database = databaseAndServices.first
|
||||
miniCorpServices = MockServices(listOf("net.corda.finance.contracts.asset"), MINI_CORP.name, MINI_CORP_KEY)
|
||||
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.
|
||||
database.transaction {
|
||||
database.transaction {
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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 {
|
||||
vaultStatesUnconsumed = ourServices.vaultService.queryBy<Cash.State>().states
|
||||
}
|
||||
@ -495,7 +499,7 @@ class CashTests {
|
||||
|
||||
private fun makeCash(amount: Amount<Currency>, issuer: AbstractParty, depositRef: Byte = 1) =
|
||||
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))
|
||||
)
|
||||
|
||||
@ -509,12 +513,14 @@ class CashTests {
|
||||
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)
|
||||
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() {
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
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() {
|
||||
val wtx =
|
||||
database.transaction {
|
||||
makeSpend(100.DOLLARS, miniCorpAnonymised)
|
||||
makeSpend(ourServices, 100.DOLLARS, miniCorpAnonymised)
|
||||
}
|
||||
database.transaction {
|
||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||
assertEquals(vaultState.ref, wtx.inputs[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
|
||||
fun generateSimpleSpendWithParties() {
|
||||
val changeIdentity = ourServices.keyManagementService.freshKeyAndCert(ourServices.myInfo.singleIdentityAndCert(), false)
|
||||
database.transaction {
|
||||
|
||||
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])
|
||||
}
|
||||
@ -621,7 +628,7 @@ class CashTests {
|
||||
fun generateSimpleSpendWithChange() {
|
||||
val wtx =
|
||||
database.transaction {
|
||||
makeSpend(10.DOLLARS, miniCorpAnonymised)
|
||||
makeSpend(ourServices, 10.DOLLARS, miniCorpAnonymised)
|
||||
}
|
||||
database.transaction {
|
||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||
@ -638,7 +645,7 @@ class CashTests {
|
||||
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(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() {
|
||||
val wtx =
|
||||
database.transaction {
|
||||
makeSpend(500.DOLLARS, miniCorpAnonymised)
|
||||
makeSpend(ourServices, 500.DOLLARS, miniCorpAnonymised)
|
||||
}
|
||||
database.transaction {
|
||||
val vaultState0 = vaultStatesUnconsumed.elementAt(0)
|
||||
@ -654,7 +661,7 @@ class CashTests {
|
||||
assertEquals(vaultState0.ref, wtx.inputs[0])
|
||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||
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() {
|
||||
val wtx =
|
||||
database.transaction {
|
||||
val wtx = makeSpend(580.DOLLARS, miniCorpAnonymised)
|
||||
val wtx = makeSpend(ourServices, 580.DOLLARS, miniCorpAnonymised)
|
||||
assertEquals(3, wtx.inputs.size)
|
||||
wtx
|
||||
}
|
||||
@ -675,7 +682,7 @@ class CashTests {
|
||||
assertEquals(vaultState2.ref, wtx.inputs[2])
|
||||
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(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 {
|
||||
|
||||
val e: InsufficientBalanceException = assertFailsWith("balance") {
|
||||
makeSpend(1000.DOLLARS, miniCorpAnonymised)
|
||||
makeSpend(ourServices, 1000.DOLLARS, miniCorpAnonymised)
|
||||
}
|
||||
assertEquals((1000 - 580).DOLLARS, e.amountMissing)
|
||||
|
||||
assertFailsWith(InsufficientBalanceException::class) {
|
||||
makeSpend(81.SWISS_FRANCS, miniCorpAnonymised)
|
||||
makeSpend(ourServices, 81.SWISS_FRANCS, miniCorpAnonymised)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -821,7 +828,7 @@ class CashTests {
|
||||
fun multiSpend() {
|
||||
val tx = TransactionBuilder(DUMMY_NOTARY)
|
||||
database.transaction {
|
||||
val changeIdentity = ourServices.keyManagementService.freshKeyAndCert(OUR_IDENTITY_AND_CERT, false)
|
||||
val changeIdentity = ourServices.keyManagementService.freshKeyAndCert(ourServices.myInfo.singleIdentityAndCert(), false)
|
||||
val payments = listOf(
|
||||
PartyAndAmount(miniCorpAnonymised, 400.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.finance.DOLLARS
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.flows.CashException
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
@ -10,8 +14,9 @@ import net.corda.testing.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.After
|
||||
import org.junit.Test
|
||||
import java.util.Collections.nCopies
|
||||
|
||||
class CashSelectionH2Test {
|
||||
class CashSelectionH2ImplTest {
|
||||
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance"))
|
||||
|
||||
@After
|
||||
@ -19,6 +24,19 @@ class CashSelectionH2Test {
|
||||
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
|
||||
fun `check does not hold connection over retries`() {
|
||||
val bankA = mockNet.createNode(MockNodeParameters(configOverrides = {
|
@ -41,26 +41,30 @@ class NodeStatePersistenceTests : IntegrationTest() {
|
||||
|
||||
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
|
||||
val message = Message("Hello world!")
|
||||
driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||
val stateAndRef: StateAndRef<MessageState>? = driver(isDebug = true, startNodesInProcess = isQuasarAgentSpecified()) {
|
||||
val nodeName = {
|
||||
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
|
||||
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 {
|
||||
it.proxy.startFlow(::SendMessageFlow, message).returnValue.getOrThrow()
|
||||
it.proxy.startFlow(::SendMessageFlow, message, defaultNotaryIdentity).returnValue.getOrThrow()
|
||||
}
|
||||
nodeHandle.stop()
|
||||
nodeName
|
||||
}()
|
||||
|
||||
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 stateAndRef = page.states.singleOrNull()
|
||||
assertNotNull(stateAndRef)
|
||||
val retrievedMessage = stateAndRef!!.state.data.message
|
||||
assertEquals(message, retrievedMessage)
|
||||
page.states.singleOrNull()
|
||||
}
|
||||
nodeHandle.stop()
|
||||
result
|
||||
}
|
||||
assertNotNull(stateAndRef)
|
||||
val retrievedMessage = stateAndRef!!.state.data.message
|
||||
assertEquals(message, retrievedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +131,7 @@ open class MessageContract : Contract {
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
class SendMessageFlow(private val message: Message) : FlowLogic<SignedTransaction>() {
|
||||
class SendMessageFlow(private val message: Message, private val notary: Party) : FlowLogic<SignedTransaction>() {
|
||||
companion object {
|
||||
object GENERATING_TRANSACTION : ProgressTracker.Step("Generating transaction based on the message.")
|
||||
object VERIFYING_TRANSACTION : ProgressTracker.Step("Verifying contract constraints.")
|
||||
@ -143,8 +147,6 @@ class SendMessageFlow(private val message: Message) : FlowLogic<SignedTransactio
|
||||
|
||||
@Suspendable
|
||||
override fun call(): SignedTransaction {
|
||||
val notary = serviceHub.networkMapCache.notaryIdentities.first()
|
||||
|
||||
progressTracker.currentStep = GENERATING_TRANSACTION
|
||||
|
||||
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 wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party
|
||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||
// Expand the anonymous party to a full party (i.e. has a name) if possible
|
||||
val candidate = party as? Party ?: keyToParties[party.owningKey]?.party
|
||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||
// 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
|
||||
// Look up the well known identity for that name
|
||||
return if (candidate != null) {
|
||||
// If we have a well known identity by that name, use it in preference to the candidate. Otherwise default
|
||||
// back to the candidate.
|
||||
principalToParties[candidate.name]?.party ?: candidate
|
||||
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party ${candidate} does not match expected ${party}" }
|
||||
wellKnownPartyFromX500Name(candidate.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -164,15 +164,13 @@ class PersistentIdentityService(identities: Iterable<PartyAndCertificate> = empt
|
||||
override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
|
||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||
// Expand the anonymous party to a full party (i.e. has a name) if possible
|
||||
val candidate = party as? Party ?: partyFromKey(party.owningKey)
|
||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||
// 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
|
||||
// Look up the well known identity for that name
|
||||
return if (candidate != null) {
|
||||
// If we have a well known identity by that name, use it in preference to the candidate. Otherwise default
|
||||
// back to the candidate.
|
||||
val res = wellKnownPartyFromX500Name(candidate.name) ?: candidate
|
||||
res
|
||||
wellKnownPartyFromX500Name(candidate.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class AbstractPartyToX500NameAsStringConverter(identitySvc: () -> IdentityServic
|
||||
if (party != null) {
|
||||
val partyName = identityService.wellKnownPartyFromAnonymous(party)?.toString()
|
||||
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
|
||||
}
|
||||
|
@ -1,37 +1,57 @@
|
||||
package net.corda.node.services.vault;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import kotlin.*;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import kotlin.Pair;
|
||||
import kotlin.Triple;
|
||||
import net.corda.core.contracts.*;
|
||||
import net.corda.core.identity.*;
|
||||
import net.corda.core.messaging.*;
|
||||
import net.corda.core.node.services.*;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import net.corda.core.messaging.DataFeed;
|
||||
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.QueryCriteria.*;
|
||||
import net.corda.core.utilities.*;
|
||||
import net.corda.finance.contracts.*;
|
||||
import net.corda.finance.contracts.asset.*;
|
||||
import net.corda.finance.schemas.*;
|
||||
import net.corda.node.utilities.*;
|
||||
import net.corda.testing.*;
|
||||
import net.corda.testing.contracts.*;
|
||||
import net.corda.testing.node.*;
|
||||
import org.junit.*;
|
||||
import net.corda.core.node.services.vault.QueryCriteria.LinearStateQueryCriteria;
|
||||
import net.corda.core.node.services.vault.QueryCriteria.VaultCustomQueryCriteria;
|
||||
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria;
|
||||
import net.corda.core.utilities.EncodingUtils;
|
||||
import net.corda.core.utilities.OpaqueBytes;
|
||||
import net.corda.finance.contracts.DealState;
|
||||
import net.corda.finance.contracts.asset.Cash;
|
||||
import net.corda.finance.contracts.asset.CashUtilities;
|
||||
import net.corda.finance.schemas.CashSchemaV1;
|
||||
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 java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.security.*;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.cert.CertificateException;
|
||||
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.utilities.ByteArrays.*;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaUtils.DEFAULT_PAGE_NUM;
|
||||
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.testing.CoreTestUtils.*;
|
||||
import static net.corda.testing.TestConstants.*;
|
||||
import static net.corda.testing.node.MockServices.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static net.corda.testing.node.MockServices.makeTestDatabaseAndMockServices;
|
||||
import static net.corda.testing.node.MockServices.makeTestIdentityService;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class VaultQueryJavaTests {
|
||||
@Rule
|
||||
@ -42,7 +62,7 @@ public class VaultQueryJavaTests {
|
||||
private CordaPersistence database;
|
||||
|
||||
@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());
|
||||
ArrayList<KeyPair> keys = new ArrayList<>();
|
||||
keys.add(getMEGA_CORP_KEY());
|
||||
@ -54,6 +74,8 @@ public class VaultQueryJavaTests {
|
||||
database = databaseAndServices.getFirst();
|
||||
services = databaseAndServices.getSecond();
|
||||
vaultService = services.getVaultService();
|
||||
services.getIdentityService().verifyAndRegisterIdentity(getDUMMY_CASH_ISSUER_IDENTITY());
|
||||
services.getIdentityService().verifyAndRegisterIdentity(getDUMMY_NOTARY_IDENTITY());
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -169,9 +169,23 @@ class InMemoryIdentityServiceTests {
|
||||
* Ensure if we feed in a full identity, we get the same identity back.
|
||||
*/
|
||||
@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 actual = InMemoryIdentityService(trustRoot = DEV_TRUST_ROOT).wellKnownPartyFromAnonymous(expected)
|
||||
service.verifyAndRegisterIdentity(ALICE_IDENTITY)
|
||||
val actual = service.wellKnownPartyFromAnonymous(expected)
|
||||
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.
|
||||
*/
|
||||
@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 actual = database.transaction {
|
||||
identityService.wellKnownPartyFromAnonymous(expected)
|
||||
}
|
||||
service.verifyAndRegisterIdentity(ALICE_IDENTITY)
|
||||
val actual = service.wellKnownPartyFromAnonymous(expected)
|
||||
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
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionState
|
||||
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.utilities.toBase58String
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.schemas.CommonSchemaV1
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
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.utilities.toBase58String
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.POUNDS
|
||||
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.SampleCashSchemaV2
|
||||
import net.corda.finance.schemas.SampleCashSchemaV3
|
||||
@ -54,7 +56,9 @@ class HibernateConfigurationTest {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
lateinit var services: MockServices
|
||||
lateinit var bankServices: MockServices
|
||||
lateinit var issuerServices: MockServices
|
||||
lateinit var notaryServices: MockServices
|
||||
lateinit var database: CordaPersistence
|
||||
val vault: VaultService get() = services.vaultService
|
||||
|
||||
@ -65,19 +69,27 @@ class HibernateConfigurationTest {
|
||||
lateinit var entityManager: EntityManager
|
||||
lateinit var criteriaBuilder: CriteriaBuilder
|
||||
|
||||
// Identities used
|
||||
private lateinit var identity: Party
|
||||
private lateinit var issuer: Party
|
||||
private lateinit var notary: Party
|
||||
|
||||
// test States
|
||||
lateinit var cashStates: List<StateAndRef<Cash.State>>
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
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 defaultDatabaseProperties = makeTestDatabaseProperties()
|
||||
database = configureDatabase(dataSourceProps, defaultDatabaseProperties, ::makeTestIdentityService)
|
||||
database.transaction {
|
||||
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 fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||
for (stx in txs) {
|
||||
@ -91,7 +103,17 @@ class HibernateConfigurationTest {
|
||||
}
|
||||
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)
|
||||
entityManager = sessionFactory.createEntityManager()
|
||||
criteriaBuilder = sessionFactory.criteriaBuilder
|
||||
@ -104,12 +126,6 @@ class HibernateConfigurationTest {
|
||||
database.close()
|
||||
}
|
||||
|
||||
private fun setUpDb() {
|
||||
database.transaction {
|
||||
cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)).states.toList()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `count rows`() {
|
||||
// structure query
|
||||
@ -125,7 +141,7 @@ class HibernateConfigurationTest {
|
||||
@Test
|
||||
fun `consumed states`() {
|
||||
database.transaction {
|
||||
services.consumeCash(50.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(50.DOLLARS, notary = notary)
|
||||
}
|
||||
|
||||
// structure query
|
||||
@ -206,11 +222,11 @@ class HibernateConfigurationTest {
|
||||
fun `with sorting by state ref desc and asc`() {
|
||||
// generate additional state ref indexes
|
||||
database.transaction {
|
||||
services.consumeCash(1.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(2.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(3.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(4.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(5.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(1.DOLLARS, notary = notary)
|
||||
services.consumeCash(2.DOLLARS, notary = notary)
|
||||
services.consumeCash(3.DOLLARS, notary = notary)
|
||||
services.consumeCash(4.DOLLARS, notary = notary)
|
||||
services.consumeCash(5.DOLLARS, notary = notary)
|
||||
}
|
||||
|
||||
// structure query
|
||||
@ -236,11 +252,11 @@ class HibernateConfigurationTest {
|
||||
fun `with sorting by state ref index and txId desc and asc`() {
|
||||
// generate additional state ref indexes
|
||||
database.transaction {
|
||||
services.consumeCash(1.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(2.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(3.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(4.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(5.DOLLARS, notary = DUMMY_NOTARY)
|
||||
services.consumeCash(1.DOLLARS, notary = notary)
|
||||
services.consumeCash(2.DOLLARS, notary = notary)
|
||||
services.consumeCash(3.DOLLARS, notary = notary)
|
||||
services.consumeCash(4.DOLLARS, notary = notary)
|
||||
services.consumeCash(5.DOLLARS, notary = notary)
|
||||
}
|
||||
|
||||
// structure query
|
||||
@ -267,7 +283,7 @@ class HibernateConfigurationTest {
|
||||
fun `with pagination`() {
|
||||
// add 100 additional cash entries
|
||||
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
|
||||
@ -369,11 +385,11 @@ class HibernateConfigurationTest {
|
||||
fun `calculate cash balances`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) // +$100 = $200
|
||||
services.fillWithSomeTestCash(50.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // £50 = £50
|
||||
services.fillWithSomeTestCash(25.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // +£25 = £175
|
||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 10, 10, Random(0L)) // CHF500 = CHF500
|
||||
services.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // +CHF250 = CHF750
|
||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 10, issuer.ref(1)) // +$100 = $200
|
||||
services.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50
|
||||
services.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175
|
||||
services.fillWithSomeTestCash(500.SWISS_FRANCS, issuerServices, notary, 10, issuer.ref(1)) // CHF500 = CHF500
|
||||
services.fillWithSomeTestCash(250.SWISS_FRANCS, issuerServices, notary, 5, issuer.ref(1)) // +CHF250 = CHF750
|
||||
}
|
||||
|
||||
// structure query
|
||||
@ -402,8 +418,8 @@ class HibernateConfigurationTest {
|
||||
@Test
|
||||
fun `calculate cash balance for single currency`() {
|
||||
database.transaction {
|
||||
services.fillWithSomeTestCash(50.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // £50 = £50
|
||||
services.fillWithSomeTestCash(25.POUNDS, issuerServices, DUMMY_NOTARY, 5, 5, Random(0L)) // +£25 = £175
|
||||
services.fillWithSomeTestCash(50.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // £50 = £50
|
||||
services.fillWithSomeTestCash(25.POUNDS, issuerServices, notary, 5, issuer.ref(1)) // +£25 = £175
|
||||
}
|
||||
|
||||
// structure query
|
||||
@ -432,10 +448,10 @@ class HibernateConfigurationTest {
|
||||
@Test
|
||||
fun `calculate and order by cash balance for owner and currency`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(200.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = BOC.ref(1))
|
||||
services.fillWithSomeTestCash(300.POUNDS, issuerServices, DUMMY_NOTARY, 3, 3, Random(0L), issuedBy = DUMMY_CASH_ISSUER)
|
||||
services.fillWithSomeTestCash(400.POUNDS, issuerServices, DUMMY_NOTARY, 4, 4, Random(0L), issuedBy = BOC.ref(2))
|
||||
val bank = bankServices.myInfo.legalIdentities.single()
|
||||
services.fillWithSomeTestCash(200.DOLLARS, bankServices, notary, 2, bank.ref(1))
|
||||
services.fillWithSomeTestCash(300.POUNDS, issuerServices, notary, 3, issuer.ref(1))
|
||||
services.fillWithSomeTestCash(400.POUNDS, bankServices, notary, 4, bank.ref(2))
|
||||
}
|
||||
|
||||
// structure query
|
||||
@ -622,9 +638,9 @@ class HibernateConfigurationTest {
|
||||
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
||||
}
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L), ownedBy = ALICE)
|
||||
val cashStates = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L),
|
||||
issuedBy = BOB.ref(0), ownedBy = (BOB)).states
|
||||
services.fillWithSomeTestCash(100.DOLLARS, issuerServices, notary, 2, 2, Random(0L),
|
||||
issuedBy = issuer.ref(1), owner = ALICE)
|
||||
val cashStates = services.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, identity.ref(0)).states
|
||||
// persist additional cash states explicitly with V3 schema
|
||||
cashStates.forEach {
|
||||
val cashState = it.state.data
|
||||
@ -646,7 +662,7 @@ class HibernateConfigurationTest {
|
||||
// search predicate
|
||||
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))
|
||||
|
||||
val joinVaultStatesToCash = criteriaBuilder.equal(vaultStates.get<PersistentStateRef>("stateRef"), cashStatesSchema.get<PersistentStateRef>("stateRef"))
|
||||
@ -701,8 +717,8 @@ class HibernateConfigurationTest {
|
||||
hibernatePersister.persistStateWithSchema(dummyFungibleState, it.ref, SampleCashSchemaV3)
|
||||
}
|
||||
|
||||
val moreCash = services.fillWithSomeTestCash(100.DOLLARS, issuerServices, DUMMY_NOTARY, 2, 2, Random(0L),
|
||||
issuedBy = BOB.ref(0), ownedBy = BOB).states
|
||||
val moreCash = services.fillWithSomeTestCash(100.DOLLARS, services, notary, 2, 2, Random(0L),
|
||||
issuedBy = identity.ref(0), owner = identity).states
|
||||
// persist additional cash states explicitly with V3 schema
|
||||
moreCash.forEach {
|
||||
val cashState = it.state.data
|
||||
@ -710,7 +726,7 @@ class HibernateConfigurationTest {
|
||||
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
|
||||
cashStates.forEach {
|
||||
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.StateAndRef
|
||||
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.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
@ -77,7 +78,9 @@ class NodeVaultServiceTest {
|
||||
identity = services.myInfo.singleIdentityAndCert()
|
||||
issuerServices = MockServices(cordappPackages, DUMMY_CASH_ISSUER_NAME, DUMMY_CASH_ISSUER_KEY)
|
||||
bocServices = MockServices(cordappPackages, BOC_NAME, BOC_KEY)
|
||||
|
||||
services.identityService.verifyAndRegisterIdentity(DUMMY_CASH_ISSUER_IDENTITY)
|
||||
services.identityService.verifyAndRegisterIdentity(BOC_IDENTITY)
|
||||
}
|
||||
|
||||
@After
|
||||
@ -511,10 +514,9 @@ class NodeVaultServiceTest {
|
||||
|
||||
@Test
|
||||
fun `correct updates are generated for general transactions`() {
|
||||
val service = vaultService
|
||||
val notary = identity.party
|
||||
val vaultSubscriber = TestSubscriber<Vault.Update<*>>().apply {
|
||||
service.updates.subscribe(this)
|
||||
vaultService.updates.subscribe(this)
|
||||
}
|
||||
|
||||
val identity = services.myInfo.singleIdentityAndCert()
|
||||
@ -533,15 +535,16 @@ class NodeVaultServiceTest {
|
||||
val signedIssuedTx = services.signInitialTransaction(issueBuilder)
|
||||
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)
|
||||
|
||||
database.transaction {
|
||||
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)
|
||||
service.notify(StatesToRecord.ONLY_RELEVANT, moveTx)
|
||||
vaultService.notify(StatesToRecord.ONLY_RELEVANT, moveTx)
|
||||
}
|
||||
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))
|
||||
|
||||
// Change notary
|
||||
services.identityService.verifyAndRegisterIdentity(DUMMY_NOTARY_IDENTITY)
|
||||
val newNotary = DUMMY_NOTARY
|
||||
val changeNotaryTx = NotaryChangeWireTransaction(listOf(initialCashState.ref), issueStx.notary!!, newNotary)
|
||||
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.vault.*
|
||||
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.contracts.CommercialPaper
|
||||
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.Companion.makeTestDatabaseAndMockServices
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
||||
import net.corda.testing.schemas.DummyLinearStateSchemaV1
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -57,7 +59,7 @@ class VaultQueryTests {
|
||||
private lateinit var services: MockServices
|
||||
private lateinit var notaryServices: MockServices
|
||||
private val vaultService: VaultService get() = services.vaultService
|
||||
private val identitySvc: IdentityService = makeTestIdentityService()
|
||||
private lateinit var identitySvc: IdentityService
|
||||
private lateinit var database: CordaPersistence
|
||||
|
||||
// test cash notary
|
||||
@ -68,14 +70,16 @@ class VaultQueryTests {
|
||||
@Before
|
||||
fun setUp() {
|
||||
// register additional identities
|
||||
identitySvc.verifyAndRegisterIdentity(CASH_NOTARY_IDENTITY)
|
||||
identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY)
|
||||
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(MEGA_CORP_KEY, DUMMY_NOTARY_KEY),
|
||||
createIdentityService = { identitySvc },
|
||||
cordappPackages = cordappPackages)
|
||||
database = databaseAndServices.first
|
||||
services = databaseAndServices.second
|
||||
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
|
||||
@ -1388,20 +1392,24 @@ class VaultQueryTests {
|
||||
// GBP issuer
|
||||
val gbpCashIssuerName = CordaX500Name(organisation = "British Pounds Cash Issuer", locality = "London", country = "GB")
|
||||
val gbpCashIssuerServices = MockServices(cordappPackages, gbpCashIssuerName, generateKeyPair())
|
||||
val gbpCashIssuer = gbpCashIssuerServices.myInfo.singleIdentity().ref(1)
|
||||
val gbpCashIssuer = gbpCashIssuerServices.myInfo.singleIdentityAndCert()
|
||||
// USD issuer
|
||||
val usdCashIssuerName = CordaX500Name(organisation = "US Dollars Cash Issuer", locality = "New York", country = "US")
|
||||
val usdCashIssuerServices = MockServices(cordappPackages, usdCashIssuerName, generateKeyPair())
|
||||
val usdCashIssuer = usdCashIssuerServices.myInfo.singleIdentity().ref(1)
|
||||
val usdCashIssuer = usdCashIssuerServices.myInfo.singleIdentityAndCert()
|
||||
// CHF issuer
|
||||
val chfCashIssuerName = CordaX500Name(organisation = "Swiss Francs Cash Issuer", locality = "Zurich", country = "CH")
|
||||
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 {
|
||||
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 results = vaultService.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(2)
|
||||
@ -1413,8 +1421,9 @@ class VaultQueryTests {
|
||||
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 = 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 results = vaultService.queryBy<FungibleAsset<*>>(criteria)
|
||||
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 {
|
||||
services.fillWithSomeTestCash(100.DOLLARS, notaryServices, CASH_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),
|
||||
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
|
||||
val criteria = FungibleAssetQueryCriteria(owner = listOf(MEGA_CORP, BOC))
|
||||
val results = vaultService.queryBy<ContractState>(criteria)
|
||||
|
@ -148,7 +148,7 @@ class VaultWithCashTest {
|
||||
|
||||
database.transaction {
|
||||
// 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))
|
||||
println("Cash balance: ${services.getCashBalance(USD)}")
|
||||
}
|
||||
@ -298,7 +298,7 @@ class VaultWithCashTest {
|
||||
|
||||
val freshKey = services.keyManagementService.freshKey()
|
||||
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.POUNDS, issuerServices, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ open class MockServices(
|
||||
vararg val keys: KeyPair
|
||||
) : ServiceHub, StateLoader by stateLoader {
|
||||
companion object {
|
||||
private val MOCK_IDENTITIES = listOf(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY)
|
||||
|
||||
@JvmStatic
|
||||
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.
|
||||
* @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].
|
||||
*/
|
||||
|
@ -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 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)
|
||||
|
||||
fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
|
||||
|
@ -96,9 +96,26 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* 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,
|
||||
atMostThisManyStates: Int = 10,
|
||||
rng: Random = Random(),
|
||||
ownedBy: AbstractParty? = null,
|
||||
owner: AbstractParty? = null,
|
||||
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault<Cash.State> {
|
||||
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.
|
||||
val cash = Cash()
|
||||
val transactions: List<SignedTransaction> = amounts.map { pennies ->
|
||||
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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user