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:
Clinton
2017-09-25 17:05:18 +01:00
committed by josecoll
parent 2a7da1eb47
commit 532bbb5cca
116 changed files with 1601 additions and 599 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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