mirror of
https://github.com/corda/corda.git
synced 2025-06-16 14:18:20 +00:00
CORDA-759: Enforce key checks on identity de-anonymisation (#1993)
Previously when de-anonymising a Party instance, the name of the Party was used rather than the key, meaning a Party could be constructed with a random nonsense key and any name, and be treated as corresponding to the well known identity. This is not a security hole in itself as in any real scenario a party shouldn't be trusted without having been registered, it creates a significant risk of a security hole depending on how trusted the anonymous identity is, and the returned identity is considered.
This commit is contained in:
@ -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))
|
||||
}
|
||||
|
Reference in New Issue
Block a user