mirror of
https://github.com/corda/corda.git
synced 2025-06-16 22:28:15 +00:00
Contract constraints (#1518)
* Contract constraints and attachment loading Fix compiler warnings. Fixed IdentitySyncFlowTests in confidential-identities. Fixes. Fix AttachmentClassLoaderTests. Added a TODO. Renamed cordapp service. Fix compilation error in java code. Fix RaftNotaryServiceTests Fix AttachmentLoadingTest Fix DistributedServiceTests and LargeTransactionTests. Add cordapp packages to Verifier tests. Refactor DummyContractBackdoor back out of internal package. Resolve compiler warnings. Consolidate excluding `isolated` project at top-level. Fix contract attachment serialisation for remote verifier. Fix integration tests for client:rpc. Contract constraints and attachment loading Fix compiler warnings. Fixed IdentitySyncFlowTests in confidential-identities. Fixes. Fix AttachmentClassLoaderTests. Added a TODO. Renamed cordapp service. Fix compilation error in java code. Fix example compilation. Fix RaftNotaryServiceTests Fix AttachmentLoadingTest Fix DistributedServiceTests and LargeTransactionTests. Add cordapp packages to Verifier tests. Refactor DummyContractBackdoor back out of internal package. Resolve compiler warnings. Consolidate excluding `isolated` project at top-level. Fix integration tests for client:rpc. Fixed issues with node driver and differing ZIPs. Review changes. Refactor GeneratedAttachment into node-api module. Merge branch 'clint/hash-constraint' of https://github.com/corda/corda into clint/hash-constraint Fixed compile error following rebase. wip - test to check that app code isn't loaded from attachments sent over the wire. Use Kotlin copyTo() rather than Apache's IOUtils. Fixes more fixes. Removing unconstrained output. More fixes. Fixed another test. Added missing plugin definition in net.corda.core.node.CordaPluginRegistry: net.corda.finance.contracts.isolated.IsolatedPlugin Re-added missing magic string used in unit test. Remove unused FlowSession variable. * Review fixes. * More review fixes. * Moved Cordapp implementation to an internal package. * More JVMOverloads.
This commit is contained in:
@ -0,0 +1,108 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.PartyAndReference
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.DUMMY_BANK_A
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.TestDependencyInjectionBase
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.MockServices
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Files
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class AttachmentLoadingTests : TestDependencyInjectionBase() {
|
||||
private class Services : MockServices() {
|
||||
private val provider = CordappProviderImpl(CordappLoader.createDevMode(listOf(isolatedJAR))).start(attachments)
|
||||
private val cordapp get() = provider.cordapps.first()
|
||||
val attachmentId get() = provider.getCordappAttachmentId(cordapp)!!
|
||||
val appContext get() = provider.getAppContext(cordapp)
|
||||
override val cordappProvider: CordappProvider = provider
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val isolatedJAR = this::class.java.getResource("isolated.jar")!!
|
||||
private val ISOLATED_CONTRACT_ID = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
}
|
||||
|
||||
private lateinit var services: Services
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
services = Services()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test a wire transaction has loaded the correct attachment`() {
|
||||
val appClassLoader = services.appContext.classLoader
|
||||
val contractClass = appClassLoader.loadClass(ISOLATED_CONTRACT_ID).asSubclass(Contract::class.java)
|
||||
val generateInitialMethod = contractClass.getDeclaredMethod("generateInitial", PartyAndReference::class.java, Integer.TYPE, Party::class.java)
|
||||
val contract = contractClass.newInstance()
|
||||
val txBuilder = generateInitialMethod.invoke(contract, PartyAndReference(DUMMY_BANK_A, OpaqueBytes(kotlin.ByteArray(1))), 1, DUMMY_NOTARY) as TransactionBuilder
|
||||
val context = SerializationFactory.defaultFactory.defaultContext
|
||||
.withClassLoader(appClassLoader)
|
||||
val ledgerTx = txBuilder.toLedgerTransaction(services, context)
|
||||
contract.verify(ledgerTx)
|
||||
|
||||
val actual = ledgerTx.attachments.first()
|
||||
val expected = services.attachments.openAttachment(services.attachmentId)!!
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
// TODO - activate this test
|
||||
// @Test
|
||||
fun `test that attachments retrieved over the network are not used for code`() {
|
||||
driver(initialiseSerialization = false) {
|
||||
val bankAName = CordaX500Name("BankA", "Zurich", "CH")
|
||||
val bankBName = CordaX500Name("BankB", "Zurich", "CH")
|
||||
// Copy the app jar to the first node. The second won't have it.
|
||||
val path = (baseDirectory(bankAName.toString()) / "plugins").createDirectories() / "isolated.jar"
|
||||
isolatedJAR.openStream().buffered().use { input ->
|
||||
Files.newOutputStream(path).buffered().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val admin = User("admin", "admin", permissions = setOf("ALL"))
|
||||
val (bankA, bankB) = listOf(
|
||||
startNode(providedName = bankAName, rpcUsers = listOf(admin)),
|
||||
startNode(providedName = bankBName, rpcUsers = listOf(admin))
|
||||
).transpose().getOrThrow() // Wait for all nodes to start up.
|
||||
|
||||
val clazz =
|
||||
Class.forName("net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator", true, URLClassLoader(arrayOf(isolatedJAR)))
|
||||
.asSubclass(FlowLogic::class.java)
|
||||
|
||||
try {
|
||||
bankA.rpcClientToNode().start("admin", "admin").use { rpc ->
|
||||
val proxy = rpc.proxy
|
||||
val party = proxy.wellKnownPartyFromX500Name(bankBName)!!
|
||||
|
||||
assertFailsWith<Exception>("xxx") {
|
||||
proxy.startFlowDynamic(clazz, party).returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
bankA.stop()
|
||||
bankB.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
@ -43,6 +44,7 @@ class BFTNotaryServiceTests {
|
||||
|
||||
private val mockNet = MockNetwork()
|
||||
private val node = mockNet.createNode(advertisedServices = ServiceInfo(NetworkMapService.type))
|
||||
|
||||
@After
|
||||
fun stopNodes() {
|
||||
mockNet.stopNodes()
|
||||
@ -75,7 +77,7 @@ class BFTNotaryServiceTests {
|
||||
val notary = node.services.getDefaultNotary()
|
||||
val f = node.run {
|
||||
val trivialTx = signInitialTransaction(notary) {
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID)
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||
}
|
||||
// Create a new consensus while the redundant replica is sleeping:
|
||||
services.startFlow(NotaryFlow.Client(trivialTx)).resultFuture
|
||||
@ -100,7 +102,7 @@ class BFTNotaryServiceTests {
|
||||
val notary = node.services.getDefaultNotary()
|
||||
node.run {
|
||||
val issueTx = signInitialTransaction(notary) {
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID)
|
||||
addOutputState(DummyContract.SingleOwnerState(owner = info.chooseIdentity()), DUMMY_PROGRAM_ID, AlwaysAcceptAttachmentConstraint)
|
||||
}
|
||||
database.transaction {
|
||||
services.recordTransactions(issueTx)
|
||||
|
@ -11,6 +11,7 @@ import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.flows.CashPaymentFlow
|
||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.*
|
||||
@ -21,7 +22,6 @@ import org.junit.Test
|
||||
import rx.Observable
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
|
||||
|
||||
class DistributedServiceTests : DriverBasedTest() {
|
||||
lateinit var alice: NodeHandle
|
||||
@ -30,7 +30,7 @@ class DistributedServiceTests : DriverBasedTest() {
|
||||
lateinit var raftNotaryIdentity: Party
|
||||
lateinit var notaryStateMachines: Observable<Pair<Party, StateMachineUpdate>>
|
||||
|
||||
override fun setup() = driver {
|
||||
override fun setup() = driver(extraCordappPackagesToScan = listOf("net.corda.finance.contracts")) {
|
||||
// Start Alice and 3 notaries in a RAFT cluster
|
||||
val clusterSize = 3
|
||||
val testUser = User("test", "test", permissions = setOf(
|
||||
|
@ -13,12 +13,12 @@ import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||
import net.corda.testing.DUMMY_BANK_A
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.node.NodeBasedTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
@ -27,6 +27,16 @@ import kotlin.test.assertFailsWith
|
||||
class RaftNotaryServiceTests : NodeBasedTest() {
|
||||
private val notaryName = CordaX500Name(RaftValidatingNotaryService.type.id, "RAFT Notary Service", "London", "GB")
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setCordappPackages("net.corda.testing.contracts")
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `detect double spend`() {
|
||||
val (bankA) = listOf(
|
||||
|
@ -62,7 +62,7 @@ class LargeTransactionsTest {
|
||||
val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 1)
|
||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 2)
|
||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024 * 3, 3)
|
||||
driver(startNodesInProcess = true) {
|
||||
driver(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.testing.contracts")) {
|
||||
val (alice, _, _) = aliceBobAndNotary()
|
||||
alice.useRPC {
|
||||
val hash1 = it.uploadAttachment(bigFile1.inputStream)
|
||||
|
@ -145,7 +145,7 @@ class SendMessageFlow(private val message: Message) : FlowLogic<SignedTransactio
|
||||
val txBuilder = TransactionBuilder(notary).withItems(StateAndContract(messageState, MESSAGE_CONTRACT_PROGRAM_ID), txCommand)
|
||||
|
||||
progressTracker.currentStep = VERIFYING_TRANSACTION
|
||||
txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify()
|
||||
txBuilder.toWireTransaction(serviceHub).toLedgerTransaction(serviceHub).verify()
|
||||
|
||||
progressTracker.currentStep = SIGNING_TRANSACTION
|
||||
val signedTx = serviceHub.signInitialTransaction(txBuilder)
|
||||
|
Binary file not shown.
Binary file not shown.
@ -7,6 +7,7 @@ import com.google.common.util.concurrent.MoreExecutors
|
||||
import net.corda.confidential.SwapIdentitiesFlow
|
||||
import net.corda.confidential.SwapIdentitiesHandler
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
@ -33,8 +34,8 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProvider
|
||||
import net.corda.node.services.ContractUpgradeHandler
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.services.FinalityHandler
|
||||
import net.corda.node.services.NotaryChangeHandler
|
||||
import net.corda.node.services.api.*
|
||||
@ -147,7 +148,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
protected val runOnStop = ArrayList<() -> Any?>()
|
||||
protected lateinit var database: CordaPersistence
|
||||
protected var dbCloser: (() -> Any?)? = null
|
||||
lateinit var cordappProvider: CordappProvider
|
||||
lateinit var cordappProvider: CordappProviderImpl
|
||||
|
||||
protected val _nodeReadyFuture = openFuture<Unit>()
|
||||
/** Completes once the node has successfully registered with the network map service
|
||||
@ -259,8 +260,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
check(myNotaryIdentity != null) { "Trying to install a notary service but no notary identity specified" }
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java, PublicKey::class.java).apply { isAccessible = true }
|
||||
constructor.newInstance(services, myNotaryIdentity!!.owningKey)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHub::class.java).apply { isAccessible = true }
|
||||
constructor.newInstance(services)
|
||||
}
|
||||
@ -382,9 +382,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
*/
|
||||
private fun makeServices(): MutableList<Any> {
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
cordappProvider = CordappProviderImpl(makeCordappLoader())
|
||||
_services = ServiceHubInternalImpl()
|
||||
attachments = NodeAttachmentService(services.monitoringService.metrics)
|
||||
cordappProvider = CordappProvider(attachments, makeCordappLoader())
|
||||
cordappProvider.start(attachments)
|
||||
legalIdentity = obtainIdentity()
|
||||
network = makeMessagingService(legalIdentity)
|
||||
info = makeInfo(legalIdentity)
|
||||
@ -393,16 +394,19 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
services.keyManagementService, services.identityService, platformClock, services.schedulerService,
|
||||
services.auditService, services.monitoringService, services.networkMapCache, services.schemaService,
|
||||
services.transactionVerifierService, services.validatedTransactions, services.contractUpgradeService,
|
||||
services, this)
|
||||
services, cordappProvider, this)
|
||||
makeNetworkServices(tokenizableServices)
|
||||
return tokenizableServices
|
||||
}
|
||||
|
||||
private fun makeCordappLoader(): CordappLoader {
|
||||
val scanPackages = System.getProperty("net.corda.node.cordapp.scan.packages")
|
||||
return if (scanPackages != null) {
|
||||
return if (CordappLoader.testPackages.isNotEmpty()) {
|
||||
check(configuration.devMode) { "Package scanning can only occur in dev mode" }
|
||||
CordappLoader.createDevMode(scanPackages)
|
||||
CordappLoader.createWithTestPackages(CordappLoader.testPackages)
|
||||
} else if (scanPackages != null) {
|
||||
check(configuration.devMode) { "Package scanning can only occur in dev mode" }
|
||||
CordappLoader.createWithTestPackages(scanPackages.split(","))
|
||||
} else {
|
||||
CordappLoader.createDefault(configuration.baseDirectory)
|
||||
}
|
||||
@ -646,8 +650,8 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
|
||||
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
||||
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||
keyStore.signAndSaveNewKeyPair(name, privateKeyAlias, generateKeyPair())
|
||||
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||
keyStore.signAndSaveNewKeyPair(name, privateKeyAlias, generateKeyPair())
|
||||
}
|
||||
|
||||
val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
@ -713,6 +717,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
override val myInfo: NodeInfo get() = info
|
||||
override val database: CordaPersistence get() = this@AbstractNode.database
|
||||
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
|
||||
override val cordappProvider: CordappProvider = this@AbstractNode.cordappProvider
|
||||
|
||||
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
|
||||
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
|
||||
@ -732,6 +737,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
super.recordTransactions(notifyVault, txs)
|
||||
}
|
||||
}
|
||||
|
||||
override fun jdbcSession(): Connection = database.createSession()
|
||||
}
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
package net.corda.node.internal.cordapp
|
||||
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.CordaPluginRegistry
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Defines a CorDapp
|
||||
*
|
||||
* @property contractClassNames List of contracts
|
||||
* @property initiatedFlows List of initiatable flow classes
|
||||
* @property rpcFlows List of RPC initiable flows classes
|
||||
* @property servies List of RPC services
|
||||
* @property plugins List of Corda plugin registries
|
||||
* @property jarPath The path to the JAR for this CorDapp
|
||||
*/
|
||||
data class Cordapp(
|
||||
val contractClassNames: List<String>,
|
||||
val initiatedFlows: List<Class<out FlowLogic<*>>>,
|
||||
val rpcFlows: List<Class<out FlowLogic<*>>>,
|
||||
val services: List<Class<out SerializeAsToken>>,
|
||||
val plugins: List<CordaPluginRegistry>,
|
||||
val customSchemas: Set<MappedSchema>,
|
||||
val jarPath: URL)
|
@ -3,24 +3,34 @@ package net.corda.node.internal.cordapp
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.UpgradedContract
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.flows.ContractUpgradeFlow
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.node.CordaPluginRegistry
|
||||
import net.corda.core.node.services.CordaService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.lang.reflect.Modifier
|
||||
import java.net.JarURLConnection
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.streams.toList
|
||||
|
||||
@ -45,35 +55,18 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
|
||||
* for classpath scanning.
|
||||
*/
|
||||
fun createDefault(baseDir: Path): CordappLoader {
|
||||
val pluginsDir = baseDir / "plugins"
|
||||
val pluginsDir = getPluginsPath(baseDir)
|
||||
return CordappLoader(if (!pluginsDir.exists()) emptyList<URL>() else pluginsDir.list {
|
||||
it.filter { it.isRegularFile() && it.toString().endsWith(".jar") }.map { it.toUri().toURL() }.toList()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dev mode CordappLoader intended to only be used in test environments.
|
||||
*
|
||||
* @param scanPackages list of packages to scan.
|
||||
*/
|
||||
fun createDevMode(scanPackages: String): CordappLoader {
|
||||
val paths = scanPackages.split(",").flatMap { scanPackage ->
|
||||
val resource = scanPackage.replace('.', '/')
|
||||
this::class.java.classLoader.getResources(resource)
|
||||
.asSequence()
|
||||
.map {
|
||||
val uri = if (it.protocol == "jar") {
|
||||
(it.openConnection() as JarURLConnection).jarFileURL.toURI()
|
||||
} else {
|
||||
URI(it.toExternalForm().removeSuffix(resource))
|
||||
}
|
||||
uri.toURL()
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
fun getPluginsPath(baseDir: Path): Path = baseDir / "plugins"
|
||||
|
||||
return CordappLoader(paths)
|
||||
}
|
||||
/**
|
||||
* Create a dev mode CordappLoader for test environments
|
||||
*/
|
||||
fun createWithTestPackages(testPackages: List<String> = CordappLoader.testPackages) = CordappLoader(testPackages.flatMap(this::createScanPackage))
|
||||
|
||||
/**
|
||||
* Creates a dev mode CordappLoader intended only to be used in test environments
|
||||
@ -81,13 +74,62 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
|
||||
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection
|
||||
*/
|
||||
@VisibleForTesting
|
||||
internal fun createDevMode(scanJars: List<URL>) = CordappLoader(scanJars)
|
||||
fun createDevMode(scanJars: List<URL>) = CordappLoader(scanJars)
|
||||
|
||||
private fun createScanPackage(scanPackage: String): List<URL> {
|
||||
val resource = scanPackage.replace('.', '/')
|
||||
return this::class.java.classLoader.getResources(resource)
|
||||
.asSequence()
|
||||
.map { path ->
|
||||
if (path.protocol == "jar") {
|
||||
(path.openConnection() as JarURLConnection).jarFileURL.toURI()
|
||||
} else {
|
||||
createDevCordappJar(scanPackage, path, resource)
|
||||
}.toURL()
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
|
||||
/** Takes a package of classes and creates a JAR from them - only use in tests */
|
||||
private fun createDevCordappJar(scanPackage: String, path: URL, jarPackageName: String): URI {
|
||||
if(!generatedCordapps.contains(path)) {
|
||||
val cordappDir = File("build/tmp/generated-test-cordapps")
|
||||
cordappDir.mkdirs()
|
||||
val cordappJAR = File(cordappDir, "$scanPackage-${UUID.randomUUID()}.jar")
|
||||
logger.info("Generating a test-only cordapp of classes discovered in $scanPackage at $cordappJAR")
|
||||
FileOutputStream(cordappJAR).use {
|
||||
JarOutputStream(it).use { jos ->
|
||||
val scanDir = File(path.toURI())
|
||||
scanDir.walkTopDown().forEach {
|
||||
val entryPath = jarPackageName + "/" + scanDir.toPath().relativize(it.toPath()).toString().replace('\\', '/')
|
||||
val time = FileTime.from(Instant.EPOCH)
|
||||
val entry = ZipEntry(entryPath).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
||||
jos.putNextEntry(entry)
|
||||
if (it.isFile) {
|
||||
Files.copy(it.toPath(), jos)
|
||||
}
|
||||
jos.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
generatedCordapps[path] = cordappJAR.toURI()
|
||||
}
|
||||
|
||||
return generatedCordapps[path]!!
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of test packages that will be scanned as CorDapps and compiled into CorDapp JARs for use in tests only
|
||||
*/
|
||||
@VisibleForTesting
|
||||
var testPackages: List<String> = emptyList()
|
||||
private val generatedCordapps = mutableMapOf<URL, URI>()
|
||||
}
|
||||
|
||||
private fun loadCordapps(): List<Cordapp> {
|
||||
return cordappJarPaths.map {
|
||||
val scanResult = scanCordapp(it)
|
||||
Cordapp(findContractClassNames(scanResult),
|
||||
CordappImpl(findContractClassNames(scanResult),
|
||||
findInitiatedFlows(scanResult),
|
||||
findRPCFlows(scanResult),
|
||||
findServices(scanResult),
|
||||
@ -131,7 +173,7 @@ class CordappLoader private constructor(private val cordappJarPaths: List<URL>)
|
||||
}
|
||||
|
||||
private fun findContractClassNames(scanResult: ScanResult): List<String> {
|
||||
return scanResult.getNamesOfClassesImplementing(Contract::class.java)
|
||||
return (scanResult.getNamesOfClassesImplementing(Contract::class.java) + scanResult.getNamesOfClassesImplementing(UpgradedContract::class.java)).distinct()
|
||||
}
|
||||
|
||||
private fun findPlugins(cordappJarPath: URL): List<CordaPluginRegistry> {
|
||||
|
@ -1,38 +0,0 @@
|
||||
package net.corda.node.internal.cordapp
|
||||
|
||||
import com.google.common.collect.HashBiMap
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Cordapp provider and store. For querying CorDapps for their attachment and vice versa.
|
||||
*/
|
||||
class CordappProvider(private val attachmentStorage: AttachmentStorage, private val cordappLoader: CordappLoader) {
|
||||
/**
|
||||
* Current known CorDapps loaded on this node
|
||||
*/
|
||||
val cordapps get() = cordappLoader.cordapps
|
||||
private lateinit var cordappAttachments: HashBiMap<SecureHash, Cordapp>
|
||||
|
||||
/**
|
||||
* Should only be called once from the initialisation routine of the node or tests
|
||||
*/
|
||||
fun start() {
|
||||
cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attachment ID of this CorDapp. Only CorDapps with contracts have an attachment ID
|
||||
*
|
||||
* @param cordapp The cordapp to get the attachment ID
|
||||
* @return An attachment ID if it exists, otherwise nothing
|
||||
*/
|
||||
fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp)
|
||||
|
||||
private fun loadContractsIntoAttachmentStore(): Map<SecureHash, Cordapp> {
|
||||
val cordappsWithAttachments = cordapps.filter { !it.contractClassNames.isEmpty() }
|
||||
val attachmentIds = cordappsWithAttachments.map { it.jarPath.openStream().use { attachmentStorage.importAttachment(it) } }
|
||||
return attachmentIds.zip(cordappsWithAttachments).toMap()
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package net.corda.node.internal.cordapp
|
||||
|
||||
import com.google.common.collect.HashBiMap
|
||||
import net.corda.core.contracts.ContractClassName
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.cordapp.CordappContext
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import java.net.URLClassLoader
|
||||
|
||||
/**
|
||||
* Cordapp provider and store. For querying CorDapps for their attachment and vice versa.
|
||||
*/
|
||||
open class CordappProviderImpl(private val cordappLoader: CordappLoader) : SingletonSerializeAsToken(), CordappProvider {
|
||||
override fun getAppContext(): CordappContext {
|
||||
// TODO: Use better supported APIs in Java 9
|
||||
Exception().stackTrace.forEach { stackFrame ->
|
||||
val cordapp = getCordappForClass(stackFrame.className)
|
||||
if(cordapp != null) {
|
||||
return getAppContext(cordapp)
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalStateException("Not in an app context")
|
||||
}
|
||||
|
||||
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? {
|
||||
return getCordappForClass(contractClassName)?.let(this::getCordappAttachmentId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Current known CorDapps loaded on this node
|
||||
*/
|
||||
val cordapps get() = cordappLoader.cordapps
|
||||
private lateinit var cordappAttachments: HashBiMap<SecureHash, Cordapp>
|
||||
|
||||
/**
|
||||
* Should only be called once from the initialisation routine of the node or tests
|
||||
*/
|
||||
fun start(attachmentStorage: AttachmentStorage): CordappProviderImpl {
|
||||
cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore(attachmentStorage))
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attachment ID of this CorDapp. Only CorDapps with contracts have an attachment ID
|
||||
*
|
||||
* @param cordapp The cordapp to get the attachment ID
|
||||
* @return An attachment ID if it exists, otherwise nothing
|
||||
*/
|
||||
fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp)
|
||||
|
||||
private fun loadContractsIntoAttachmentStore(attachmentStorage: AttachmentStorage): Map<SecureHash, Cordapp> {
|
||||
val cordappsWithAttachments = cordapps.filter { !it.contractClassNames.isEmpty() }
|
||||
val attachmentIds = cordappsWithAttachments.map { it.jarPath.openStream().use { attachmentStorage.importAttachment(it) } }
|
||||
return attachmentIds.zip(cordappsWithAttachments).toMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current cordapp context for the given CorDapp
|
||||
*
|
||||
* @param cordapp The cordapp to get the context for
|
||||
* @return A cordapp context for the given CorDapp
|
||||
*/
|
||||
fun getAppContext(cordapp: Cordapp): CordappContext {
|
||||
return CordappContext(cordapp, getCordappAttachmentId(cordapp), URLClassLoader(arrayOf(cordapp.jarPath), cordappLoader.appClassLoader))
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a cordapp for the provided class or null if there isn't one
|
||||
*
|
||||
* @param className The class name
|
||||
* @return cordapp A cordapp or null if no cordapp has the given class loaded
|
||||
*/
|
||||
fun getCordappForClass(className: String): Cordapp? = cordapps.find { it.cordappClasses.contains(className) }
|
||||
}
|
@ -58,7 +58,7 @@ class ContractUpgradeHandler(otherSide: FlowSession) : AbstractStateReplacementF
|
||||
val authorisedUpgrade = serviceHub.contractUpgradeService.getAuthorisedContractUpgrade(oldStateAndRef.ref) ?:
|
||||
throw IllegalStateException("Contract state upgrade is unauthorised. State hash : ${oldStateAndRef.ref}")
|
||||
val proposedTx = stx.tx
|
||||
val expectedTx = ContractUpgradeUtils.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction()
|
||||
val expectedTx = ContractUpgradeUtils.assembleBareTx(oldStateAndRef, proposal.modification, proposedTx.privacySalt).toWireTransaction(serviceHub)
|
||||
requireThat {
|
||||
"The instigator is one of the participants" using (initiatingSession.counterparty in oldStateAndRef.state.data.participants)
|
||||
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification.name == authorisedUpgrade)
|
||||
|
@ -15,7 +15,6 @@ import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.utilities.DatabaseTransactionManager
|
||||
import net.corda.node.utilities.NODE_DATABASE_PREFIX
|
||||
import java.io.*
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Paths
|
||||
import java.util.jar.JarInputStream
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
@ -166,16 +165,14 @@ class NodeAttachmentService(metrics: MetricRegistry) : AttachmentStorage, Single
|
||||
criteriaQuery.select(criteriaBuilder.count(criteriaQuery.from(NodeAttachmentService.DBAttachment::class.java)))
|
||||
criteriaQuery.where(criteriaBuilder.equal(attachments.get<String>(DBAttachment::attId.name), id.toString()))
|
||||
val count = session.createQuery(criteriaQuery).singleResult
|
||||
if (count > 0) {
|
||||
throw FileAlreadyExistsException(id.toString())
|
||||
if (count == 0L) {
|
||||
val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes)
|
||||
session.save(attachment)
|
||||
|
||||
attachmentCount.inc()
|
||||
log.info("Stored new attachment $id")
|
||||
}
|
||||
|
||||
val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes)
|
||||
session.save(attachment)
|
||||
|
||||
attachmentCount.inc()
|
||||
log.info("Stored new attachment $id")
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
|
@ -62,13 +62,14 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
setCordappPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset");
|
||||
ArrayList<KeyPair> keys = new ArrayList<>();
|
||||
keys.add(getMEGA_CORP_KEY());
|
||||
keys.add(getDUMMY_NOTARY_KEY());
|
||||
Set<MappedSchema> requiredSchemas = Collections.singleton(CashSchemaV1.INSTANCE);
|
||||
IdentityService identitySvc = makeTestIdentityService();
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc);
|
||||
Pair<CordaPersistence, MockServices> databaseAndServices = makeTestDatabaseAndMockServices(requiredSchemas, keys, () -> identitySvc, Collections.EMPTY_LIST);
|
||||
issuerServices = new MockServices(getDUMMY_CASH_ISSUER_KEY(), getBOC_KEY());
|
||||
database = databaseAndServices.getFirst();
|
||||
services = databaseAndServices.getSecond();
|
||||
@ -78,6 +79,7 @@ public class VaultQueryJavaTests extends TestDependencyInjectionBase {
|
||||
@After
|
||||
public void cleanUp() throws IOException {
|
||||
database.close();
|
||||
unsetCordappPackages();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,12 +32,9 @@ import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.client.rpc.PermissionException
|
||||
import net.corda.nodeapi.User
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.expect
|
||||
import net.corda.testing.expectEvents
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetwork.MockNode
|
||||
import net.corda.testing.sequence
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.After
|
||||
@ -68,6 +65,8 @@ class CordaRPCOpsImplTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setCordappPackages("net.corda.finance.contracts.asset")
|
||||
|
||||
mockNet = MockNetwork()
|
||||
val networkMap = mockNet.createNode(advertisedServices = ServiceInfo(NetworkMapService.type))
|
||||
aliceNode = mockNet.createNode(networkMapAddress = networkMap.network.myAddress)
|
||||
@ -86,6 +85,7 @@ class CordaRPCOpsImplTest {
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.node.cordapp
|
||||
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
@ -15,7 +14,7 @@ class DummyFlow : FlowLogic<Unit>() {
|
||||
}
|
||||
|
||||
@InitiatedBy(DummyFlow::class)
|
||||
class LoaderTestFlow(unusedSession: FlowSession) : FlowLogic<Unit>() {
|
||||
class LoaderTestFlow : FlowLogic<Unit>() {
|
||||
override fun call() { }
|
||||
}
|
||||
|
||||
@ -29,7 +28,7 @@ class CordappLoaderTest {
|
||||
|
||||
@Test
|
||||
fun `test that classes that are in a cordapp are loaded`() {
|
||||
val loader = CordappLoader.createDevMode("net.corda.node.cordapp")
|
||||
val loader = CordappLoader.createWithTestPackages(listOf("net.corda.node.cordapp"))
|
||||
val initiatedFlows = loader.cordapps.first().initiatedFlows
|
||||
val expectedClass = loader.appClassLoader.loadClass("net.corda.node.cordapp.LoaderTestFlow").asSubclass(FlowLogic::class.java)
|
||||
assertThat(initiatedFlows).contains(expectedClass)
|
||||
@ -49,7 +48,7 @@ class CordappLoaderTest {
|
||||
assertThat(actualCordapp.rpcFlows).contains(loader.appClassLoader.loadClass("net.corda.core.flows.ContractUpgradeFlow\$Initiate").asSubclass(FlowLogic::class.java))
|
||||
assertThat(actualCordapp.services).isEmpty()
|
||||
assertThat(actualCordapp.plugins).hasSize(1)
|
||||
assertThat(actualCordapp.plugins.first().javaClass.name).isEqualTo("net.corda.finance.contracts.isolated.DummyPlugin")
|
||||
assertThat(actualCordapp.plugins.first().javaClass.name).isEqualTo("net.corda.finance.contracts.isolated.IsolatedPlugin")
|
||||
assertThat(actualCordapp.jarPath).isEqualTo(isolatedJAR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
package net.corda.node.cordapp
|
||||
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.testing.node.MockAttachmentStorage
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class CordappProviderImplTests {
|
||||
companion object {
|
||||
private val isolatedJAR = this::class.java.getResource("isolated.jar")!!
|
||||
private val emptyJAR = this::class.java.getResource("empty.jar")!!
|
||||
}
|
||||
|
||||
private lateinit var attachmentStore: AttachmentStorage
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
attachmentStore = MockAttachmentStorage()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isolated jar is loaded into the attachment store`() {
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val provider = CordappProviderImpl(loader)
|
||||
|
||||
provider.start(attachmentStore)
|
||||
val maybeAttachmentId = provider.getCordappAttachmentId(provider.cordapps.first())
|
||||
|
||||
Assert.assertNotNull(maybeAttachmentId)
|
||||
Assert.assertNotNull(attachmentStore.openAttachment(maybeAttachmentId!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty jar is not loaded into the attachment store`() {
|
||||
val loader = CordappLoader.createDevMode(listOf(emptyJAR))
|
||||
val provider = CordappProviderImpl(loader)
|
||||
|
||||
provider.start(attachmentStore)
|
||||
|
||||
Assert.assertNull(provider.getCordappAttachmentId(provider.cordapps.first()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that we find a cordapp class that is loaded into the store`() {
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val provider = CordappProviderImpl(loader)
|
||||
val className = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
|
||||
val expected = provider.cordapps.first()
|
||||
val actual = provider.getCordappForClass(className)
|
||||
|
||||
Assert.assertNotNull(actual)
|
||||
Assert.assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test that we find an attachment for a cordapp contrat class`() {
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val provider = CordappProviderImpl(loader)
|
||||
val className = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||
|
||||
provider.start(attachmentStore)
|
||||
val expected = provider.getAppContext(provider.cordapps.first()).attachmentId
|
||||
val actual = provider.getContractAttachmentID(className)
|
||||
|
||||
Assert.assertNotNull(actual)
|
||||
Assert.assertEquals(actual!!, expected)
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package net.corda.node.cordapp
|
||||
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProvider
|
||||
import net.corda.testing.node.MockAttachmentStorage
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class CordappProviderTests {
|
||||
@Test
|
||||
fun `isolated jar is loaded into the attachment store`() {
|
||||
val attachmentStore = MockAttachmentStorage()
|
||||
val isolatedJAR = this::class.java.getResource("isolated.jar")!!
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val provider = CordappProvider(attachmentStore, loader)
|
||||
|
||||
provider.start()
|
||||
val maybeAttachmentId = provider.getCordappAttachmentId(provider.cordapps.first())
|
||||
|
||||
Assert.assertNotNull(maybeAttachmentId)
|
||||
Assert.assertNotNull(attachmentStore.openAttachment(maybeAttachmentId!!))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty jar is not loaded into the attachment store`() {
|
||||
val attachmentStore = MockAttachmentStorage()
|
||||
val isolatedJAR = this::class.java.getResource("empty.jar")!!
|
||||
val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
|
||||
val provider = CordappProvider(attachmentStore, loader)
|
||||
|
||||
provider.start()
|
||||
|
||||
Assert.assertNull(provider.getCordappAttachmentId(provider.cordapps.first()))
|
||||
}
|
||||
}
|
@ -67,10 +67,11 @@ import kotlin.test.assertTrue
|
||||
* We assume that Alice and Bob already found each other via some market, and have agreed the details already.
|
||||
*/
|
||||
class TwoPartyTradeFlowTests {
|
||||
lateinit var mockNet: MockNetwork
|
||||
private lateinit var mockNet: MockNetwork
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
setCordappPackages("net.corda.finance.contracts")
|
||||
LogHelper.setLevel("platform.trade", "core.contract.TransactionGroup", "recordingmap")
|
||||
}
|
||||
|
||||
@ -78,6 +79,7 @@ class TwoPartyTradeFlowTests {
|
||||
fun after() {
|
||||
mockNet.stopNodes()
|
||||
LogHelper.reset("platform.trade", "core.contract.TransactionGroup", "recordingmap")
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -351,8 +353,9 @@ class TwoPartyTradeFlowTests {
|
||||
attachment(ByteArrayInputStream(stream.toByteArray()))
|
||||
}
|
||||
|
||||
val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobNode.info.chooseIdentity().owningKey),
|
||||
notary).second
|
||||
val bobsFakeCash = bobNode.database.transaction {
|
||||
fillUpForBuyer(false, issuer, AnonymousParty(bobNode.info.chooseIdentity().owningKey), notary)
|
||||
}.second
|
||||
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode)
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
fillUpForSeller(false, issuer, aliceNode.info.chooseIdentity(),
|
||||
@ -458,8 +461,9 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
|
||||
val bobsKey = bobNode.services.keyManagementService.keys.single()
|
||||
val bobsFakeCash = fillUpForBuyer(false, issuer, AnonymousParty(bobsKey),
|
||||
notary).second
|
||||
val bobsFakeCash = bobNode.database.transaction {
|
||||
fillUpForBuyer(false, issuer, AnonymousParty(bobsKey), notary)
|
||||
}.second
|
||||
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bankNode)
|
||||
|
||||
val alicesFakePaper = aliceNode.database.transaction {
|
||||
|
@ -15,11 +15,9 @@ import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.getTestPartyAndCertificate
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.After
|
||||
@ -41,6 +39,7 @@ class NotaryChangeTests {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setCordappPackages("net.corda.testing.contracts")
|
||||
mockNet = MockNetwork()
|
||||
oldNotaryNode = mockNet.createNode(
|
||||
legalName = DUMMY_NOTARY.name,
|
||||
@ -58,6 +57,7 @@ class NotaryChangeTests {
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -151,7 +151,7 @@ class NotaryChangeTests {
|
||||
}
|
||||
val stx = node.services.signInitialTransaction(tx)
|
||||
node.services.recordTransactions(stx)
|
||||
return tx.toWireTransaction()
|
||||
return tx.toWireTransaction(node.services)
|
||||
}
|
||||
|
||||
// TODO: Add more test cases once we have a general flow/service exception handling mechanism:
|
||||
@ -180,8 +180,7 @@ fun issueMultiPartyState(nodeA: StartedNode<*>, nodeB: StartedNode<*>, notaryNod
|
||||
val stx = notaryNode.services.addSignature(signedByAB, notaryIdentity.owningKey)
|
||||
nodeA.services.recordTransactions(stx)
|
||||
nodeB.services.recordTransactions(stx)
|
||||
val stateAndRef = StateAndRef(state, StateRef(stx.id, 0))
|
||||
return stateAndRef
|
||||
return StateAndRef(state, StateRef(stx.id, 0))
|
||||
}
|
||||
|
||||
fun issueInvalidState(node: StartedNode<*>, notary: Party): StateAndRef<*> {
|
||||
|
@ -13,6 +13,8 @@ import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.services.persistence.DBCheckpointStorage
|
||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||
@ -43,19 +45,19 @@ import java.util.concurrent.TimeUnit
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
val realClock: Clock = Clock.systemUTC()
|
||||
val stoppedClock: Clock = Clock.fixed(realClock.instant(), realClock.zone)
|
||||
val testClock = TestClock(stoppedClock)
|
||||
private val realClock: Clock = Clock.systemUTC()
|
||||
private val stoppedClock: Clock = Clock.fixed(realClock.instant(), realClock.zone)
|
||||
private val testClock = TestClock(stoppedClock)
|
||||
|
||||
val schedulerGatedExecutor = AffinityExecutor.Gate(true)
|
||||
private val schedulerGatedExecutor = AffinityExecutor.Gate(true)
|
||||
|
||||
lateinit var services: MockServiceHubInternal
|
||||
private lateinit var services: MockServiceHubInternal
|
||||
|
||||
lateinit var scheduler: NodeSchedulerService
|
||||
lateinit var smmExecutor: AffinityExecutor.ServiceAffinityExecutor
|
||||
lateinit var database: CordaPersistence
|
||||
lateinit var countDown: CountDownLatch
|
||||
lateinit var smmHasRemovedAllFlows: CountDownLatch
|
||||
private lateinit var scheduler: NodeSchedulerService
|
||||
private lateinit var smmExecutor: AffinityExecutor.ServiceAffinityExecutor
|
||||
private lateinit var database: CordaPersistence
|
||||
private lateinit var countDown: CountDownLatch
|
||||
private lateinit var smmHasRemovedAllFlows: CountDownLatch
|
||||
|
||||
var calls: Int = 0
|
||||
|
||||
@ -70,6 +72,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setCordappPackages("net.corda.testing.contracts")
|
||||
initialiseTestSerialization()
|
||||
countDown = CountDownLatch(1)
|
||||
smmHasRemovedAllFlows = CountDownLatch(1)
|
||||
@ -95,6 +98,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
network = mockMessagingService), TestReference {
|
||||
override val vaultService: VaultService = NodeVaultService(this)
|
||||
override val testReference = this@NodeSchedulerServiceTest
|
||||
override val cordappProvider: CordappProviderImpl = CordappProviderImpl(CordappLoader.createWithTestPackages()).start(attachments)
|
||||
}
|
||||
smmExecutor = AffinityExecutor.ServiceAffinityExecutor("test", 1)
|
||||
scheduler = NodeSchedulerService(services, schedulerGatedExecutor, serverThread = smmExecutor)
|
||||
@ -120,6 +124,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
||||
smmExecutor.awaitTermination(60, TimeUnit.SECONDS)
|
||||
database.close()
|
||||
resetTestSerialization()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant, val myIdentity: Party) : LinearState, SchedulableState {
|
||||
|
@ -19,14 +19,13 @@ import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.node.services.statemachine.StateMachineManager
|
||||
import net.corda.node.services.transactions.ValidatingNotaryService
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DUMMY_PROGRAM_ID
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
@ -34,7 +33,7 @@ import kotlin.test.assertEquals
|
||||
|
||||
class ScheduledFlowTests {
|
||||
companion object {
|
||||
val PAGE_SIZE = 20
|
||||
const val PAGE_SIZE = 20
|
||||
val SORTING = Sort(listOf(Sort.SortColumn(SortAttribute.Standard(Sort.CommonStateAttribute.STATE_REF_TXN_ID), Sort.Direction.DESC)))
|
||||
}
|
||||
|
||||
@ -97,6 +96,7 @@ class ScheduledFlowTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setCordappPackages("net.corda.testing.contracts")
|
||||
mockNet = MockNetwork(threadPerNode = true)
|
||||
notaryNode = mockNet.createNode(
|
||||
legalName = DUMMY_NOTARY.name,
|
||||
@ -114,6 +114,7 @@ class ScheduledFlowTests {
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -135,7 +136,7 @@ class ScheduledFlowTests {
|
||||
nodeB.services.vaultQueryService.queryBy<ScheduledState>().states.single()
|
||||
}
|
||||
assertEquals(1, countScheduledFlows)
|
||||
assertEquals(stateFromA, stateFromB, "Must be same copy on both nodes")
|
||||
assertEquals("Must be same copy on both nodes", stateFromA, stateFromB)
|
||||
assertTrue("Must be processed", stateFromB.state.data.processed)
|
||||
}
|
||||
|
||||
@ -159,7 +160,7 @@ class ScheduledFlowTests {
|
||||
val statesFromB: List<StateAndRef<ScheduledState>> = nodeB.database.transaction {
|
||||
queryStatesWithPaging(nodeB.services.vaultQueryService)
|
||||
}
|
||||
assertEquals(2 * N, statesFromA.count(), "Expect all states to be present")
|
||||
assertEquals("Expect all states to be present",2 * N, statesFromA.count())
|
||||
statesFromA.forEach { ref ->
|
||||
if (ref !in statesFromB) {
|
||||
throw IllegalStateException("State $ref is only present on node A.")
|
||||
@ -170,7 +171,7 @@ class ScheduledFlowTests {
|
||||
throw IllegalStateException("State $ref is only present on node B.")
|
||||
}
|
||||
}
|
||||
assertEquals(statesFromA, statesFromB, "Expect identical data on both nodes")
|
||||
assertEquals("Expect identical data on both nodes", statesFromA, statesFromB)
|
||||
assertTrue("Expect all states have run the scheduled task", statesFromB.all { it.state.data.processed })
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setCordappPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset")
|
||||
issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY, BOB_KEY, BOC_KEY)
|
||||
val dataSourceProps = makeTestDataSourceProperties()
|
||||
val defaultDatabaseProperties = makeTestDatabaseProperties()
|
||||
@ -105,6 +106,7 @@ class HibernateConfigurationTest : TestDependencyInjectionBase() {
|
||||
@After
|
||||
fun cleanUp() {
|
||||
database.close()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
private fun setUpDb() {
|
||||
|
@ -19,6 +19,7 @@ import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestIdentityService
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
@ -77,6 +78,7 @@ class NodeAttachmentStorageTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("We need to be able to restart nodes - make importing attachments idempotent?")
|
||||
@Test
|
||||
fun `duplicates not allowed`() {
|
||||
val testJar = makeTestJar()
|
||||
|
@ -66,7 +66,7 @@ class FlowFrameworkTests {
|
||||
}
|
||||
}
|
||||
|
||||
private val mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin())
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private val receivedSessionMessages = ArrayList<SessionTransfer>()
|
||||
private lateinit var node1: StartedNode<MockNode>
|
||||
private lateinit var node2: StartedNode<MockNode>
|
||||
@ -77,6 +77,8 @@ class FlowFrameworkTests {
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
setCordappPackages("net.corda.finance.contracts", "net.corda.testing.contracts")
|
||||
mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin())
|
||||
node1 = mockNet.createNode(advertisedServices = ServiceInfo(NetworkMapService.type))
|
||||
node2 = mockNet.createNode(networkMapAddress = node1.network.myAddress)
|
||||
|
||||
@ -105,6 +107,7 @@ class FlowFrameworkTests {
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
receivedSessionMessages.clear()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -15,10 +15,8 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.node.internal.StartedNode
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.getDefaultNotary
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -38,6 +36,7 @@ class NotaryServiceTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setCordappPackages("net.corda.testing.contracts")
|
||||
mockNet = MockNetwork()
|
||||
notaryNode = mockNet.createNode(
|
||||
legalName = DUMMY_NOTARY.name,
|
||||
@ -51,6 +50,7 @@ class NotaryServiceTests {
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -16,9 +16,7 @@ import net.corda.node.internal.StartedNode
|
||||
import net.corda.nodeapi.internal.ServiceInfo
|
||||
import net.corda.node.services.issueInvalidState
|
||||
import net.corda.node.services.network.NetworkMapService
|
||||
import net.corda.testing.DUMMY_NOTARY
|
||||
import net.corda.testing.MEGA_CORP_KEY
|
||||
import net.corda.testing.chooseIdentity
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.dummyCommand
|
||||
import net.corda.testing.getDefaultNotary
|
||||
@ -39,6 +37,7 @@ class ValidatingNotaryServiceTests {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
setCordappPackages("net.corda.testing.contracts")
|
||||
mockNet = MockNetwork()
|
||||
notaryNode = mockNet.createNode(
|
||||
legalName = DUMMY_NOTARY.name,
|
||||
@ -53,6 +52,7 @@ class ValidatingNotaryServiceTests {
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -53,6 +53,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setCordappPackages("net.corda.finance.contracts.asset")
|
||||
LogHelper.setLevel(NodeVaultService::class)
|
||||
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(BOC_KEY, DUMMY_CASH_ISSUER_KEY),
|
||||
customSchemas = setOf(CashSchemaV1))
|
||||
@ -65,6 +66,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
fun tearDown() {
|
||||
database.close()
|
||||
LogHelper.reset(NodeVaultService::class)
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
@ -509,7 +511,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
val issueTx = TransactionBuilder(services.myInfo.chooseIdentity()).apply {
|
||||
Cash().generateIssue(this,
|
||||
amount, anonymousIdentity.party, services.myInfo.chooseIdentity())
|
||||
}.toWireTransaction()
|
||||
}.toWireTransaction(services)
|
||||
val cashState = StateAndRef(issueTx.outputs.single(), StateRef(issueTx.id, 0))
|
||||
|
||||
database.transaction { service.notify(issueTx) }
|
||||
@ -518,7 +520,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
database.transaction {
|
||||
val moveTx = TransactionBuilder(services.myInfo.chooseIdentity()).apply {
|
||||
Cash.generateSpend(services, this, Amount(1000, GBP), thirdPartyIdentity)
|
||||
}.toWireTransaction()
|
||||
}.toWireTransaction(services)
|
||||
service.notify(moveTx)
|
||||
}
|
||||
val expectedMoveUpdate = Vault.Update(setOf(cashState), emptySet(), null)
|
||||
@ -563,7 +565,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
val moveTx = database.transaction {
|
||||
TransactionBuilder(newNotary).apply {
|
||||
Cash.generateSpend(services, this, Amount(1000, GBP), thirdPartyIdentity)
|
||||
}.toWireTransaction()
|
||||
}.toWireTransaction(services)
|
||||
}
|
||||
|
||||
database.transaction {
|
||||
|
@ -46,20 +46,22 @@ import java.util.*
|
||||
|
||||
class VaultQueryTests : TestDependencyInjectionBase() {
|
||||
|
||||
lateinit var services: MockServices
|
||||
lateinit var notaryServices: MockServices
|
||||
val vaultSvc: VaultService get() = services.vaultService
|
||||
val vaultQuerySvc: VaultQueryService get() = services.vaultQueryService
|
||||
val identitySvc: IdentityService = makeTestIdentityService()
|
||||
lateinit var database: CordaPersistence
|
||||
private lateinit var services: MockServices
|
||||
private lateinit var notaryServices: MockServices
|
||||
private val vaultSvc: VaultService get() = services.vaultService
|
||||
private val vaultQuerySvc: VaultQueryService get() = services.vaultQueryService
|
||||
private val identitySvc: IdentityService = makeTestIdentityService()
|
||||
private lateinit var database: CordaPersistence
|
||||
|
||||
// test cash notary
|
||||
val CASH_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(21)) }
|
||||
val CASH_NOTARY: Party get() = Party(CordaX500Name(organisation = "Cash Notary Service", locality = "Zurich", country = "CH"), CASH_NOTARY_KEY.public)
|
||||
val CASH_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CASH_NOTARY.nameOrNull(), CASH_NOTARY_KEY.public)
|
||||
private val CASH_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(21)) }
|
||||
private val CASH_NOTARY: Party get() = Party(CordaX500Name(organisation = "Cash Notary Service", locality = "Zurich", country = "CH"), CASH_NOTARY_KEY.public)
|
||||
private val CASH_NOTARY_IDENTITY: PartyAndCertificate get() = getTestPartyAndCertificate(CASH_NOTARY.nameOrNull(), CASH_NOTARY_KEY.public)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setCordappPackages("net.corda.testing.contracts", "net.corda.finance.contracts")
|
||||
|
||||
// register additional identities
|
||||
identitySvc.verifyAndRegisterIdentity(CASH_NOTARY_IDENTITY)
|
||||
identitySvc.verifyAndRegisterIdentity(BOC_IDENTITY)
|
||||
@ -74,6 +76,7 @@ class VaultQueryTests : TestDependencyInjectionBase() {
|
||||
@After
|
||||
fun tearDown() {
|
||||
database.close()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1100,12 +1103,12 @@ class VaultQueryTests : TestDependencyInjectionBase() {
|
||||
val states = result.states
|
||||
val metadata = result.statesMetadata
|
||||
|
||||
for (i in 0..states.size - 1) {
|
||||
for (i in 0 until states.size) {
|
||||
println("${states[i].ref} : ${metadata[i].contractStateClassName}, ${metadata[i].status}, ${metadata[i].consumedTime}")
|
||||
}
|
||||
|
||||
assertThat(states).hasSize(20)
|
||||
assertThat(metadata.first().contractStateClassName).isEqualTo("net.corda.testing.contracts.DummyLinearContract\$State")
|
||||
assertThat(metadata.first().contractStateClassName).isEqualTo("$DUMMY_LINEAR_CONTRACT_PROGRAM_ID\$State")
|
||||
assertThat(metadata.first().status).isEqualTo(Vault.StateStatus.UNCONSUMED) // 0 = UNCONSUMED
|
||||
assertThat(metadata.last().contractStateClassName).isEqualTo("net.corda.finance.contracts.asset.Cash\$State")
|
||||
assertThat(metadata.last().status).isEqualTo(Vault.StateStatus.CONSUMED) // 1 = CONSUMED
|
||||
|
@ -40,22 +40,26 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
|
||||
val vault: VaultService get() = services.vaultService
|
||||
val vaultQuery: VaultQueryService get() = services.vaultQueryService
|
||||
lateinit var database: CordaPersistence
|
||||
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
|
||||
lateinit var notaryServices: MockServices
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setCordappPackages("net.corda.testing.contracts", "net.corda.finance.contracts.asset")
|
||||
|
||||
LogHelper.setLevel(VaultWithCashTest::class)
|
||||
val databaseAndServices = makeTestDatabaseAndMockServices(keys = listOf(DUMMY_CASH_ISSUER_KEY, DUMMY_NOTARY_KEY),
|
||||
customSchemas = setOf(CashSchemaV1))
|
||||
database = databaseAndServices.first
|
||||
services = databaseAndServices.second
|
||||
issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY, MEGA_CORP_KEY)
|
||||
notaryServices = MockServices(DUMMY_NOTARY_KEY)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
LogHelper.reset(VaultWithCashTest::class)
|
||||
database.close()
|
||||
unsetCordappPackages()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user