Merge pull request #1379 from corda/os-merge-d56a80d

O/S merge d56a80d
This commit is contained in:
Shams Asari 2018-09-10 13:23:22 +01:00 committed by GitHub
commit 4ffe2960db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 260 additions and 305 deletions

View File

@ -369,6 +369,7 @@ bintrayConfig {
'corda-rpc',
'corda-core',
'corda-core-deterministic',
'corda-deterministic-verifier',
'corda-djvm',
'corda',
'corda-finance',

View File

@ -11,8 +11,8 @@ def javaHome = System.getProperty('java.home')
def jarBaseName = "corda-${project.name}".toString()
configurations {
runtimeLibraries
runtimeArtifacts.extendsFrom runtimeLibraries
deterministicLibraries
deterministicArtifacts.extendsFrom deterministicLibraries
}
dependencies {
@ -20,14 +20,14 @@ dependencies {
// Configure these by hand. It should be a minimal subset of core's dependencies,
// and without any obviously non-deterministic ones such as Hibernate.
runtimeLibraries "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
runtimeLibraries "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
runtimeLibraries "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final"
runtimeLibraries "org.bouncycastle:bcprov-jdk15on:$bouncycastle_version"
runtimeLibraries "org.bouncycastle:bcpkix-jdk15on:$bouncycastle_version"
runtimeLibraries "com.google.code.findbugs:jsr305:$jsr305_version"
runtimeLibraries "net.i2p.crypto:eddsa:$eddsa_version"
runtimeLibraries "org.slf4j:slf4j-api:$slf4j_version"
deterministicLibraries "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
deterministicLibraries "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
deterministicLibraries "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final"
deterministicLibraries "org.bouncycastle:bcprov-jdk15on:$bouncycastle_version"
deterministicLibraries "org.bouncycastle:bcpkix-jdk15on:$bouncycastle_version"
deterministicLibraries "com.google.code.findbugs:jsr305:$jsr305_version"
deterministicLibraries "net.i2p.crypto:eddsa:$eddsa_version"
deterministicLibraries "org.slf4j:slf4j-api:$slf4j_version"
}
jar {
@ -115,7 +115,7 @@ task determinise(type: ProGuardTask) {
libraryjars file("$javaHome/lib/rt.jar")
libraryjars file("$javaHome/lib/jce.jar")
configurations.runtimeLibraries.forEach {
configurations.deterministicLibraries.forEach {
libraryjars it, filter: '!META-INF/versions/**'
}
@ -155,7 +155,7 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
libraryjars deterministic_rt_jar
configurations.runtimeLibraries.forEach {
configurations.deterministicLibraries.forEach {
libraryjars it, filter: '!META-INF/versions/**'
}
@ -176,12 +176,12 @@ assemble.dependsOn checkDeterminism
def deterministicJar = metafix.outputs.files.singleFile
artifacts {
runtimeArtifacts file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
deterministicArtifacts file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
publish file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
}
publish {
dependenciesFrom configurations.runtimeArtifacts
dependenciesFrom configurations.deterministicArtifacts
publishSources = false
publishJavadoc = false
name jarBaseName

View File

@ -1,10 +1,10 @@
apply plugin: 'kotlin'
dependencies {
testCompile project(path: ':core-deterministic', configuration: 'runtimeArtifacts')
testCompile project(path: ':serialization-deterministic', configuration: 'runtimeArtifacts')
testCompile project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
testCompile project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
testCompile project(path: ':core-deterministic:testing:verifier', configuration: 'deterministicArtifacts')
testCompile project(path: ':core-deterministic:testing:data', configuration: 'testData')
testCompile project(':core-deterministic:testing:common')
testCompile(project(':finance')) {
transitive = false
}

View File

@ -1,16 +0,0 @@
apply from: '../../../deterministic.gradle'
apply plugin: 'idea'
dependencies {
compileOnly project(path: ':core-deterministic', configuration: 'runtimeArtifacts')
compileOnly project(path: ':serialization-deterministic', configuration: 'runtimeArtifacts')
compileOnly "junit:junit:$junit_version"
}
idea {
module {
if (project.hasProperty("deterministic_idea_sdk")) {
jdkName project.property("deterministic_idea_sdk") as String
}
}
}

View File

@ -8,7 +8,7 @@ dependencies {
testCompile project(':core')
testCompile project(':finance')
testCompile project(':node-driver')
testCompile project(':core-deterministic:testing:common')
testCompile project(path: ':core-deterministic:testing:verifier', configuration: 'runtimeArtifacts')
testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile "org.jetbrains.kotlin:kotlin-reflect"

View File

@ -1,8 +1,8 @@
package net.corda.deterministic.data
import net.corda.core.serialization.deserialize
import net.corda.deterministic.common.LocalSerializationRule
import net.corda.deterministic.common.TransactionVerificationRequest
import net.corda.deterministic.verifier.LocalSerializationRule
import net.corda.deterministic.verifier.TransactionVerificationRequest
import org.junit.Before
import org.junit.Rule
import org.junit.Test

View File

@ -7,9 +7,9 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.serialization.serialize
import net.corda.deterministic.common.MockContractAttachment
import net.corda.deterministic.common.SampleCommandData
import net.corda.deterministic.common.TransactionVerificationRequest
import net.corda.deterministic.verifier.MockContractAttachment
import net.corda.deterministic.verifier.SampleCommandData
import net.corda.deterministic.verifier.TransactionVerificationRequest
import net.corda.finance.POUNDS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash.*

View File

@ -3,7 +3,7 @@ package net.corda.deterministic.crypto
import net.corda.core.crypto.*
import net.corda.deterministic.KeyStoreProvider
import net.corda.deterministic.CheatingSecurityProvider
import net.corda.deterministic.common.LocalSerializationRule
import net.corda.deterministic.verifier.LocalSerializationRule
import org.junit.*
import org.junit.rules.RuleChain
import java.security.*

View File

@ -1,29 +1,29 @@
package net.corda.deterministic.txverify
import net.corda.deterministic.bytesOfResource
import net.corda.deterministic.common.LocalSerializationRule
import net.corda.deterministic.common.verifyInEnclave
import net.corda.deterministic.verifier.LocalSerializationRule
import net.corda.deterministic.verifier.verifyTransaction
import net.corda.finance.contracts.asset.Cash.Commands.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.ClassRule
import org.junit.Test
import kotlin.test.assertFailsWith
class EnclaveletTest {
class VerifyTransactionTest {
companion object {
@ClassRule
@JvmField
val serialization = LocalSerializationRule(EnclaveletTest::class)
val serialization = LocalSerializationRule(VerifyTransactionTest::class)
}
@Test
fun success() {
verifyInEnclave(bytesOfResource("txverify/tx-success.bin"))
verifyTransaction(bytesOfResource("txverify/tx-success.bin"))
}
@Test
fun failure() {
val e = assertFailsWith<Exception> { verifyInEnclave(bytesOfResource("txverify/tx-failure.bin")) }
val e = assertFailsWith<Exception> { verifyTransaction(bytesOfResource("txverify/tx-failure.bin")) }
assertThat(e).hasMessageContaining("Required ${Move::class.java.canonicalName} command")
}
}

View File

@ -0,0 +1,48 @@
apply plugin: 'java-library'
apply from: '../../../deterministic.gradle'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'idea'
description 'Test utilities for deterministic contract verification'
configurations {
deterministicArtifacts
runtimeArtifacts.extendsFrom api
}
dependencies {
deterministicArtifacts project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts')
deterministicArtifacts project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
runtimeArtifacts project(':serialization')
runtimeArtifacts project(':core')
// Compile against the deterministic artifacts to ensure that we use only the deterministic API subset.
compileOnly configurations.deterministicArtifacts
api "junit:junit:$junit_version"
}
jar {
baseName 'corda-deterministic-verifier'
}
artifacts {
deterministicArtifacts jar
runtimeArtifacts jar
publish jar
}
publish {
// Our published POM will contain dependencies on the non-deterministic Corda artifacts.
dependenciesFrom configurations.runtimeArtifacts
name jar.baseName
}
idea {
module {
if (project.hasProperty("deterministic_idea_sdk")) {
jdkName project.property("deterministic_idea_sdk") as String
}
}
}

View File

@ -1,4 +1,4 @@
package net.corda.deterministic.common
package net.corda.deterministic.verifier
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext
@ -83,4 +83,4 @@ class LocalSerializationRule(private val label: String) : TestRule {
return canDeserializeVersion(magic) && target == P2P
}
}
}
}

View File

@ -1,4 +1,4 @@
package net.corda.deterministic.common
package net.corda.deterministic.verifier
import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractClassName

View File

@ -1,5 +1,5 @@
@file:JvmName("SampleData")
package net.corda.deterministic.common
package net.corda.deterministic.verifier
import net.corda.core.contracts.TypeOnlyCommandData

View File

@ -1,4 +1,4 @@
package net.corda.deterministic.common
package net.corda.deterministic.verifier
import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment

View File

@ -1,5 +1,5 @@
@file:JvmName("Enclavelet")
package net.corda.deterministic.common
@file:JvmName("Verifier")
package net.corda.deterministic.verifier
import net.corda.core.serialization.deserialize
import net.corda.core.transactions.LedgerTransaction
@ -11,7 +11,7 @@ import net.corda.core.transactions.LedgerTransaction
* TODO: Transaction data is meant to be encrypted under an enclave-private key.
*/
@Throws(Exception::class)
fun verifyInEnclave(reqBytes: ByteArray) {
fun verifyTransaction(reqBytes: ByteArray) {
deserialize(reqBytes).verify()
}

View File

@ -47,22 +47,5 @@ interface Cordapp {
val allFlows: List<Class<out FlowLogic<*>>>
val jarPath: URL
val cordappClasses: List<String>
val info: Info
val jarHash: SecureHash.SHA256
/**
* CorDapp's information, including vendor and version.
*
* @property shortName Cordapp's shortName
* @property vendor Cordapp's vendor
* @property version Cordapp's version
*/
@DoNotImplement
interface Info {
val shortName: String
val vendor: String
val version: String
fun hasUnknownFields(): Boolean
}
}
}

View File

@ -24,26 +24,28 @@ data class CordappImpl(
override val customSchemas: Set<MappedSchema>,
override val allFlows: List<Class<out FlowLogic<*>>>,
override val jarPath: URL,
override val info: Cordapp.Info = CordappImpl.Info.UNKNOWN,
override val jarHash: SecureHash.SHA256,
override val name: String = jarPath.toPath().fileName.toString().removeSuffix(".jar") ) : Cordapp {
val info: Info,
override val jarHash: SecureHash.SHA256) : Cordapp {
override val name: String = jarName(jarPath)
companion object {
fun jarName(url: URL): String = url.toPath().fileName.toString().removeSuffix(".jar")
}
/**
* An exhaustive list of all classes relevant to the node within this CorDapp
*
* TODO: Also add [SchedulableFlow] as a Cordapp class
*/
override val cordappClasses = ((rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames)
override val cordappClasses: List<String> = (rpcFlows + initiatedFlows + services + serializationWhitelists.map { javaClass }).map { it.name } + contractClassNames
data class Info(override val shortName: String, override val vendor: String, override val version: String): Cordapp.Info {
// TODO Why a seperate Info class and not just have the fields directly in CordappImpl?
data class Info(val shortName: String, val vendor: String, val version: String) {
companion object {
private const val UNKNOWN_VALUE = "Unknown"
val UNKNOWN = Info(UNKNOWN_VALUE, UNKNOWN_VALUE, UNKNOWN_VALUE)
}
override fun hasUnknownFields(): Boolean {
return setOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
}
fun hasUnknownFields(): Boolean = arrayOf(shortName, vendor, version).any { it == UNKNOWN_VALUE }
}
}

View File

@ -49,6 +49,5 @@ artifacts {
publish {
dependenciesFrom configurations.shadow
disableDefaultJar true
name shadowJar.baseName
}

View File

@ -119,7 +119,7 @@ class FlowWorkerServiceHub(override val configuration: NodeConfiguration, overri
identityService.database = database
}
override val networkMapCache = PersistentNetworkMapCache(database, identityService, myInfo.legalIdentities[0].name)
override val networkMapCache = PersistentNetworkMapCache(database, identityService)
private val checkpointStorage = DBCheckpointStorage()
@Suppress("LeakingThis")
override val validatedTransactions: WritableTransactionStorage = DBTransactionStorage(configuration.transactionCacheSizeBytes, database).tokenize()

View File

@ -36,7 +36,10 @@ import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.identity.PersistentIdentityService
import net.corda.node.services.keys.PersistentKeyManagementService
import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.network.*
import net.corda.node.services.network.NetworkMapClient
import net.corda.node.services.network.NetworkMapUpdater
import net.corda.node.services.network.NodeInfoWatcher
import net.corda.node.services.network.PersistentNetworkMapCache
import net.corda.node.services.persistence.DBTransactionMappingStorage
import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.NodeAttachmentService
@ -84,7 +87,7 @@ class RpcWorkerServiceHub(override val configuration: NodeConfiguration, overrid
identityService.database = database
}
override val networkMapCache = PersistentNetworkMapCache(database, identityService, myInfo.legalIdentities[0].name)
override val networkMapCache = PersistentNetworkMapCache(database, identityService)
@Suppress("LeakingThis")
override val validatedTransactions: WritableTransactionStorage = DBTransactionStorage(configuration.transactionCacheSizeBytes, database)
private val networkMapClient: NetworkMapClient? = configuration.networkServices?.let { NetworkMapClient(it.networkMapURL, versionInfo) }

View File

@ -75,19 +75,17 @@ jar {
manifest {
attributes(
"Manifest-Version": "1.0",
"Name": "net/corda/finance",
"Specification-Title": description,
"Specification-Version": version,
"Specification-Vendor": "R3",
"Implementation-Title": "$group.$baseName",
"Implementation-Version": version,
"Implementation-Vendor": "R3"
)
}
}
cordapp {
info {
name "net/corda/finance"
vendor "R3"
}
}

View File

@ -93,7 +93,7 @@ class ArtemisMessagingTest {
}
LogHelper.setLevel(PersistentUniquenessProvider::class)
database = configureDatabase(makeInternalTestDataSourceProperties(configSupplier = { ConfigFactory.empty() }), DatabaseConfig(runMigration = true), { null }, { null })
networkMapCache = PersistentNetworkMapCache(database, rigorousMock(), ALICE_NAME).apply { start(emptyList()) }
networkMapCache = PersistentNetworkMapCache(database, rigorousMock()).apply { start(emptyList()) }
}
@After

View File

@ -1,13 +1,12 @@
package net.corda.node.services.network
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.configureDatabase
import net.corda.node.internal.schemas.NodeInfoSchemaV1
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.nodeapi.internal.DEV_ROOT_CA
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.core.*
import net.corda.testing.internal.IntegrationTest
@ -23,7 +22,6 @@ class PersistentNetworkMapCacheTest : IntegrationTest() {
private companion object {
val ALICE = TestIdentity(ALICE_NAME, 70)
val BOB = TestIdentity(BOB_NAME, 80)
val CHARLIE = TestIdentity(CHARLIE_NAME, 90)
@ClassRule
@JvmField
@ -38,14 +36,14 @@ class PersistentNetworkMapCacheTest : IntegrationTest() {
//Enterprise only - objects created in the setup method, below initialized with dummy values to avoid need for nullable type declaration
private var database = CordaPersistence(DatabaseConfig(), emptySet())
private var charlieNetMapCache = PersistentNetworkMapCache(database, InMemoryIdentityService(trustRoot = DEV_ROOT_CA.certificate), CHARLIE.name)
private var charlieNetMapCache = PersistentNetworkMapCache(database, InMemoryIdentityService(trustRoot = DEV_ROOT_CA.certificate))
@Before()
fun setup() {
//Enterprise only - for test in database mode ensure the remote database is setup before creating CordaPersistence
super.setUp()
database = configureDatabase(makeTestDataSourceProperties(CHARLIE_NAME.toDatabaseSchemaName()), makeTestDatabaseProperties(CHARLIE_NAME.toDatabaseSchemaName()), { null }, { null })
charlieNetMapCache = PersistentNetworkMapCache(database, InMemoryIdentityService(trustRoot = DEV_ROOT_CA.certificate), CHARLIE.name)
charlieNetMapCache = PersistentNetworkMapCache(database, InMemoryIdentityService(trustRoot = DEV_ROOT_CA.certificate))
}
@After
@ -57,7 +55,6 @@ class PersistentNetworkMapCacheTest : IntegrationTest() {
fun addNode() {
val alice = createNodeInfo(listOf(ALICE))
charlieNetMapCache.addNode(alice)
assertThat(charlieNetMapCache.nodeReady).isDone()
val fromDb = database.transaction {
session.createQuery(
"from ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name}",
@ -67,32 +64,6 @@ class PersistentNetworkMapCacheTest : IntegrationTest() {
assertThat(fromDb).containsOnly(alice)
}
@Test
fun `adding the node's own node-info doesn't complete the nodeReady future`() {
val charlie = createNodeInfo(listOf(CHARLIE))
charlieNetMapCache.addNode(charlie)
assertThat(charlieNetMapCache.nodeReady).isNotDone()
assertThat(charlieNetMapCache.getNodeByLegalName(CHARLIE.name)).isEqualTo(charlie)
}
@Test
fun `starting with just the node's own node-info in the db`() {
val charlie = createNodeInfo(listOf(CHARLIE))
saveNodeInfoIntoDb(charlie)
assertThat(charlieNetMapCache.allNodes).containsOnly(charlie)
charlieNetMapCache.start(emptyList())
assertThat(charlieNetMapCache.nodeReady).isNotDone()
}
@Test
fun `starting with another node-info in the db`() {
val alice = createNodeInfo(listOf(ALICE))
saveNodeInfoIntoDb(alice)
assertThat(charlieNetMapCache.allNodes).containsOnly(alice)
charlieNetMapCache.start(emptyList())
assertThat(charlieNetMapCache.nodeReady).isDone()
}
@Test
fun `unknown legal name`() {
charlieNetMapCache.addNode(createNodeInfo(listOf(ALICE)))
@ -154,19 +125,4 @@ class PersistentNetworkMapCacheTest : IntegrationTest() {
serial = 1
)
}
private fun saveNodeInfoIntoDb(nodeInfo: NodeInfo) {
database.transaction {
session.save(NodeInfoSchemaV1.PersistentNodeInfo(
id = 0,
hash = nodeInfo.serialize().hash.toString(),
addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
},
platformVersion = nodeInfo.platformVersion,
serial = nodeInfo.serial
))
}
}
}

View File

@ -2,6 +2,7 @@ package net.corda.node.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.schemas.MappedSchema
/**
@ -12,7 +13,7 @@ interface CordappLoader {
/**
* Returns all [Cordapp]s found.
*/
val cordapps: List<Cordapp>
val cordapps: List<CordappImpl>
/**
* Returns a [ClassLoader] containing all types from all [Cordapp]s.

View File

@ -150,7 +150,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
// TODO Break cyclic dependency
identityService.database = database
}
val networkMapCache = PersistentNetworkMapCache(database, identityService, configuration.myLegalName).tokenize()
val networkMapCache = PersistentNetworkMapCache(database, identityService).tokenize()
val checkpointStorage = DBCheckpointStorage()
@Suppress("LeakingThis")
val transactionStorage = makeTransactionStorage(configuration.transactionCacheSizeBytes).tokenize()
@ -221,7 +221,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
/** Set to non-null once [start] has been successfully called. */
open val started get() = _started
open val started: S? get() = _started
@Volatile
private var _started: S? = null
@ -311,6 +311,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
"Node's platform version is lower than network's required minimumPlatformVersion"
}
servicesForResolution.start(netParams)
networkMapCache.start(netParams.notaries)
startDatabase()
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
@ -332,7 +333,6 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
val (keyPairs, nodeInfoAndSigned, myNotaryIdentity) = database.transaction {
networkMapCache.start(netParams.notaries)
updateNodeInfo(identity, identityKeyPair, publish = true)
}
@ -480,7 +480,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
} else {
1.days
}
val executor = Executors.newSingleThreadScheduledExecutor(NamedThreadFactory("Network Map Updater", Executors.defaultThreadFactory()))
val executor = Executors.newSingleThreadScheduledExecutor(NamedThreadFactory("Network Map Updater"))
executor.submit(object : Runnable {
override fun run() {
val republishInterval = try {

View File

@ -137,6 +137,7 @@ open class Node(configuration: NodeConfiguration,
return JarScanningCordappLoader.fromDirectories(configuration.cordappDirectories, versionInfo)
}
// TODO: make this configurable.
const val MAX_RPC_MESSAGE_SIZE = 10485760
}

View File

@ -11,6 +11,7 @@ import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.Crypto
import net.corda.core.internal.*
import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.internal.errors.AddressBindingException
import net.corda.core.utilities.Try
import net.corda.core.utilities.loggerFor
@ -329,7 +330,6 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") {
val nodeInfo = node.start()
logLoadedCorDapps(node.services.cordappProvider.cordapps)
Node.printBasicNodeInfo("Loaded CorDapps", node.services.cordappProvider.cordapps.joinToString { it.name })
node.nodeReadyFuture.thenMatch({
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
@ -424,10 +424,10 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") {
)
}
open protected fun logLoadedCorDapps(corDapps: List<Cordapp>) {
fun Cordapp.Info.description() = "$shortName version $version by $vendor"
open protected fun logLoadedCorDapps(corDapps: List<CordappImpl>) {
fun CordappImpl.Info.description() = "$shortName version $version by $vendor"
Node.printBasicNodeInfo("Loaded ${corDapps.size} CorDapp(s)", corDapps.map { it.info }.joinToString(", ", transform = Cordapp.Info::description))
Node.printBasicNodeInfo("Loaded ${corDapps.size} CorDapp(s)", corDapps.map { it.info }.joinToString(", ", transform = CordappImpl.Info::description))
corDapps.map { it.info }.filter { it.hasUnknownFields() }.let { malformed ->
if (malformed.isNotEmpty()) {
logger.warn("Found ${malformed.size} CorDapp(s) with unknown information. They will be unable to run on Corda in the future.")

View File

@ -8,6 +8,7 @@ import net.corda.core.cordapp.CordappContext
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.internal.createCordappContext
import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage
@ -34,7 +35,7 @@ open class CordappProviderImpl(private val cordappLoader: CordappLoader,
/**
* Current known CorDapps loaded on this node
*/
override val cordapps get() = cordappLoader.cordapps
override val cordapps: List<CordappImpl> get() = cordappLoader.cordapps
fun start(whitelistedContractImplementations: Map<String, List<AttachmentId>>) {
cordappAttachments.putAll(loadContractsIntoAttachmentStore())

View File

@ -3,8 +3,9 @@ package net.corda.node.internal.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappProvider
import net.corda.core.flows.FlowLogic
import net.corda.core.internal.cordapp.CordappImpl
interface CordappProviderInternal : CordappProvider {
val cordapps: List<Cordapp>
val cordapps: List<CordappImpl>
fun getCordappForFlow(flowLogic: FlowLogic<*>): Cordapp?
}

View File

@ -9,12 +9,12 @@ import net.corda.core.flows.*
import net.corda.core.internal.*
import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.node.services.CordaService
import net.corda.node.VersionInfo
import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.utilities.contextLogger
import net.corda.node.VersionInfo
import net.corda.node.cordapp.CordappLoader
import net.corda.node.internal.classloading.requireAnnotation
import net.corda.nodeapi.internal.coreContractClasses
@ -36,7 +36,7 @@ import kotlin.streams.toList
*/
class JarScanningCordappLoader private constructor(private val cordappJarPaths: List<RestrictedURL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN) : CordappLoaderTemplate() {
override val cordapps: List<Cordapp> by lazy { loadCordapps() + coreCordapp }
override val cordapps: List<CordappImpl> by lazy { loadCordapps() + coreCordapp }
override val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader)
@ -57,7 +57,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
* @param corDappDirectories Directories used to scan for CorDapp JARs.
*/
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
return JarScanningCordappLoader(corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }, versionInfo)
}
@ -67,7 +66,9 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
*
* @param scanJars Uses the JAR URLs provided for classpath scanning and Cordapp detection.
*/
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN) = JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo)
fun fromJarUrls(scanJars: List<URL>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): JarScanningCordappLoader {
return JarScanningCordappLoader(scanJars.map { it.restricted() }, versionInfo)
}
private fun URL.restricted(rootPackageName: String? = null) = RestrictedURL(this, rootPackageName)
@ -108,14 +109,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
jarHash = SecureHash.allOnesHash
)
private fun loadCordapps(): List<Cordapp> {
return cordappJarPaths.map { scanCordapp(it).toCordapp(it) }
}
private fun loadCordapps(): List<CordappImpl> = cordappJarPaths.map { scanCordapp(it).toCordapp(it) }
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): Cordapp {
val name = url.url.toPath().fileName.toString().removeSuffix(".jar")
val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(name)
private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl {
val info = url.url.openStream().let(::JarInputStream).use { it.manifest }.toCordappInfo(CordappImpl.jarName(url.url))
return CordappImpl(
findContractClassNames(this),
findInitiatedFlows(this),
@ -129,8 +126,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
findAllFlows(this),
url.url,
info,
getJarHash(url.url),
name
getJarHash(url.url)
)
}
@ -275,24 +271,23 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
class MultipleCordappsForFlowException(message: String) : Exception(message)
abstract class CordappLoaderTemplate : CordappLoader {
override val flowCordappMap: Map<Class<out FlowLogic<*>>, Cordapp> by lazy {
cordapps.flatMap { corDapp -> corDapp.allFlows.map { flow -> flow to corDapp } }
.groupBy { it.first }
.mapValues {
if(it.value.size > 1) { throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow ${it.value.first().first.name}: [ ${it.value.joinToString { it.second.name }} ].") }
it.value.single().second
.mapValues { entry ->
if (entry.value.size > 1) {
throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow " +
"${entry.value.first().first.name}: [ ${entry.value.joinToString { it.second.name }} ].")
}
entry.value.single().second
}
}
override val cordappSchemas: Set<MappedSchema> by lazy {
cordapps.flatMap { it.customSchemas }.toSet()
}
override val appClassLoader: ClassLoader by lazy {
URLClassLoader(cordapps.stream().map { it.jarPath }.toTypedArray(), javaClass.classLoader)
}
}

View File

@ -1,13 +1,10 @@
package net.corda.node.internal.cordapp
import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.cordapp.CordappImpl
import java.util.*
import java.util.jar.Attributes
import java.util.jar.Manifest
fun createTestManifest(name: String, title: String, version: String, vendor: String): Manifest {
val manifest = Manifest()
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
@ -26,27 +23,20 @@ fun createTestManifest(name: String, title: String, version: String, vendor: Str
return manifest
}
internal fun createTestManifest(name: String, title: String, jarUUID: UUID): Manifest {
return createTestManifest(name, title, "test-$jarUUID", "R3")
}
operator fun Manifest.set(key: String, value: String) {
mainAttributes.putValue(key, value)
}
internal fun Manifest?.toCordappInfo(defaultShortName: String): Cordapp.Info {
var unknown = CordappImpl.Info.UNKNOWN
fun Manifest?.toCordappInfo(defaultShortName: String): CordappImpl.Info {
var info = CordappImpl.Info.UNKNOWN
(this?.mainAttributes?.getValue("Name") ?: defaultShortName).let { shortName ->
unknown = unknown.copy(shortName = shortName)
info = info.copy(shortName = shortName)
}
this?.mainAttributes?.getValue("Implementation-Vendor")?.let { vendor ->
unknown = unknown.copy(vendor = vendor)
info = info.copy(vendor = vendor)
}
this?.mainAttributes?.getValue("Implementation-Version")?.let { version ->
unknown = unknown.copy(version = version)
info = info.copy(version = version)
}
return unknown
}
return info
}

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.FlowStateMachine
import net.corda.core.internal.concurrent.OpenFuture
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.NodeInfo
@ -28,6 +29,8 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import java.security.PublicKey
interface NetworkMapCacheInternal : NetworkMapCache, NetworkMapCacheBase {
override val nodeReady: OpenFuture<Void?>
val allNodeHashes: List<SecureHash>
fun getNodeByHash(nodeHash: SecureHash): NodeInfo?

View File

@ -24,13 +24,12 @@ import java.nio.file.StandardCopyOption
import java.security.cert.X509Certificate
import java.time.Duration
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit
import kotlin.system.exitProcess
class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
private val fileWatcher: NodeInfoWatcher,
private val nodeInfoWatcher: NodeInfoWatcher,
private val networkMapClient: NetworkMapClient?,
private val baseDirectory: Path,
private val extraNetworkMapKeys: List<UUID>
@ -40,8 +39,10 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
private val defaultRetryInterval = 1.minutes
}
private val parametersUpdatesTrack: PublishSubject<ParametersUpdateInfo> = PublishSubject.create<ParametersUpdateInfo>()
private val executor = ScheduledThreadPoolExecutor(1, NamedThreadFactory("Network Map Updater Thread", Executors.defaultThreadFactory()))
private val parametersUpdatesTrack = PublishSubject.create<ParametersUpdateInfo>()
private val networkMapPoller = ScheduledThreadPoolExecutor(1, NamedThreadFactory("Network Map Updater Thread")).apply {
executeExistingDelayedTasksAfterShutdownPolicy = false
}
private var newNetworkParameters: Pair<ParametersUpdate, SignedNetworkParameters>? = null
private var fileWatcherSubscription: Subscription? = null
private lateinit var trustRoot: X509Certificate
@ -50,7 +51,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
override fun close() {
fileWatcherSubscription?.unsubscribe()
MoreExecutors.shutdownAndAwaitTermination(executor, 50, TimeUnit.SECONDS)
MoreExecutors.shutdownAndAwaitTermination(networkMapPoller, 50, TimeUnit.SECONDS)
}
fun start(trustRoot: X509Certificate, currentParametersHash: SecureHash, ourNodeInfoHash: SecureHash) {
@ -58,26 +59,38 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
this.trustRoot = trustRoot
this.currentParametersHash = currentParametersHash
this.ourNodeInfoHash = ourNodeInfoHash
// Subscribe to file based networkMap
fileWatcherSubscription = fileWatcher.nodeInfoUpdates().subscribe {
when (it) {
is NodeInfoUpdate.Add -> {
networkMapCache.addNode(it.nodeInfo)
}
is NodeInfoUpdate.Remove -> {
if (it.hash != ourNodeInfoHash) {
val nodeInfo = networkMapCache.getNodeByHash(it.hash)
nodeInfo?.let { networkMapCache.removeNode(it) }
watchForNodeInfoFiles()
if (networkMapClient != null) {
watchHttpNetworkMap()
}
}
private fun watchForNodeInfoFiles() {
nodeInfoWatcher
.nodeInfoUpdates()
.subscribe {
for (update in it) {
when (update) {
is NodeInfoUpdate.Add -> networkMapCache.addNode(update.nodeInfo)
is NodeInfoUpdate.Remove -> {
if (update.hash != ourNodeInfoHash) {
val nodeInfo = networkMapCache.getNodeByHash(update.hash)
nodeInfo?.let(networkMapCache::removeNode)
}
}
}
}
if (networkMapClient == null) {
// Mark the network map cache as ready on a successful poll of the node infos dir if not using
// the HTTP network map even if there aren't any node infos
networkMapCache.nodeReady.set(null)
}
}
}
}
}
if (networkMapClient == null) return
// Subscribe to remote network map if configured.
executor.executeExistingDelayedTasksAfterShutdownPolicy = false
executor.submit(object : Runnable {
private fun watchHttpNetworkMap() {
// The check may be expensive, so always run it in the background even the first time.
networkMapPoller.submit(object : Runnable {
override fun run() {
val nextScheduleDelay = try {
updateNetworkMapCache()
@ -86,9 +99,9 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
defaultRetryInterval
}
// Schedule the next update.
executor.schedule(this, nextScheduleDelay.toMillis(), TimeUnit.MILLISECONDS)
networkMapPoller.schedule(this, nextScheduleDelay.toMillis(), TimeUnit.MILLISECONDS)
}
}) // The check may be expensive, so always run it in the background even the first time.
})
}
fun trackParametersUpdate(): DataFeed<ParametersUpdateInfo?, ParametersUpdateInfo> {
@ -99,9 +112,13 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
}
fun updateNetworkMapCache(): Duration {
if (networkMapClient == null) throw CordaRuntimeException("Network map cache can be updated only if network map/compatibility zone URL is specified")
if (networkMapClient == null) {
throw CordaRuntimeException("Network map cache can be updated only if network map/compatibility zone URL is specified")
}
val (globalNetworkMap, cacheTimeout) = networkMapClient.getNetworkMap()
globalNetworkMap.parametersUpdate?.let { handleUpdateNetworkParameters(networkMapClient, it) }
val additionalHashes = extraNetworkMapKeys.flatMap {
try {
networkMapClient.getNetworkMap(it).payload.nodeInfoHashes
@ -111,6 +128,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
emptyList<SecureHash>()
}
}
val allHashesFromNetworkMap = (globalNetworkMap.nodeInfoHashes + additionalHashes).toSet()
if (currentParametersHash != globalNetworkMap.networkParameterHash) {
@ -120,12 +138,9 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
val currentNodeHashes = networkMapCache.allNodeHashes
// Remove node info from network map.
(currentNodeHashes - allHashesFromNetworkMap - fileWatcher.processedNodeInfoHashes)
.mapNotNull {
if (it != ourNodeInfoHash) {
networkMapCache.getNodeByHash(it)
} else null
}.forEach(networkMapCache::removeNode)
(currentNodeHashes - allHashesFromNetworkMap - nodeInfoWatcher.processedNodeInfoHashes)
.mapNotNull { if (it != ourNodeInfoHash) networkMapCache.getNodeByHash(it) else null }
.forEach(networkMapCache::removeNode)
(allHashesFromNetworkMap - currentNodeHashes).mapNotNull {
// Download new node info from network map
@ -141,6 +156,10 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
networkMapCache.addNode(it)
}
// Mark the network map cache as ready on a successful poll of the HTTP network map, even on the odd chance that
// it's empty
networkMapCache.nodeReady.set(null)
return cacheTimeout
}

View File

@ -3,23 +3,16 @@ package net.corda.node.services.network
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.*
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.core.utilities.seconds
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.NODE_INFO_DIRECTORY
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
import net.corda.serialization.internal.SerializationFactoryImpl
import rx.Observable
import rx.Scheduler
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.nio.file.attribute.FileTime
import java.time.Duration
@ -63,7 +56,7 @@ class NodeInfoWatcher(private val nodePath: Path,
}
}
internal data class NodeInfoFromFile(val nodeInfohash: SecureHash, val lastModified: FileTime)
private data class NodeInfoFromFile(val nodeInfohash: SecureHash, val lastModified: FileTime)
private val nodeInfosDir = nodePath / NODE_INFO_DIRECTORY
private val nodeInfoFilesMap = HashMap<Path, NodeInfoFromFile>()
@ -75,20 +68,16 @@ class NodeInfoWatcher(private val nodePath: Path,
}
/**
* Read all the files contained in [nodePath] / [NODE_INFO_DIRECTORY] and keep watching
* the folder for further updates.
* Read all the files contained in [nodePath] / [NODE_INFO_DIRECTORY] and keep watching the folder for further updates.
*
* We simply list the directory content every 5 seconds, the Java implementation of WatchService has been proven to
* be unreliable on MacOs and given the fairly simple use case we have, this simple implementation should do.
*
* @return an [Observable] returning [NodeInfoUpdate]s, at most one [NodeInfo] is returned for each processed file.
* @return an [Observable] that emits lists of [NodeInfoUpdate]s. Each emitted list is a poll event of the folder and
* may be empty if no changes were detected.
*/
fun nodeInfoUpdates(): Observable<NodeInfoUpdate> {
return Observable.interval(0, pollInterval.toMillis(), TimeUnit.MILLISECONDS, scheduler)
.flatMapIterable { loadFromDirectory() }
fun nodeInfoUpdates(): Observable<List<NodeInfoUpdate>> {
return Observable.interval(0, pollInterval.toMillis(), TimeUnit.MILLISECONDS, scheduler).map { pollDirectory() }
}
private fun loadFromDirectory(): List<NodeInfoUpdate> {
private fun pollDirectory(): List<NodeInfoUpdate> {
val processedPaths = HashSet<Path>()
val result = nodeInfosDir.list { paths ->
paths
@ -122,14 +111,3 @@ class NodeInfoWatcher(private val nodePath: Path,
return result.map { NodeInfoUpdate.Add(it.nodeInfo) } + removedHashes
}
}
// TODO Remove this once we have a tool that can read AMQP serialised files
fun main(args: Array<String>) {
_contextSerializationEnv.set(SerializationEnvironmentImpl(
SerializationFactoryImpl().apply {
registerScheme(AMQPServerSerializationScheme())
},
AMQP_P2P_CONTEXT)
)
println(Paths.get(args[0]).readObject<SignedNodeInfo>().verified())
}

View File

@ -1,6 +1,5 @@
package net.corda.node.services.network
import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty
@ -8,6 +7,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.internal.concurrent.OpenFuture
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.DataFeed
import net.corda.core.node.NodeInfo
@ -37,8 +37,7 @@ import javax.annotation.concurrent.ThreadSafe
/** Database-based network map cache. */
@ThreadSafe
open class PersistentNetworkMapCache(private val database: CordaPersistence,
private val identityService: IdentityService,
private val myLegalName: CordaX500Name) : NetworkMapCacheInternal, SingletonSerializeAsToken() {
private val identityService: IdentityService) : NetworkMapCacheInternal, SingletonSerializeAsToken() {
companion object {
private val logger = contextLogger()
}
@ -48,10 +47,8 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence,
override val changed: Observable<MapChange> = _changed.wrapWithDatabaseTransaction()
private val changePublisher: rx.Observer<MapChange> get() = _changed.bufferUntilDatabaseCommit()
// TODO revisit the logic under which nodeReady and loadDBSuccess are set.
// with the NetworkMapService redesign their meaning is not too well defined.
private val _nodeReady = openFuture<Void?>()
override val nodeReady: CordaFuture<Void?> = _nodeReady
override val nodeReady: OpenFuture<Void?> = openFuture()
private lateinit var notaries: List<NotaryInfo>
override val notaryIdentities: List<Party> get() = notaries.map { it.identity }
@ -71,15 +68,6 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence,
fun start(notaries: List<NotaryInfo>) {
this.notaries = notaries
val otherNodeInfoCount = database.transaction {
session.createQuery(
"select count(*) from ${NodeInfoSchemaV1.PersistentNodeInfo::class.java.name} n join n.legalIdentitiesAndCerts i where i.name != :myLegalName")
.setParameter("myLegalName", myLegalName.toString())
.singleResult as Long
}
if (otherNodeInfoCount > 0) {
_nodeReady.set(null)
}
}
override fun getNodeByLegalIdentity(party: AbstractParty): NodeInfo? {
@ -193,9 +181,6 @@ open class PersistentNetworkMapCache(private val database: CordaPersistence,
logger.info("Previous node was identical to incoming one - doing nothing")
}
}
if (node.legalIdentities[0].name != myLegalName) {
_nodeReady.set(null)
}
logger.debug { "Done adding node with info: $node" }
}

View File

@ -1,6 +1,5 @@
package net.corda.node.utilities
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicInteger
@ -10,19 +9,12 @@ import java.util.concurrent.atomic.AtomicInteger
* via an executor. It will use an underlying thread factory to create the actual thread
* and then override the thread name with the prefix and an ever increasing number
*/
class NamedThreadFactory(private val name: String, private val underlyingFactory: ThreadFactory) : ThreadFactory {
val threadNumber = AtomicInteger(1)
override fun newThread(runnable: Runnable?): Thread {
val thread = underlyingFactory.newThread(runnable)
class NamedThreadFactory(private val name: String,
private val delegate: ThreadFactory = Executors.defaultThreadFactory()) : ThreadFactory {
private val threadNumber = AtomicInteger(1)
override fun newThread(runnable: Runnable): Thread {
val thread = delegate.newThread(runnable)
thread.name = name + "-" + threadNumber.getAndIncrement()
return thread
}
}
/**
* Create a single thread executor with a NamedThreadFactory based on the default thread factory
* defined in java.util.concurrent.Executors
*/
fun newNamedSingleThreadExecutor(name: String): ExecutorService {
return Executors.newSingleThreadExecutor(NamedThreadFactory(name, Executors.defaultThreadFactory()))
}

View File

@ -9,6 +9,7 @@ import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.*
import net.corda.core.internal.concurrent.openFuture
import net.corda.core.messaging.ParametersUpdateInfo
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
@ -27,8 +28,8 @@ import net.corda.testing.internal.DEV_ROOT_CA
import net.corda.testing.internal.TestNodeInfoBuilder
import net.corda.testing.internal.createNodeInfoAndSigned
import net.corda.testing.node.internal.network.NetworkMapServer
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Before
import org.junit.Rule
@ -55,6 +56,7 @@ class NetworkMapUpdaterTest {
private val nodeInfoDir = baseDir / NODE_INFO_DIRECTORY
private val scheduler = TestScheduler()
private val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
private val nodeReadyFuture = openFuture<Void?>()
private val networkMapCache = createMockNetworkMapCache()
private lateinit var server: NetworkMapServer
private lateinit var networkMapClient: NetworkMapClient
@ -100,16 +102,18 @@ class NetworkMapUpdaterTest {
startUpdater()
networkMapClient.publish(signedNodeInfo2)
assertThat(nodeReadyFuture).isNotDone()
// TODO: Remove sleep in unit test.
Thread.sleep(2L * cacheExpiryMs)
verify(networkMapCache, times(2)).addNode(any())
verify(networkMapCache, times(1)).addNode(nodeInfo1)
verify(networkMapCache, times(1)).addNode(nodeInfo2)
assertThat(nodeReadyFuture).isDone()
NodeInfoWatcher.saveToFile(nodeInfoDir, fileNodeInfoAndSigned)
networkMapClient.publish(signedNodeInfo3)
networkMapClient.publish(signedNodeInfo4)
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
// TODO: Remove sleep in unit test.
Thread.sleep(2L * cacheExpiryMs)
// 4 node info from network map, and 1 from file.
@ -136,7 +140,7 @@ class NetworkMapUpdaterTest {
networkMapClient.publish(signedNodeInfo4)
startUpdater()
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
// TODO: Remove sleep in unit test.
Thread.sleep(2L * cacheExpiryMs)
@ -162,7 +166,7 @@ class NetworkMapUpdaterTest {
@Test
fun `receive node infos from directory, without a network map`() {
setUpdater()
setUpdater(netMapClient = null)
val fileNodeInfoAndSigned = createNodeInfoAndSigned("Info from file")
// Not subscribed yet.
@ -171,10 +175,12 @@ class NetworkMapUpdaterTest {
startUpdater()
NodeInfoWatcher.saveToFile(nodeInfoDir, fileNodeInfoAndSigned)
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
assertThat(nodeReadyFuture).isNotDone()
advanceTime()
verify(networkMapCache, times(1)).addNode(any())
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned.nodeInfo)
assertThat(nodeReadyFuture).isDone()
assertThat(networkMapCache.allNodeHashes).containsOnly(fileNodeInfoAndSigned.nodeInfo.serialize().hash)
}
@ -223,7 +229,7 @@ class NetworkMapUpdaterTest {
fun `fetch nodes from private network`() {
setUpdater(extraNetworkMapKeys = listOf(privateNetUUID))
server.addNodesToPrivateNetwork(privateNetUUID, listOf(ALICE_NAME))
Assertions.assertThatThrownBy { networkMapClient.getNetworkMap(privateNetUUID).payload.nodeInfoHashes }
assertThatThrownBy { networkMapClient.getNetworkMap(privateNetUUID).payload.nodeInfoHashes }
.isInstanceOf(IOException::class.java)
.hasMessageContaining("Response Code 404")
val (aliceInfo, signedAliceInfo) = createNodeInfoAndSigned(ALICE_NAME) // Goes to private network map
@ -245,7 +251,7 @@ class NetworkMapUpdaterTest {
NodeInfoWatcher.saveToFile(nodeInfoDir, fileNodeInfoAndSigned1)
NodeInfoWatcher.saveToFile(nodeInfoDir, fileNodeInfoAndSigned2)
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
verify(networkMapCache, times(2)).addNode(any())
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned1.nodeInfo)
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned2.nodeInfo)
@ -253,7 +259,7 @@ class NetworkMapUpdaterTest {
// Remove one of the nodes
val fileName1 = "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${fileNodeInfoAndSigned1.nodeInfo.legalIdentities[0].name.serialize().hash}"
(nodeInfoDir / fileName1).delete()
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
verify(networkMapCache, times(1)).removeNode(any())
verify(networkMapCache, times(1)).removeNode(fileNodeInfoAndSigned1.nodeInfo)
assertThat(networkMapCache.allNodeHashes).containsOnly(fileNodeInfoAndSigned2.signed.raw.hash)
@ -275,14 +281,14 @@ class NetworkMapUpdaterTest {
// Publish to network map the one with lower serial.
networkMapClient.publish(serverSignedNodeInfo)
startUpdater()
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
verify(networkMapCache, times(1)).addNode(localNodeInfo)
Thread.sleep(2L * cacheExpiryMs)
// Node from file has higher serial than the one from NetworkMapServer
assertThat(networkMapCache.allNodeHashes).containsOnly(localSignedNodeInfo.signed.raw.hash)
val fileName = "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}${localNodeInfo.legalIdentities[0].name.serialize().hash}"
(nodeInfoDir / fileName).delete()
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
verify(networkMapCache, times(1)).removeNode(any())
verify(networkMapCache).removeNode(localNodeInfo)
Thread.sleep(2L * cacheExpiryMs)
@ -331,7 +337,7 @@ class NetworkMapUpdaterTest {
assert(networkMapCache.allNodeHashes.size == 1)
networkMapClient.publish(signedNodeInfo2)
Thread.sleep(2L * cacheExpiryMs)
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
advanceTime()
verify(networkMapCache, times(1)).addNode(signedNodeInfo2.verified())
verify(networkMapCache, times(1)).removeNode(signedNodeInfo1.verified())
@ -341,6 +347,7 @@ class NetworkMapUpdaterTest {
private fun createMockNetworkMapCache(): NetworkMapCacheInternal {
return mock {
on { nodeReady }.thenReturn(nodeReadyFuture)
val data = ConcurrentHashMap<Party, NodeInfo>()
on { addNode(any()) }.then {
val nodeInfo = it.arguments[0] as NodeInfo
@ -359,4 +366,8 @@ class NetworkMapUpdaterTest {
private fun createNodeInfoAndSigned(org: String): NodeInfoAndSigned {
return createNodeInfoAndSigned(CordaX500Name(org, "London", "GB"))
}
}
private fun advanceTime() {
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
}
}

View File

@ -36,7 +36,7 @@ class NodeInfoWatcherTest {
val tempFolder = TemporaryFolder()
private val scheduler = TestScheduler()
private val testSubscriber = TestSubscriber<NodeInfoUpdate>()
private val testSubscriber = TestSubscriber<List<NodeInfoUpdate>>()
private lateinit var nodeInfoAndSigned: NodeInfoAndSigned
private lateinit var nodeInfoPath: Path
@ -83,7 +83,7 @@ class NodeInfoWatcherTest {
val subscription = nodeInfoWatcher.nodeInfoUpdates().subscribe(testSubscriber)
try {
advanceTime()
val readNodes = testSubscriber.onNextEvents.distinct()
val readNodes = testSubscriber.onNextEvents.distinct().flatten()
assertEquals(0, readNodes.size)
} finally {
subscription.unsubscribe()
@ -98,7 +98,7 @@ class NodeInfoWatcherTest {
advanceTime()
try {
val readNodes = testSubscriber.onNextEvents.distinct()
val readNodes = testSubscriber.onNextEvents.distinct().flatten()
assertEquals(1, readNodes.size)
assertEquals(nodeInfoAndSigned.nodeInfo, (readNodes.first() as? NodeInfoUpdate.Add)?.nodeInfo)
} finally {
@ -116,7 +116,8 @@ class NodeInfoWatcherTest {
// Ensure the watch service is started.
advanceTime()
// Check no nodeInfos are read.
assertEquals(0, testSubscriber.valueCount)
assertEquals(0, testSubscriber.onNextEvents.distinct().flatten().size)
createNodeInfoFileInPath()
advanceTime()
@ -124,7 +125,7 @@ class NodeInfoWatcherTest {
// We need the WatchService to report a change and that might not happen immediately.
testSubscriber.awaitValueCount(1, 5, TimeUnit.SECONDS)
// The same folder can be reported more than once, so take unique values.
val readNodes = testSubscriber.onNextEvents.distinct()
val readNodes = testSubscriber.onNextEvents.distinct().flatten()
assertEquals(nodeInfoAndSigned.nodeInfo, (readNodes.first() as? NodeInfoUpdate.Add)?.nodeInfo)
} finally {
subscription.unsubscribe()

View File

@ -11,8 +11,8 @@ def javaHome = System.getProperty('java.home')
def jarBaseName = "corda-${project.name}".toString()
configurations {
runtimeLibraries
runtimeArtifacts.extendsFrom runtimeLibraries
deterministicLibraries
deterministicArtifacts.extendsFrom deterministicLibraries
}
dependencies {
@ -20,10 +20,10 @@ dependencies {
// Configure these by hand. It should be a minimal subset of dependencies,
// and without any obviously non-deterministic ones such as Hibernate.
runtimeLibraries project(path: ':core-deterministic', configuration: 'runtimeArtifacts')
runtimeLibraries "org.apache.qpid:proton-j:$protonj_version"
runtimeLibraries "org.iq80.snappy:snappy:$snappy_version"
runtimeLibraries "com.google.guava:guava:$guava_version"
deterministicLibraries project(path: ':core-deterministic', configuration: 'deterministicArtifacts')
deterministicLibraries "org.apache.qpid:proton-j:$protonj_version"
deterministicLibraries "org.iq80.snappy:snappy:$snappy_version"
deterministicLibraries "com.google.guava:guava:$guava_version"
}
jar {
@ -108,7 +108,7 @@ task determinise(type: ProGuardTask) {
libraryjars file("$javaHome/lib/rt.jar")
libraryjars file("$javaHome/lib/jce.jar")
configurations.runtimeLibraries.forEach {
configurations.deterministicLibraries.forEach {
libraryjars it, filter: '!META-INF/versions/**'
}
@ -142,7 +142,7 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
libraryjars deterministic_rt_jar
configurations.runtimeLibraries.forEach {
configurations.deterministicLibraries.forEach {
libraryjars it, filter: '!META-INF/versions/**'
}
@ -162,12 +162,12 @@ assemble.dependsOn checkDeterminism
def deterministicJar = metafix.outputs.files.singleFile
artifacts {
runtimeArtifacts file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
deterministicArtifacts file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
publish file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
}
publish {
dependenciesFrom configurations.runtimeArtifacts
dependenciesFrom configurations.deterministicArtifacts
publishSources = false
publishJavadoc = false
name jarBaseName

View File

@ -83,7 +83,7 @@ include 'tools:notary-healthcheck:client'
if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
include 'core-deterministic'
include 'core-deterministic:testing'
include 'core-deterministic:testing:common'
include 'core-deterministic:testing:data'
include 'core-deterministic:testing:verifier'
include 'serialization-deterministic'
}

View File

@ -33,6 +33,7 @@ class MockCordappProvider(
serializationCustomSerializers = emptyList(),
customSchemas = emptySet(),
jarPath = Paths.get("").toUri().toURL(),
info = CordappImpl.Info.UNKNOWN,
allFlows = emptyList(),
jarHash = SecureHash.allOnesHash)
if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) {
@ -40,7 +41,9 @@ class MockCordappProvider(
}
}
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName)
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? {
return cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName)
}
private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId {
val existingAttachment = attachments.files.filter {