diff --git a/.gitignore b/.gitignore
index 154f5008db..78038540b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,3 +81,8 @@ docs/virtualenv/
# bft-smart
node/bft-smart-config/currentView
node/config/currentView
+
+# Files you may find useful to have in your working directory.
+PLAN
+NOTES
+TODO
\ No newline at end of file
diff --git a/.idea/runConfigurations/Attachment_Demo__Run_Recipient.xml b/.idea/runConfigurations/Attachment_Demo__Run_Recipient.xml
index 7ea10c3f86..2d3762ecd0 100644
--- a/.idea/runConfigurations/Attachment_Demo__Run_Recipient.xml
+++ b/.idea/runConfigurations/Attachment_Demo__Run_Recipient.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.idea/runConfigurations/Attachment_Demo__Run_Sender.xml b/.idea/runConfigurations/Attachment_Demo__Run_Sender.xml
index db6567b2e1..419abc2218 100644
--- a/.idea/runConfigurations/Attachment_Demo__Run_Sender.xml
+++ b/.idea/runConfigurations/Attachment_Demo__Run_Sender.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.idea/runConfigurations/Trader_Demo__Run_Buyer.xml b/.idea/runConfigurations/Trader_Demo__Run_Buyer.xml
index 21a140d525..dbfbf49a48 100644
--- a/.idea/runConfigurations/Trader_Demo__Run_Buyer.xml
+++ b/.idea/runConfigurations/Trader_Demo__Run_Buyer.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.idea/runConfigurations/Trader_Demo__Run_Seller.xml b/.idea/runConfigurations/Trader_Demo__Run_Seller.xml
index 530b17f0db..5202c95ad1 100644
--- a/.idea/runConfigurations/Trader_Demo__Run_Seller.xml
+++ b/.idea/runConfigurations/Trader_Demo__Run_Seller.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/build.gradle b/build.gradle
index be4b095ca7..0a1402b5e4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@ buildscript {
// Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things.
//
// TODO: Sort this alphabetically.
- ext.kotlin_version = '1.0.6'
+ ext.kotlin_version = '1.0.7'
ext.quasar_version = '0.7.6' // TODO: Upgrade to 0.7.7+ when Quasar bug 238 is resolved.
ext.asm_version = '0.5.3'
ext.artemis_version = '1.5.3'
@@ -19,6 +19,7 @@ buildscript {
ext.jersey_version = '2.25'
ext.jolokia_version = '2.0.0-M3'
ext.assertj_version = '3.6.1'
+ ext.slf4j_version = '1.7.22'
ext.log4j_version = '2.7'
ext.bouncycastle_version = '1.56'
ext.guava_version = '19.0'
@@ -30,7 +31,7 @@ buildscript {
ext.jopt_simple_version = '5.0.2'
ext.jansi_version = '1.14'
ext.hibernate_version = '5.2.6.Final'
- ext.h2_version = '1.4.193'
+ ext.h2_version = '1.4.194'
ext.rxjava_version = '1.2.4'
ext.requery_version = '1.1.1'
ext.dokka_version = '0.9.13'
@@ -119,8 +120,17 @@ repositories {
dependencies {
compile project(':node')
compile "com.google.guava:guava:$guava_version"
+
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
- runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
+ runtime project(path: ":node:webserver:webcapsule", configuration: 'runtimeArtifacts')
+
+ // For the buildCordappDependenciesJar task
+ runtime project(':client:jfx')
+ runtime project(':client:mock')
+ runtime project(':core')
+ runtime project(':finance')
+ runtime project(':node:webserver')
+ testCompile project(':test-utils')
}
task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
@@ -155,23 +165,25 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
- artemisPort 10002
+ p2pPort 10002
cordapps = []
}
node {
name "Bank A"
nearestCity "London"
advertisedServices = []
- artemisPort 10004
- webPort 10005
+ p2pPort 10012
+ rpcPort 10013
+ webPort 10014
cordapps = []
}
node {
name "Bank B"
nearestCity "New York"
advertisedServices = []
- artemisPort 10006
- webPort 10007
+ p2pPort 10007
+ rpcPort 10008
+ webPort 10009
cordapps = []
}
}
@@ -186,7 +198,7 @@ bintrayConfig {
projectUrl = 'https://github.com/corda/corda'
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
- publications = ['client', 'core', 'corda', 'corda-webserver', 'finance', 'node', 'node-schemas', 'test-utils', 'jackson']
+ publications = ['jfx', 'mock', 'core', 'corda', 'corda-webserver', 'finance', 'node', 'node-api', 'node-schemas', 'test-utils', 'jackson', 'webserver']
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'
@@ -205,7 +217,7 @@ dokka {
moduleName = 'corda'
outputDirectory = 'docs/build/html/api/kotlin'
processConfigurations = ['compile']
- sourceDirs = files('core/src/main/kotlin', 'client/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin', 'client/jackson/src/main/kotlin')
+ sourceDirs = files('core/src/main/kotlin', 'client/jfx/src/main/kotlin', 'client/mock/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin', 'client/jackson/src/main/kotlin')
}
task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
@@ -213,7 +225,20 @@ task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputFormat = "javadoc"
outputDirectory = 'docs/build/html/api/javadoc'
processConfigurations = ['compile']
- sourceDirs = files('core/src/main/kotlin', 'client/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin', 'client/jackson/src/main/kotlin')
+ sourceDirs = files('core/src/main/kotlin', 'client/jfx/src/main/kotlin', 'client/mock/src/main/kotlin', 'node/src/main/kotlin', 'finance/src/main/kotlin', 'client/jackson/src/main/kotlin')
}
task apidocs(dependsOn: ['dokka', 'dokkaJavadoc'])
+
+// Build a ZIP of all JARs required to compile the Cordapp template
+// Note: corda.jar is used at runtime so no runtime ZIP is necessary.
+// Resulting ZIP can be found in "build/distributions"
+task buildCordappDependenciesZip(type: Zip) {
+ baseName 'corda-deps'
+ from configurations.runtime
+ from configurations.compile
+ from configurations.testCompile
+ from buildscript.configurations.classpath
+ from 'node/capsule/NOTICE' // CDDL notice
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+}
diff --git a/client/jackson/build.gradle b/client/jackson/build.gradle
index ea9d24b501..a895476683 100644
--- a/client/jackson/build.gradle
+++ b/client/jackson/build.gradle
@@ -14,6 +14,13 @@ repositories {
dependencies {
compile project(':core')
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- compile ("com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}")
+
+ // Jackson and its plugins: parsing to/from JSON and other textual formats.
+ compile "com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}"
+ // Yaml is useful for parsing strings to method calls.
+ compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
+ // This adds support for java.time types.
+ compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
+
testCompile "junit:junit:$junit_version"
}
diff --git a/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt b/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt
index 5cbfb70423..d9cdd9a0ce 100644
--- a/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt
+++ b/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt
@@ -1,25 +1,24 @@
package net.corda.jackson
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParseException
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.core.JsonToken
+import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers
import com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer
import com.fasterxml.jackson.databind.module.SimpleModule
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
+import net.corda.core.contracts.Amount
import net.corda.core.contracts.BusinessCalendar
import net.corda.core.crypto.*
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.IdentityService
+import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAPublicKey
import java.math.BigDecimal
-import java.time.LocalDate
-import java.time.LocalDateTime
+import java.util.*
/**
* Utilities and serialisers for working with JSON representations of basic types. This adds Jackson support for
@@ -28,6 +27,7 @@ import java.time.LocalDateTime
* Note that Jackson can also be used to serialise/deserialise other formats such as Yaml and XML.
*/
object JacksonSupport {
+ // TODO: This API could use some tidying up - there should really only need to be one kind of mapper.
// If you change this API please update the docs in the docsite (json.rst)
interface PartyObjectMapper {
@@ -35,28 +35,19 @@ object JacksonSupport {
fun partyFromKey(owningKey: CompositeKey): Party?
}
- class RpcObjectMapper(val rpc: CordaRPCOps) : PartyObjectMapper, ObjectMapper() {
+ class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
override fun partyFromName(partyName: String): Party? = rpc.partyFromName(partyName)
override fun partyFromKey(owningKey: CompositeKey): Party? = rpc.partyFromKey(owningKey)
}
- class IdentityObjectMapper(val identityService: IdentityService) : PartyObjectMapper, ObjectMapper(){
+ class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
override fun partyFromName(partyName: String): Party? = identityService.partyFromName(partyName)
override fun partyFromKey(owningKey: CompositeKey): Party? = identityService.partyFromKey(owningKey)
}
- class NoPartyObjectMapper: PartyObjectMapper, ObjectMapper() {
+ class NoPartyObjectMapper(factory: JsonFactory): PartyObjectMapper, ObjectMapper(factory) {
override fun partyFromName(partyName: String): Party? = throw UnsupportedOperationException()
override fun partyFromKey(owningKey: CompositeKey): Party? = throw UnsupportedOperationException()
}
- val javaTimeModule: Module by lazy {
- SimpleModule("java.time").apply {
- addSerializer(LocalDate::class.java, ToStringSerializer)
- addDeserializer(LocalDate::class.java, LocalDateDeserializer)
- addKeyDeserializer(LocalDate::class.java, LocalDateKeyDeserializer)
- addSerializer(LocalDateTime::class.java, ToStringSerializer)
- }
- }
-
val cordaModule: Module by lazy {
SimpleModule("core").apply {
addSerializer(AnonymousParty::class.java, AnonymousPartySerializer)
@@ -66,8 +57,8 @@ object JacksonSupport {
addSerializer(BigDecimal::class.java, ToStringSerializer)
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
addSerializer(SecureHash::class.java, SecureHashSerializer)
- // It's slightly remarkable, but apparently Jackson works out that this is the only possibility
- // for a SecureHash at the moment and tries to use SHA256 directly even though we only give it SecureHash
+ addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer)
+ addDeserializer(SecureHash::class.java, SecureHashDeserializer())
addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer())
addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
@@ -83,27 +74,35 @@ object JacksonSupport {
// TODO this tunnels the Kryo representation as a Base58 encoded string. Replace when RPC supports this.
addSerializer(NodeInfo::class.java, NodeInfoSerializer)
addDeserializer(NodeInfo::class.java, NodeInfoDeserializer)
+
+ // For Amount
+ addSerializer(Amount::class.java, AmountSerializer)
+ addDeserializer(Amount::class.java, AmountDeserializer)
+
+ // For OpaqueBytes
+ addDeserializer(OpaqueBytes::class.java, OpaqueBytesDeserializer)
+ addSerializer(OpaqueBytes::class.java, OpaqueBytesSerializer)
}
}
/** Mapper requiring RPC support to deserialise parties from names */
- @JvmStatic
- fun createDefaultMapper(rpc: CordaRPCOps): ObjectMapper = configureMapper(RpcObjectMapper(rpc))
+ @JvmStatic @JvmOverloads
+ fun createDefaultMapper(rpc: CordaRPCOps, factory: JsonFactory = JsonFactory()): ObjectMapper = configureMapper(RpcObjectMapper(rpc, factory))
/** For testing or situations where deserialising parties is not required */
- @JvmStatic
- fun createNonRpcMapper(): ObjectMapper = configureMapper(NoPartyObjectMapper())
+ @JvmStatic @JvmOverloads
+ fun createNonRpcMapper(factory: JsonFactory = JsonFactory()): ObjectMapper = configureMapper(NoPartyObjectMapper(factory))
/** For testing with an in memory identity service */
- @JvmStatic
- fun createInMemoryMapper(identityService: IdentityService) = configureMapper(IdentityObjectMapper(identityService))
+ @JvmStatic @JvmOverloads
+ fun createInMemoryMapper(identityService: IdentityService, factory: JsonFactory = JsonFactory()) = configureMapper(IdentityObjectMapper(identityService, factory))
private fun configureMapper(mapper: ObjectMapper): ObjectMapper = mapper.apply {
enable(SerializationFeature.INDENT_OUTPUT)
enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
- registerModule(javaTimeModule)
+ registerModule(JavaTimeModule())
registerModule(cordaModule)
registerModule(KotlinModule())
}
@@ -114,23 +113,6 @@ object JacksonSupport {
}
}
- object LocalDateDeserializer : JsonDeserializer() {
- override fun deserialize(parser: JsonParser, context: DeserializationContext): LocalDate {
- return try {
- LocalDate.parse(parser.text)
- } catch (e: Exception) {
- throw JsonParseException(parser, "Invalid LocalDate ${parser.text}: ${e.message}")
- }
- }
- }
-
- object LocalDateKeyDeserializer : KeyDeserializer() {
- override fun deserializeKey(text: String, p1: DeserializationContext): Any? {
- return LocalDate.parse(text)
- }
-
- }
-
object AnonymousPartySerializer : JsonSerializer() {
override fun serialize(obj: AnonymousParty, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.owningKey.toBase58String())
@@ -143,7 +125,6 @@ object JacksonSupport {
parser.nextToken()
}
- val mapper = parser.codec as PartyObjectMapper
// TODO this needs to use some industry identifier(s) instead of these keys
val key = CompositeKey.parseFromBase58(parser.text)
return AnonymousParty(key)
@@ -253,5 +234,43 @@ object JacksonSupport {
}
}
}
+
+ object AmountSerializer : JsonSerializer>() {
+ override fun serialize(value: Amount<*>, gen: JsonGenerator, serializers: SerializerProvider) {
+ gen.writeString(value.toString())
+ }
+ }
+
+ object AmountDeserializer : JsonDeserializer>() {
+ override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> {
+ try {
+ return Amount.parseCurrency(parser.text)
+ } catch (e: Exception) {
+ try {
+ val tree = parser.readValueAsTree()
+ require(tree["quantity"].canConvertToLong() && tree["token"].asText().isNotBlank())
+ val quantity = tree["quantity"].asLong()
+ val token = tree["token"].asText()
+ // Attempt parsing as a currency token. TODO: This needs thought about how to extend to other token types.
+ val currency = Currency.getInstance(token)
+ return Amount(quantity, currency)
+ } catch(e2: Exception) {
+ throw JsonParseException(parser, "Invalid amount ${parser.text}", e2)
+ }
+ }
+ }
+ }
+
+ object OpaqueBytesDeserializer : JsonDeserializer() {
+ override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): OpaqueBytes {
+ return OpaqueBytes(parser.text.toByteArray())
+ }
+ }
+
+ object OpaqueBytesSerializer : JsonSerializer() {
+ override fun serialize(value: OpaqueBytes, gen: JsonGenerator, serializers: SerializerProvider) {
+ gen.writeBinary(value.bytes)
+ }
+ }
}
diff --git a/client/jackson/src/main/kotlin/net/corda/jackson/StringToMethodCallParser.kt b/client/jackson/src/main/kotlin/net/corda/jackson/StringToMethodCallParser.kt
new file mode 100644
index 0000000000..15c48a745a
--- /dev/null
+++ b/client/jackson/src/main/kotlin/net/corda/jackson/StringToMethodCallParser.kt
@@ -0,0 +1,184 @@
+package net.corda.jackson
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
+import net.corda.jackson.StringToMethodCallParser.ParsedMethodCall
+import org.slf4j.LoggerFactory
+import java.lang.reflect.Constructor
+import java.lang.reflect.Method
+import java.util.concurrent.Callable
+import javax.annotation.concurrent.ThreadSafe
+import kotlin.reflect.KClass
+import kotlin.reflect.KFunction
+import kotlin.reflect.KotlinReflectionInternalError
+import kotlin.reflect.jvm.kotlinFunction
+
+/**
+ * This class parses strings in a format designed for human usability into [ParsedMethodCall] objects representing a
+ * ready-to-invoke call on the given target object. The strings accepted by this class are a minor variant of
+ * [Yaml](http://www.yaml.org/spec/1.2/spec.html) and can be easily typed at a command line. Intended use cases include
+ * things like the Corda shell, text-based RPC dispatch, simple scripting and so on.
+ *
+ * # Syntax
+ *
+ * The format of the string is as follows. The first word is the name of the method and must always be present. The rest,
+ * which is optional, is wrapped in curly braces and parsed as if it were a Yaml object. The keys of this object are then
+ * mapped to the parameters of the method via the usual Jackson mechanisms. The standard [java.lang.Object] methods are
+ * excluded.
+ *
+ * One convenient feature of Yaml is that barewords collapse into strings, thus you can write a call like the following:
+ *
+ * fun someCall(note: String, option: Boolean)
+ *
+ * someCall note: This is a really helpful feature, option: true
+ *
+ * ... and it will be parsed in the intuitive way. Quotes are only needed if you want to put a comma into the string.
+ *
+ * There is an [online Yaml parser](http://yaml-online-parser.appspot.com/) which can be used to explore
+ * the allowed syntax.
+ *
+ * # Usage
+ *
+ * This class is thread safe. Multiple strings may be parsed in parallel, and the resulting [ParsedMethodCall]
+ * objects may be reused multiple times and also invoked in parallel, as long as the underling target object is
+ * thread safe itself.
+ *
+ * You may pass in an alternative [ObjectMapper] to control what types can be parsed, but it must be configured
+ * with the [YAMLFactory] for the class to work.
+ *
+ * # Limitations
+ *
+ * - The target class must be either a Kotlin class, or a Java class compiled with the -parameters command line
+ * switch, as the class relies on knowing the names of parameters which isn't data provided by default by the
+ * Java compiler.
+ * - Vararg methods are not supported, as the type information that'd be required is missing.
+ * - Method overloads that have identical parameter names but different types can't be handled, because often
+ * a string could map to multiple types, so which one to use is ambiguous. If you want your interface to be
+ * usable with this utility make sure the parameter and method names don't rely on type overloading.
+ *
+ * # Examples
+ *
+ * fun simple() = ...
+ * "simple" -> runs the no-args function 'simple'
+ *
+ * fun attachmentExists(id: SecureHash): Boolean
+ * "attachmentExists id: b6d7e826e87" -> parses the given ID as a SecureHash
+ *
+ * fun addNote(id: SecureHash, note: String)
+ * "addNote id: b6d7e826e8739ab2eb6e077fc4fba9b04fb880bb4cbd09bc618d30234a8827a4, note: Some note"
+ */
+@ThreadSafe
+open class StringToMethodCallParser(targetType: Class,
+ private val om: ObjectMapper = JacksonSupport.createNonRpcMapper(YAMLFactory())) {
+ /** Same as the regular constructor but takes a Kotlin reflection [KClass] instead of a Java [Class]. */
+ constructor(targetType: KClass) : this(targetType.java)
+
+ companion object {
+ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ private val ignoredNames = Object::class.java.methods.map { it.name }
+ private fun methodsFromType(clazz: Class<*>) = clazz.methods.map { it.name to it }.toMap().filterKeys { it !in ignoredNames }
+ private val log = LoggerFactory.getLogger(StringToMethodCallParser::class.java)!!
+ }
+
+ /** The methods that can be invoked via this parser. */
+ protected val methodMap = methodsFromType(targetType)
+ /** A map of method name to parameter names for the target type. */
+ val methodParamNames: Map> = targetType.declaredMethods.mapNotNull {
+ try {
+ it.name to paramNamesFromMethod(it)
+ } catch(e: KotlinReflectionInternalError) {
+ // Kotlin reflection doesn't support every method that can exist on an object (in particular, reified
+ // inline methods) so we just ignore those here.
+ null
+ }
+ }.toMap()
+
+ inner class ParsedMethodCall(private val target: T?, val methodName: String, val args: Array) : Callable {
+ operator fun invoke(): Any? = call()
+ override fun call(): Any? {
+ if (target == null)
+ throw IllegalStateException("No target object was specified")
+ if (log.isDebugEnabled)
+ log.debug("Invoking call $methodName($args)")
+ return methodMap[methodName]!!.invoke(target, *args)
+ }
+ }
+
+ /**
+ * Uses either Kotlin or Java 8 reflection to learn the names of the parameters to a method.
+ */
+ open fun paramNamesFromMethod(method: Method): List {
+ val kf: KFunction<*>? = method.kotlinFunction
+ return method.parameters.mapIndexed { index, param ->
+ when {
+ param.isNamePresent -> param.name
+ // index + 1 because the first Kotlin reflection param is 'this', but that doesn't match Java reflection.
+ kf != null -> kf.parameters[index + 1].name ?: throw UnparseableCallException.ReflectionDataMissing(method.name, index)
+ else -> throw UnparseableCallException.ReflectionDataMissing(method.name, index)
+ }
+ }
+ }
+
+ /**
+ * Uses either Kotlin or Java 8 reflection to learn the names of the parameters to a constructor.
+ */
+ open fun paramNamesFromConstructor(ctor: Constructor<*>): List {
+ val kf: KFunction<*>? = ctor.kotlinFunction
+ return ctor.parameters.mapIndexed { index, param ->
+ when {
+ param.isNamePresent -> param.name
+ kf != null -> kf.parameters[index].name ?: throw UnparseableCallException.ReflectionDataMissing("", index)
+ else -> throw UnparseableCallException.ReflectionDataMissing("", index)
+ }
+ }
+ }
+
+ open class UnparseableCallException(command: String) : Exception("Could not parse as a command: $command") {
+ class UnknownMethod(val methodName: String) : UnparseableCallException("Unknown command name: $methodName")
+ class MissingParameter(methodName: String, val paramName: String, command: String) : UnparseableCallException("Parameter $paramName missing from attempt to invoke $methodName in command: $command")
+ class TooManyParameters(methodName: String, command: String) : UnparseableCallException("Too many parameters provided for $methodName: $command")
+ class ReflectionDataMissing(methodName: String, argIndex: Int) : UnparseableCallException("Method $methodName missing parameter name at index $argIndex")
+ }
+
+ /**
+ * Parses the given command as a call on the target type. The target should be specified, if it's null then
+ * the resulting [ParsedMethodCall] can't be invoked, just inspected.
+ */
+ @Throws(UnparseableCallException::class)
+ fun parse(target: T?, command: String): ParsedMethodCall {
+ log.debug("Parsing call command from string: {}", command)
+ val spaceIndex = command.indexOf(' ')
+ val name = if (spaceIndex != -1) command.substring(0, spaceIndex) else command
+ val argStr = if (spaceIndex != -1) command.substring(spaceIndex) else ""
+ val method = methodMap[name] ?: throw UnparseableCallException.UnknownMethod(name)
+ log.debug("Parsing call for method {}", name)
+ val args = parseArguments(name, paramNamesFromMethod(method).zip(method.parameterTypes), argStr)
+ return ParsedMethodCall(target, name, args)
+ }
+
+ /**
+ * Parses only the arguments string given the info about parameter names and types.
+ *
+ * @param methodNameHint A name that will be used in exceptions if thrown; not used for any other purpose.
+ */
+ @Throws(UnparseableCallException::class)
+ fun parseArguments(methodNameHint: String, parameters: List>>, args: String): Array {
+ // If we have parameters, wrap them in {} to allow the Yaml parser to eat them on a single line.
+ val parameterString = "{ $args }"
+ val tree: JsonNode = om.readTree(parameterString) ?: throw UnparseableCallException(args)
+ if (tree.size() > parameters.size) throw UnparseableCallException.TooManyParameters(methodNameHint, args)
+ val inOrderParams: List = parameters.mapIndexed { index, param ->
+ val (argName, argType) = param
+ val entry = tree[argName] ?: throw UnparseableCallException.MissingParameter(methodNameHint, argName, args)
+ om.readValue(entry.traverse(om), argType)
+ }
+ if (log.isDebugEnabled) {
+ inOrderParams.forEachIndexed { i, param ->
+ val tmp = if (param != null) "${param.javaClass.name} -> $param" else "(null)"
+ log.debug("Parameter $i. $tmp")
+ }
+ }
+ return inOrderParams.toTypedArray()
+ }
+}
\ No newline at end of file
diff --git a/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt
index 8ca943f7be..b1bbfa8f67 100644
--- a/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt
+++ b/client/jackson/src/test/kotlin/net/corda/jackson/JacksonSupportTest.kt
@@ -1,12 +1,17 @@
package net.corda.jackson
+import com.fasterxml.jackson.databind.SerializationFeature
import com.pholser.junit.quickcheck.From
import com.pholser.junit.quickcheck.Property
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
+import net.corda.core.contracts.Amount
+import net.corda.core.contracts.USD
import net.corda.core.testing.PublicKeyGenerator
import net.i2p.crypto.eddsa.EdDSAPublicKey
+import org.junit.Test
import org.junit.runner.RunWith
import java.security.PublicKey
+import java.util.*
import kotlin.test.assertEquals
@RunWith(JUnitQuickcheck::class)
@@ -21,4 +26,28 @@ class JacksonSupportTest {
val parsedKey = mapper.readValue(serialized, EdDSAPublicKey::class.java)
assertEquals(publicKey, parsedKey)
}
+
+ private class Dummy(val notional: Amount)
+
+ @Test
+ fun readAmount() {
+ val oldJson = """
+ {
+ "notional": {
+ "quantity": 2500000000,
+ "token": "USD"
+ }
+ }
+ """
+ val newJson = """ { "notional" : "$25000000" } """
+
+ assertEquals(Amount(2500000000L, USD), mapper.readValue(newJson, Dummy::class.java).notional)
+ assertEquals(Amount(2500000000L, USD), mapper.readValue(oldJson, Dummy::class.java).notional)
+ }
+
+ @Test
+ fun writeAmount() {
+ val writer = mapper.writer().without(SerializationFeature.INDENT_OUTPUT)
+ assertEquals("""{"notional":"25000000.00 USD"}""", writer.writeValueAsString(Dummy(Amount.parseCurrency("$25000000"))))
+ }
}
diff --git a/client/jackson/src/test/kotlin/net/corda/jackson/StringToMethodCallParserTest.kt b/client/jackson/src/test/kotlin/net/corda/jackson/StringToMethodCallParserTest.kt
new file mode 100644
index 0000000000..d0e0c7cbb8
--- /dev/null
+++ b/client/jackson/src/test/kotlin/net/corda/jackson/StringToMethodCallParserTest.kt
@@ -0,0 +1,34 @@
+package net.corda.jackson
+
+import net.corda.core.crypto.SecureHash
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class StringToMethodCallParserTest {
+ @Suppress("UNUSED")
+ class Target {
+ fun simple() = "simple"
+ fun string(note: String) = note
+ fun twoStrings(a: String, b: String) = a + b
+ fun simpleObject(hash: SecureHash.SHA256) = hash.toString()!!
+ fun complexObject(pair: Pair) = pair
+ }
+
+ val randomHash = "361170110f61086f77ff2c5b7ab36513705da1a3ebabf14dbe5cc9c982c45401"
+ val tests = mapOf(
+ "simple" to "simple",
+ "string note: A test of barewords" to "A test of barewords",
+ "twoStrings a: Some words, b: ' and some words, like, Kirk, would, speak'" to "Some words and some words, like, Kirk, would, speak",
+ "simpleObject hash: $randomHash" to randomHash.toUpperCase(),
+ "complexObject pair: { first: 12, second: Word up brother }" to Pair(12, "Word up brother")
+ )
+
+ @Test
+ fun calls() {
+ val parser = StringToMethodCallParser(Target::class)
+ val target = Target()
+ for ((input, output) in tests) {
+ assertEquals(output, parser.parse(target, input).invoke())
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/build.gradle b/client/jfx/build.gradle
similarity index 88%
rename from client/build.gradle
rename to client/jfx/build.gradle
index 4878aede15..90b36527e0 100644
--- a/client/build.gradle
+++ b/client/jfx/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils'
-description 'Corda client modules'
+description 'Corda client JavaFX modules'
repositories {
mavenLocal()
@@ -35,7 +35,7 @@ sourceSets {
}
test {
resources {
- srcDir "../config/test"
+ srcDir "../../config/test"
}
}
}
@@ -47,10 +47,6 @@ dependencies {
compile project(":core")
compile project(':node')
- // Log4J: logging framework (with SLF4J bindings)
- compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
- compile "org.apache.logging.log4j:log4j-core:${log4j_version}"
-
compile "com.google.guava:guava:$guava_version"
// ReactFX: Functional reactive UI programming.
diff --git a/client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/CordaRPCClientTest.kt
similarity index 91%
rename from client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt
rename to client/jfx/src/integration-test/kotlin/net/corda/client/jfx/CordaRPCClientTest.kt
index b9dee73411..37b99d0c79 100644
--- a/client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt
+++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/CordaRPCClientTest.kt
@@ -1,7 +1,6 @@
-package net.corda.client
+package net.corda.client.jfx
import net.corda.core.contracts.DOLLARS
-import net.corda.core.contracts.issuedBy
import net.corda.core.flows.FlowException
import net.corda.core.getOrThrow
import net.corda.core.messaging.startFlow
@@ -12,7 +11,6 @@ import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.internal.Node
import net.corda.node.services.User
-import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.CordaRPCClient
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
@@ -37,7 +35,7 @@ class CordaRPCClientTest : NodeBasedTest() {
@Before
fun setUp() {
node = startNode("Alice", rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
- client = CordaRPCClient(node.configuration.artemisAddress, configureTestSSL())
+ client = CordaRPCClient(node.configuration.rpcAddress!!)
}
@After
@@ -85,10 +83,7 @@ class CordaRPCClientTest : NodeBasedTest() {
fun `FlowException thrown by flow`() {
client.start(rpcUser.username, rpcUser.password)
val proxy = client.proxy()
- val handle = proxy.startFlow(::CashPaymentFlow,
- 100.DOLLARS.issuedBy(node.info.legalIdentity.ref(1)),
- node.info.legalIdentity
- )
+ val handle = proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.legalIdentity)
// TODO Restrict this to CashException once RPC serialisation has been fixed
assertThatExceptionOfType(FlowException::class.java).isThrownBy {
handle.returnValue.getOrThrow()
diff --git a/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt
similarity index 88%
rename from client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt
rename to client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt
index d72dd4342b..fff3c09523 100644
--- a/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt
+++ b/client/jfx/src/integration-test/kotlin/net/corda/client/jfx/NodeMonitorModelTest.kt
@@ -1,11 +1,10 @@
-package net.corda.client
+package net.corda.client.jfx
-import net.corda.client.model.NodeMonitorModel
-import net.corda.client.model.ProgressTrackingEvent
+import net.corda.client.jfx.model.NodeMonitorModel
+import net.corda.client.jfx.model.ProgressTrackingEvent
import net.corda.core.bufferUntilSubscribed
import net.corda.core.contracts.Amount
-import net.corda.core.contracts.Issued
-import net.corda.core.contracts.PartyAndReference
+import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.USD
import net.corda.core.flows.StateMachineRunId
import net.corda.core.getOrThrow
@@ -22,16 +21,14 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashExitFlow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
-import net.corda.node.driver.DriverBasedTest
import net.corda.node.driver.driver
import net.corda.node.services.User
-import net.corda.node.services.config.configureTestSSL
-import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.testing.expect
import net.corda.testing.expectEvents
+import net.corda.testing.node.DriverBasedTest
import net.corda.testing.sequence
import org.junit.Test
import rx.Observable
@@ -57,9 +54,10 @@ class NodeMonitorModelTest : DriverBasedTest() {
)
val aliceNodeFuture = startNode("Alice", rpcUsers = listOf(cashUser))
val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
-
- aliceNode = aliceNodeFuture.getOrThrow().nodeInfo
- notaryNode = notaryNodeFuture.getOrThrow().nodeInfo
+ val aliceNodeHandle = aliceNodeFuture.getOrThrow()
+ val notaryNodeHandle = notaryNodeFuture.getOrThrow()
+ aliceNode = aliceNodeHandle.nodeInfo
+ notaryNode = notaryNodeHandle.nodeInfo
newNode = { nodeName -> startNode(nodeName).getOrThrow().nodeInfo }
val monitor = NodeMonitorModel()
@@ -70,7 +68,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
- monitor.register(ArtemisMessagingComponent.toHostAndPort(aliceNode.address), configureTestSSL(), cashUser.username, cashUser.password)
+ monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
rpc = monitor.proxyObservable.value!!
runTest()
}
@@ -124,17 +122,8 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `cash issue and move`() {
- rpc.startFlow(::CashIssueFlow,
- Amount(100, USD),
- OpaqueBytes(ByteArray(1, { 1 })),
- aliceNode.legalIdentity,
- notaryNode.notaryIdentity
- ).returnValue.getOrThrow()
-
- rpc.startFlow(::CashPaymentFlow,
- Amount(100, Issued(PartyAndReference(aliceNode.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)),
- aliceNode.legalIdentity
- )
+ rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), aliceNode.legalIdentity, notaryNode.notaryIdentity).returnValue.getOrThrow()
+ rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.legalIdentity).returnValue.getOrThrow()
var issueSmId: StateMachineRunId? = null
var moveSmId: StateMachineRunId? = null
diff --git a/client/src/main/kotlin/net/corda/client/model/ContractStateModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt
similarity index 94%
rename from client/src/main/kotlin/net/corda/client/model/ContractStateModel.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt
index 33ec550322..4ed8c99ca5 100644
--- a/client/src/main/kotlin/net/corda/client/model/ContractStateModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ContractStateModel.kt
@@ -1,10 +1,10 @@
-package net.corda.client.model
+package net.corda.client.jfx.model
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import kotlinx.support.jdk8.collections.removeIf
-import net.corda.client.fxutils.fold
-import net.corda.client.fxutils.map
+import net.corda.client.jfx.utils.fold
+import net.corda.client.jfx.utils.map
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
diff --git a/client/src/main/kotlin/net/corda/client/model/ExchangeRateModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ExchangeRateModel.kt
similarity index 95%
rename from client/src/main/kotlin/net/corda/client/model/ExchangeRateModel.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/model/ExchangeRateModel.kt
index a3108649fb..243d87843e 100644
--- a/client/src/main/kotlin/net/corda/client/model/ExchangeRateModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/ExchangeRateModel.kt
@@ -1,4 +1,4 @@
-package net.corda.client.model
+package net.corda.client.jfx.model
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableValue
diff --git a/client/src/main/kotlin/net/corda/client/model/Models.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt
similarity index 99%
rename from client/src/main/kotlin/net/corda/client/model/Models.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt
index 1d2b6ab460..57f939e672 100644
--- a/client/src/main/kotlin/net/corda/client/model/Models.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/Models.kt
@@ -1,4 +1,4 @@
-package net.corda.client.model
+package net.corda.client.jfx.model
import javafx.beans.property.ObjectProperty
import javafx.beans.value.ObservableValue
diff --git a/client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt
similarity index 90%
rename from client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt
index ff91f08a32..9c3b1d408b 100644
--- a/client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt
@@ -1,13 +1,13 @@
-package net.corda.client.model
+package net.corda.client.jfx.model
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import kotlinx.support.jdk8.collections.removeIf
-import net.corda.client.fxutils.firstOrDefault
-import net.corda.client.fxutils.firstOrNullObservable
-import net.corda.client.fxutils.fold
-import net.corda.client.fxutils.map
+import net.corda.client.jfx.utils.firstOrDefault
+import net.corda.client.jfx.utils.firstOrNullObservable
+import net.corda.client.jfx.utils.fold
+import net.corda.client.jfx.utils.map
import net.corda.core.crypto.CompositeKey
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange
@@ -48,4 +48,4 @@ class NetworkIdentityModel {
fun lookup(publicKey: PublicKey): ObservableValue = parties.firstOrDefault(notaries.firstOrNullObservable { it.notaryIdentity.owningKey.keys.any { it == publicKey } }) {
it.legalIdentity.owningKey.keys.any { it == publicKey }
}
-}
\ No newline at end of file
+}
diff --git a/client/src/main/kotlin/net/corda/client/model/NodeMonitorModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
similarity index 94%
rename from client/src/main/kotlin/net/corda/client/model/NodeMonitorModel.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
index 8569835beb..24b5611752 100644
--- a/client/src/main/kotlin/net/corda/client/model/NodeMonitorModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NodeMonitorModel.kt
@@ -1,4 +1,4 @@
-package net.corda.client.model
+package net.corda.client.jfx.model
import com.google.common.net.HostAndPort
import javafx.beans.property.SimpleObjectProperty
@@ -11,7 +11,6 @@ import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
-import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.CordaRPCClient
import rx.Observable
import rx.subjects.PublishSubject
@@ -52,8 +51,8 @@ class NodeMonitorModel {
* Register for updates to/from a given vault.
* TODO provide an unsubscribe mechanism
*/
- fun register(nodeHostAndPort: HostAndPort, sslConfig: SSLConfiguration, username: String, password: String) {
- val client = CordaRPCClient(nodeHostAndPort, sslConfig){
+ fun register(nodeHostAndPort: HostAndPort, username: String, password: String) {
+ val client = CordaRPCClient(nodeHostAndPort){
maxRetryInterval = 10.seconds.toMillis()
}
client.start(username, password)
@@ -97,4 +96,4 @@ class NodeMonitorModel {
proxyObservable.set(proxy)
}
-}
\ No newline at end of file
+}
diff --git a/client/src/main/kotlin/net/corda/client/model/TransactionDataModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/model/TransactionDataModel.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt
index 3a6c9eddd7..4a709616f1 100644
--- a/client/src/main/kotlin/net/corda/client/model/TransactionDataModel.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/TransactionDataModel.kt
@@ -1,11 +1,11 @@
-package net.corda.client.model
+package net.corda.client.jfx.model
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.collections.ObservableMap
-import net.corda.client.fxutils.*
+import net.corda.client.jfx.utils.*
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/AggregatedList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AggregatedList.kt
similarity index 99%
rename from client/src/main/kotlin/net/corda/client/fxutils/AggregatedList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AggregatedList.kt
index 99f7f28c49..801def3027 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/AggregatedList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AggregatedList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.ListChangeListener
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/AmountBindings.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AmountBindings.kt
similarity index 95%
rename from client/src/main/kotlin/net/corda/client/fxutils/AmountBindings.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AmountBindings.kt
index cbfc7020c4..5b88591d9e 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/AmountBindings.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AmountBindings.kt
@@ -1,10 +1,10 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.beans.binding.Bindings
import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import kotlinx.support.jdk8.collections.stream
-import net.corda.client.model.ExchangeRate
+import net.corda.client.jfx.model.ExchangeRate
import net.corda.core.contracts.Amount
import org.fxmisc.easybind.EasyBind
import java.util.*
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/AssociatedList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AssociatedList.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/AssociatedList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AssociatedList.kt
index d610f43d97..6bac0fcb47 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/AssociatedList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/AssociatedList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.ListChangeListener
import javafx.collections.ObservableList
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/ChosenList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ChosenList.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/ChosenList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ChosenList.kt
index 32a618b851..77447feb58 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/ChosenList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ChosenList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.beans.Observable
import javafx.beans.value.ObservableValue
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/ConcatenatedList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ConcatenatedList.kt
similarity index 99%
rename from client/src/main/kotlin/net/corda/client/fxutils/ConcatenatedList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ConcatenatedList.kt
index 280f8293d0..d6f665d397 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/ConcatenatedList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ConcatenatedList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import co.paralleluniverse.common.util.VisibleForTesting
import javafx.collections.ListChangeListener
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/FlattenedList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/FlattenedList.kt
similarity index 99%
rename from client/src/main/kotlin/net/corda/client/fxutils/FlattenedList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/FlattenedList.kt
index 35531f8ede..8e9bcf7d98 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/FlattenedList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/FlattenedList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/LeftOuterJoinedMap.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/LeftOuterJoinedMap.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/LeftOuterJoinedMap.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/LeftOuterJoinedMap.kt
index 4e20966128..3a2be71518 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/LeftOuterJoinedMap.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/LeftOuterJoinedMap.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableValue
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/MapValuesList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MapValuesList.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/MapValuesList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MapValuesList.kt
index 90e3cef0c2..37c0963f0d 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/MapValuesList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MapValuesList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/MappedList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MappedList.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/MappedList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MappedList.kt
index 9747b92b86..3578646ccc 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/MappedList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/MappedList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.ListChangeListener
import javafx.collections.ObservableList
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/ObservableFold.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableFold.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/ObservableFold.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableFold.kt
index d9c34b11dd..aa5187aa3e 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/ObservableFold.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableFold.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.application.Platform
import javafx.beans.property.SimpleObjectProperty
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/ObservableUtilities.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt
similarity index 99%
rename from client/src/main/kotlin/net/corda/client/fxutils/ObservableUtilities.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt
index 1cabac56a2..b39cc8a8d0 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/ObservableUtilities.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ObservableUtilities.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.beans.binding.Bindings
import javafx.beans.binding.BooleanBinding
@@ -306,4 +306,4 @@ fun ObservableList.firstOrDefault(default: ObservableValue, predicate
*/
fun ObservableList.firstOrNullObservable(predicate: (A) -> Boolean): ObservableValue {
return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, arrayOf(this))
-}
\ No newline at end of file
+}
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/ReadOnlyBackedObservableMapBase.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ReadOnlyBackedObservableMapBase.kt
similarity index 99%
rename from client/src/main/kotlin/net/corda/client/fxutils/ReadOnlyBackedObservableMapBase.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ReadOnlyBackedObservableMapBase.kt
index 74020206fd..30c3c334b4 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/ReadOnlyBackedObservableMapBase.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ReadOnlyBackedObservableMapBase.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import com.sun.javafx.collections.MapListenerHelper
import javafx.beans.InvalidationListener
diff --git a/client/src/main/kotlin/net/corda/client/fxutils/ReplayedList.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ReplayedList.kt
similarity index 98%
rename from client/src/main/kotlin/net/corda/client/fxutils/ReplayedList.kt
rename to client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ReplayedList.kt
index 162107b5a0..61ca306485 100644
--- a/client/src/main/kotlin/net/corda/client/fxutils/ReplayedList.kt
+++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/utils/ReplayedList.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.ListChangeListener
import javafx.collections.ObservableList
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/AggregatedListTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/AggregatedListTest.kt
similarity index 98%
rename from client/src/test/kotlin/net/corda/client/fxutils/AggregatedListTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/AggregatedListTest.kt
index 19f457d0a8..b04817f62d 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/AggregatedListTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/AggregatedListTest.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.ObservableList
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/AssociatedListTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/AssociatedListTest.kt
similarity index 98%
rename from client/src/test/kotlin/net/corda/client/fxutils/AssociatedListTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/AssociatedListTest.kt
index 5feac46a2d..052b422c01 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/AssociatedListTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/AssociatedListTest.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.ObservableList
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/ConcatenatedListTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ConcatenatedListTest.kt
similarity index 99%
rename from client/src/test/kotlin/net/corda/client/fxutils/ConcatenatedListTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ConcatenatedListTest.kt
index 001d567ac7..effbed01a7 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/ConcatenatedListTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ConcatenatedListTest.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.ObservableList
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/FlattenedListTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/FlattenedListTest.kt
similarity index 99%
rename from client/src/test/kotlin/net/corda/client/fxutils/FlattenedListTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/FlattenedListTest.kt
index d972c68a5b..36b55233c7 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/FlattenedListTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/FlattenedListTest.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/LeftOuterJoinedMapTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/LeftOuterJoinedMapTest.kt
similarity index 99%
rename from client/src/test/kotlin/net/corda/client/fxutils/LeftOuterJoinedMapTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/LeftOuterJoinedMapTest.kt
index eb6bbc18da..5a1d8c2937 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/LeftOuterJoinedMapTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/LeftOuterJoinedMapTest.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.ObservableList
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/MappedListTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/MappedListTest.kt
similarity index 98%
rename from client/src/test/kotlin/net/corda/client/fxutils/MappedListTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/MappedListTest.kt
index aa9551aef3..be8783ef97 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/MappedListTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/MappedListTest.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
import javafx.collections.ObservableList
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/ReplayedListTest.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ReplayedListTest.kt
similarity index 93%
rename from client/src/test/kotlin/net/corda/client/fxutils/ReplayedListTest.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ReplayedListTest.kt
index 4857e01207..b645221788 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/ReplayedListTest.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ReplayedListTest.kt
@@ -1,13 +1,14 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.FXCollections
+import javafx.collections.ObservableList
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
class ReplayedListTest {
- var sourceList = FXCollections.observableArrayList(1234)
+ var sourceList: ObservableList = FXCollections.observableArrayList(1234)
var replayedList = ReplayedList(sourceList)
@Before
diff --git a/client/src/test/kotlin/net/corda/client/fxutils/ReplayedMap.kt b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ReplayedMap.kt
similarity index 95%
rename from client/src/test/kotlin/net/corda/client/fxutils/ReplayedMap.kt
rename to client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ReplayedMap.kt
index afe3d3ce61..1b1e69daaa 100644
--- a/client/src/test/kotlin/net/corda/client/fxutils/ReplayedMap.kt
+++ b/client/jfx/src/test/kotlin/net/corda/client/jfx/utils/ReplayedMap.kt
@@ -1,4 +1,4 @@
-package net.corda.client.fxutils
+package net.corda.client.jfx.utils
import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap
diff --git a/client/mock/build.gradle b/client/mock/build.gradle
new file mode 100644
index 0000000000..ec64233971
--- /dev/null
+++ b/client/mock/build.gradle
@@ -0,0 +1,44 @@
+apply plugin: 'kotlin'
+apply plugin: 'net.corda.plugins.quasar-utils'
+apply plugin: 'net.corda.plugins.publish-utils'
+
+description 'Corda client mock modules'
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url 'http://oss.sonatype.org/content/repositories/snapshots'
+ }
+ jcenter()
+ maven {
+ url 'https://dl.bintray.com/kotlin/exposed'
+ }
+}
+
+//noinspection GroovyAssignabilityCheck
+configurations {
+ // we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment
+ runtime.exclude module: 'isolated'
+}
+
+sourceSets {
+ test {
+ resources {
+ srcDir "../../config/test"
+ }
+ }
+}
+
+// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
+// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
+
+dependencies {
+ compile project(":node")
+
+ // Unit testing helpers.
+ testCompile "junit:junit:$junit_version"
+ testCompile "org.assertj:assertj-core:${assertj_version}"
+
+ testCompile project(':test-utils')
+}
diff --git a/client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt b/client/mock/src/main/kotlin/net/corda/client/mock/EventGenerator.kt
similarity index 95%
rename from client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt
rename to client/mock/src/main/kotlin/net/corda/client/mock/EventGenerator.kt
index c343878cc4..99eed3a4e0 100644
--- a/client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt
+++ b/client/mock/src/main/kotlin/net/corda/client/mock/EventGenerator.kt
@@ -73,11 +73,9 @@ class EventGenerator(
}
val moveCashGenerator =
- amountIssuedGenerator.combine(
- partyGenerator
- ) { amountIssued, recipient ->
+ amountIssuedGenerator.combine(partyGenerator) { amountIssued, recipient ->
CashFlowCommand.PayCash(
- amount = amountIssued,
+ amount = amountIssued.withoutIssuer(),
recipient = recipient
)
}
@@ -101,4 +99,4 @@ class EventGenerator(
val bankOfCordaIssueGenerator = Generator.frequency(
0.6 to issueCashGenerator
)
-}
\ No newline at end of file
+}
diff --git a/client/src/main/kotlin/net/corda/client/mock/Generator.kt b/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt
similarity index 71%
rename from client/src/main/kotlin/net/corda/client/mock/Generator.kt
rename to client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt
index 660fd8054d..bf737bc898 100644
--- a/client/src/main/kotlin/net/corda/client/mock/Generator.kt
+++ b/client/mock/src/main/kotlin/net/corda/client/mock/Generator.kt
@@ -31,42 +31,42 @@ import java.util.*
*
* The above will generate a random list of animals.
*/
-class Generator(val generate: (SplittableRandom) -> ErrorOr) {
+class Generator(val generate: (SplittableRandom) -> ErrorOr) {
// Functor
- fun map(function: (A) -> B): Generator =
+ fun map(function: (A) -> B): Generator =
Generator { generate(it).map(function) }
// Applicative
- fun product(other: Generator<(A) -> B>) =
+ fun product(other: Generator<(A) -> B>) =
Generator { generate(it).combine(other.generate(it)) { a, f -> f(a) } }
- fun combine(other1: Generator, function: (A, B) -> R) =
+ fun combine(other1: Generator, function: (A, B) -> R) =
product(other1.product(pure({ b -> { a -> function(a, b) } })))
- fun combine(other1: Generator, other2: Generator, function: (A, B, C) -> R) =
+ fun combine(other1: Generator, other2: Generator, function: (A, B, C) -> R) =
product(other1.product(other2.product(pure({ c -> { b -> { a -> function(a, b, c) } } }))))
- fun combine(other1: Generator, other2: Generator, other3: Generator, function: (A, B, C, D) -> R) =
+ fun combine(other1: Generator, other2: Generator, other3: Generator, function: (A, B, C, D) -> R) =
product(other1.product(other2.product(other3.product(pure({ d -> { c -> { b -> { a -> function(a, b, c, d) } } } })))))
- fun combine(other1: Generator, other2: Generator, other3: Generator, other4: Generator, function: (A, B, C, D, E) -> R) =
+ fun combine(other1: Generator, other2: Generator, other3: Generator, other4: Generator, function: (A, B, C, D, E) -> R) =
product(other1.product(other2.product(other3.product(other4.product(pure({ e -> { d -> { c -> { b -> { a -> function(a, b, c, d, e) } } } } }))))))
// Monad
- fun bind(function: (A) -> Generator) =
+ fun bind(function: (A) -> Generator) =
Generator { generate(it).bind { a -> function(a).generate(it) } }
companion object {
- fun pure(value: A) = Generator { ErrorOr(value) }
- fun impure(valueClosure: () -> A) = Generator { ErrorOr(valueClosure()) }
- fun fail(error: Exception) = Generator { ErrorOr.of(error) }
+ fun pure(value: A) = Generator { ErrorOr(value) }
+ fun impure(valueClosure: () -> A) = Generator { ErrorOr(valueClosure()) }
+ fun fail(error: Exception) = Generator { ErrorOr.of(error) }
// Alternative
- fun choice(generators: List>) = intRange(0, generators.size - 1).bind { generators[it] }
+ fun choice(generators: List>) = intRange(0, generators.size - 1).bind { generators[it] }
- fun success(generate: (SplittableRandom) -> A) = Generator { ErrorOr(generate(it)) }
- fun frequency(generators: List>>): Generator {
+ fun success(generate: (SplittableRandom) -> A) = Generator { ErrorOr(generate(it)) }
+ fun frequency(generators: List>>): Generator {
val ranges = mutableListOf>()
var current = 0.0
generators.forEach {
@@ -87,7 +87,7 @@ class Generator(val generate: (SplittableRandom) -> ErrorOr) {
}
}
- fun sequence(generators: List>) = Generator> {
+ fun sequence(generators: List>) = Generator> {
val result = mutableListOf()
for (generator in generators) {
val element = generator.generate(it)
@@ -103,9 +103,9 @@ class Generator(val generate: (SplittableRandom) -> ErrorOr) {
}
}
-fun Generator.Companion.frequency(vararg generators: Pair>) = frequency(generators.toList())
+fun Generator.Companion.frequency(vararg generators: Pair>) = frequency(generators.toList())
-fun Generator.generateOrFail(random: SplittableRandom, numberOfTries: Int = 1): A {
+fun Generator.generateOrFail(random: SplittableRandom, numberOfTries: Int = 1): A {
var error: Throwable? = null
for (i in 0..numberOfTries - 1) {
val result = generate(random)
@@ -124,6 +124,7 @@ fun Generator.generateOrFail(random: SplittableRandom, numberOfTrie
}
fun Generator.Companion.int() = Generator.success(SplittableRandom::nextInt)
+fun Generator.Companion.long() = Generator.success(SplittableRandom::nextLong)
fun Generator.Companion.bytes(size: Int): Generator = Generator.success { random ->
ByteArray(size) { random.nextInt().toByte() }
}
@@ -143,7 +144,7 @@ fun Generator.Companion.doubleRange(from: Double, to: Double): Generator
from + it.nextDouble() * (to - from)
}
-fun Generator.Companion.replicate(number: Int, generator: Generator): Generator> {
+fun Generator.Companion.replicate(number: Int, generator: Generator): Generator> {
val generators = mutableListOf>()
for (i in 1..number) {
generators.add(generator)
@@ -152,7 +153,7 @@ fun Generator.Companion.replicate(number: Int, generator: Generator
}
-fun Generator.Companion.replicatePoisson(meanSize: Double, generator: Generator) = Generator> {
+fun Generator.Companion.replicatePoisson(meanSize: Double, generator: Generator) = Generator> {
val chance = (meanSize - 1) / meanSize
val result = mutableListOf()
var finish = false
@@ -173,8 +174,8 @@ fun Generator.Companion.replicatePoisson(meanSize: Double, generator:
ErrorOr(result)
}
-fun Generator.Companion.pickOne(list: List) = Generator.intRange(0, list.size - 1).map { list[it] }
-fun Generator.Companion.pickN(number: Int, list: List) = Generator> {
+fun Generator.Companion.pickOne(list: List) = Generator.intRange(0, list.size - 1).map { list[it] }
+fun Generator.Companion.pickN(number: Int, list: List) = Generator> {
val mask = BitSet(list.size)
val size = Math.min(list.size, number)
for (i in 0..size - 1) {
@@ -199,15 +200,13 @@ fun Generator.Companion.pickN(number: Int, list: List) = Generator<
fun Generator.Companion.sampleBernoulli(maxRatio: Double = 1.0, vararg collection: A) =
sampleBernoulli(listOf(collection), maxRatio)
-fun Generator.Companion.sampleBernoulli(collection: Collection, maxRatio: Double = 1.0): Generator> =
- intRange(0, (maxRatio * collection.size).toInt()).bind { howMany ->
- replicate(collection.size, Generator.doubleRange(0.0, 1.0)).map { chances ->
- val result = mutableListOf()
- collection.forEachIndexed { index, element ->
- if (chances[index] < howMany.toDouble() / collection.size.toDouble()) {
- result.add(element)
- }
+fun Generator.Companion.sampleBernoulli(collection: Collection, meanRatio: Double = 1.0): Generator> =
+ replicate(collection.size, Generator.doubleRange(0.0, 1.0)).map { chances ->
+ val result = mutableListOf()
+ collection.forEachIndexed { index, element ->
+ if (chances[index] < meanRatio) {
+ result.add(element)
}
- result
}
+ result
}
diff --git a/client/src/main/kotlin/net/corda/client/mock/Generators.kt b/client/mock/src/main/kotlin/net/corda/client/mock/Generators.kt
similarity index 100%
rename from client/src/main/kotlin/net/corda/client/mock/Generators.kt
rename to client/mock/src/main/kotlin/net/corda/client/mock/Generators.kt
diff --git a/config/dev/generalnodea.conf b/config/dev/generalnodea.conf
index 77f03585dd..366c5bbfc1 100644
--- a/config/dev/generalnodea.conf
+++ b/config/dev/generalnodea.conf
@@ -2,11 +2,12 @@ myLegalName : "Bank A"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
-artemisAddress : "localhost:31337"
-webAddress : "localhost:31339"
+p2pAddress : "localhost:10002"
+rpcAddress : "localhost:10003"
+webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
- address : "localhost:12345"
+ address : "localhost:10000"
legalName : "Network Map Service"
}
useHTTPS : false
diff --git a/config/dev/generalnodeb.conf b/config/dev/generalnodeb.conf
index 3b7915efe5..a6c4aa3361 100644
--- a/config/dev/generalnodeb.conf
+++ b/config/dev/generalnodeb.conf
@@ -2,11 +2,12 @@ myLegalName : "Bank B"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
-artemisAddress : "localhost:31338"
-webAddress : "localhost:31340"
+p2pAddress : "localhost:10005"
+rpcAddress : "localhost:10006"
+webAddress : "localhost:10007"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
- address : "localhost:12345"
+ address : "localhost:10000"
legalName : "Network Map Service"
}
useHTTPS : false
diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml
index caa9eb427f..820d5d73f1 100644
--- a/config/dev/log4j2.xml
+++ b/config/dev/log4j2.xml
@@ -16,6 +16,11 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/dev/nameservernode.conf b/config/dev/nameservernode.conf
index cfa3390671..feab919ab2 100644
--- a/config/dev/nameservernode.conf
+++ b/config/dev/nameservernode.conf
@@ -2,7 +2,7 @@ myLegalName : "Notary Service"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
-artemisAddress : "localhost:12345"
-webAddress : "localhost:12346"
+p2pAddress : "localhost:10000"
+webAddress : "localhost:10001"
extraAdvertisedServiceIds : [ "corda.notary.validating" ]
-useHTTPS : false
\ No newline at end of file
+useHTTPS : false
diff --git a/config/test/log4j2.xml b/config/test/log4j2.xml
index d656719c10..eca12924ef 100644
--- a/config/test/log4j2.xml
+++ b/config/test/log4j2.xml
@@ -7,6 +7,10 @@
+
+
+
+
@@ -15,5 +19,8 @@
+
+
+
diff --git a/core/build.gradle b/core/build.gradle
index 4445fc058e..b9241e4a47 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -50,10 +50,13 @@ dependencies {
// Thread safety annotations
compile "com.google.code.findbugs:jsr305:3.0.1"
- // Log4J: logging framework (with SLF4J bindings)
- compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
+ // Log4J: logging framework (ONLY explicitly referenced by net.corda.core.utilities.Logging.kt)
compile "org.apache.logging.log4j:log4j-core:${log4j_version}"
+ // SLF4J: commons-logging bindings for a SLF4J back end
+ compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
+ compile "org.slf4j:slf4j-api:$slf4j_version"
+
// AssertJ: for fluent assertions for testing
testCompile "org.assertj:assertj-core:${assertj_version}"
@@ -91,8 +94,14 @@ dependencies {
compile "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final"
// RS API: Response type and codes for ApiUtils.
- compile "javax.ws.rs:javax.ws.rs-api:2.0"
+ compile "javax.ws.rs:javax.ws.rs-api:2.0.1"
// Requery: SQL based query & persistence for Kotlin
compile "io.requery:requery-kotlin:$requery_version"
}
+
+configurations.compile {
+ // We want to use SLF4J's version of these binding: jcl-over-slf4j
+ // Remove any transitive dependency on Apache's version.
+ exclude group: 'commons-logging', module: 'commons-logging'
+}
diff --git a/core/src/main/kotlin/net/corda/core/Utils.kt b/core/src/main/kotlin/net/corda/core/Utils.kt
index 6b4969bdce..ca39b64fcd 100644
--- a/core/src/main/kotlin/net/corda/core/Utils.kt
+++ b/core/src/main/kotlin/net/corda/core/Utils.kt
@@ -357,7 +357,7 @@ data class ErrorOr private constructor(val value: A?, val error: Throwabl
}
// Monad
- fun bind(function: (A) -> ErrorOr): ErrorOr {
+ fun bind(function: (A) -> ErrorOr): ErrorOr {
return if (error == null) {
function(value as A)
} else {
diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt
index 922d222403..30eef737c8 100644
--- a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt
+++ b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt
@@ -27,7 +27,9 @@ fun commodity(code: String) = Commodity.getInstance(code)!!
@JvmField val GBP = currency("GBP")
@JvmField val EUR = currency("EUR")
@JvmField val CHF = currency("CHF")
-@JvmField val FCOJ = commodity("FCOJ")
+@JvmField val JPY = currency("JPY")
+@JvmField val RUB = currency("RUB")
+@JvmField val FCOJ = commodity("FCOJ") // Frozen concentrated orange juice, yum!
fun DOLLARS(amount: Int): Amount = Amount(amount.toLong() * 100, USD)
fun DOLLARS(amount: Double): Amount = Amount((amount * 100).toLong(), USD)
diff --git a/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt b/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt
index bcdca06360..c545901a2c 100644
--- a/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt
+++ b/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt
@@ -39,7 +39,7 @@ import java.util.*
data class Amount(val quantity: Long, val token: T) : Comparable> {
companion object {
/**
- * Build an amount from a decimal representation. For example, with an input of "12.34" GBP,
+ * Build a currency amount from a decimal representation. For example, with an input of "12.34" GBP,
* returns an amount with a quantity of "1234".
*
* @see Amount.toDecimal
@@ -48,6 +48,66 @@ data class Amount(val quantity: Long, val token: T) : Comparable> {
val longQuantity = quantity.movePointRight(currency.defaultFractionDigits).toLong()
return Amount(longQuantity, currency)
}
+
+ private val currencySymbols: Map = mapOf(
+ "$" to USD,
+ "£" to GBP,
+ "€" to EUR,
+ "¥" to JPY,
+ "₽" to RUB
+ )
+ private val currencyCodes: Map by lazy { Currency.getAvailableCurrencies().map { it.currencyCode to it }.toMap() }
+
+ /**
+ * Returns an amount that is equal to the given currency amount in text. Examples of what is supported:
+ *
+ * - 12 USD
+ * - 14.50 USD
+ * - 10 USD
+ * - 30 CHF
+ * - $10.24
+ * - £13
+ * - €5000
+ *
+ * Note this method does NOT respect internationalisation rules: it ignores commas and uses . as the
+ * decimal point separator, always. It also ignores the users locale:
+ *
+ * - $ is always USD,
+ * - £ is always GBP
+ * - € is always the Euro
+ * - ¥ is always Japanese Yen.
+ * - ₽ is always the Russian ruble.
+ *
+ * Thus an input of $12 expecting some other countries dollar will not work. Do your own parsing if
+ * you need correct handling of currency amounts with locale-sensitive handling.
+ *
+ * @throws IllegalArgumentException if the input string was not understood.
+ */
+ fun parseCurrency(input: String): Amount {
+ val i = input.filter { it != ',' }
+ try {
+ // First check the symbols at the front.
+ for ((symbol, currency) in currencySymbols) {
+ if (i.startsWith(symbol)) {
+ val rest = i.substring(symbol.length)
+ return fromDecimal(BigDecimal(rest), currency)
+ }
+ }
+ // Now check the codes at the end.
+ val split = i.split(' ')
+ if (split.size == 2) {
+ val (rest, code) = split
+ for ((cc, currency) in currencyCodes) {
+ if (cc == code) {
+ return fromDecimal(BigDecimal(rest), currency)
+ }
+ }
+ }
+ } catch(e: Exception) {
+ throw IllegalArgumentException("Could not parse $input as a currency", e)
+ }
+ throw IllegalArgumentException("Did not recognise the currency in $input or could not parse")
+ }
}
init {
@@ -65,17 +125,17 @@ data class Amount(val quantity: Long, val token: T) : Comparable> {
constructor(quantity: BigInteger, token: T) : this(quantity.toLong(), token)
operator fun plus(other: Amount): Amount {
- checkCurrency(other)
+ checkToken(other)
return Amount(Math.addExact(quantity, other.quantity), token)
}
operator fun minus(other: Amount): Amount {
- checkCurrency(other)
+ checkToken(other)
return Amount(Math.subtractExact(quantity, other.quantity), token)
}
- private fun checkCurrency(other: Amount) {
- require(other.token == token) { "Currency mismatch: ${other.token} vs $token" }
+ private fun checkToken(other: Amount) {
+ require(other.token == token) { "Token mismatch: ${other.token} vs $token" }
}
operator fun div(other: Long): Amount = Amount(quantity / other, token)
@@ -83,10 +143,16 @@ data class Amount(val quantity: Long, val token: T) : Comparable> {
operator fun div(other: Int): Amount = Amount(quantity / other, token)
operator fun times(other: Int): Amount = Amount(Math.multiplyExact(quantity, other.toLong()), token)
- override fun toString(): String = (BigDecimal(quantity).divide(BigDecimal(100))).setScale(2).toPlainString() + " " + token
+ override fun toString(): String {
+ val bd = if (token is Currency)
+ BigDecimal(quantity).movePointLeft(token.defaultFractionDigits)
+ else
+ BigDecimal(quantity)
+ return bd.toPlainString() + " " + token
+ }
override fun compareTo(other: Amount): Int {
- checkCurrency(other)
+ checkToken(other)
return quantity.compareTo(other.quantity)
}
}
diff --git a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt
index 50651b1ea0..8b346bb185 100644
--- a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt
+++ b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt
@@ -3,9 +3,7 @@ package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.flows.FlowException
-class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException() {
- override fun toString() = "Insufficient balance, missing $amountMissing"
-}
+class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing")
/**
* Interface for contract states representing assets which are fungible, countable and issued by a
diff --git a/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt
index f8a2398e58..d27756a355 100644
--- a/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt
+++ b/core/src/main/kotlin/net/corda/core/node/PluginServiceHub.kt
@@ -10,18 +10,20 @@ import kotlin.reflect.KClass
interface PluginServiceHub : ServiceHub {
/**
* Register the flow factory we wish to use when a initiating party attempts to communicate with us. The
- * registration is done against a marker [KClass] which is sent in the session handshake by the other party. If this
+ * registration is done against a marker [Class] which is sent in the session handshake by the other party. If this
* marker class has been registered then the corresponding factory will be used to create the flow which will
* communicate with the other side. If there is no mapping then the session attempt is rejected.
- * @param markerClass The marker [KClass] present in a session initiation attempt, which is a 1:1 mapping to a [Class]
- * using the ::class
construct. Conventionally this is a [FlowLogic] subclass, however any class can
- * be used, with the default being the class of the initiating flow. This enables the registration to be of the
- * form: registerFlowInitiator(InitiatorFlow::class, ::InitiatedFlow)
+ * @param markerClass The marker [Class] present in a session initiation attempt. Conventionally this is a [FlowLogic]
+ * subclass, however any class can be used, with the default being the class of the initiating flow. This enables
+ * the registration to be of the form: `registerFlowInitiator(InitiatorFlow.class, InitiatedFlow::new)`
* @param flowFactory The flow factory generating the initiated flow.
*/
+ fun registerFlowInitiator(markerClass: Class<*>, flowFactory: (Party) -> FlowLogic<*>)
- // TODO: remove dependency on Kotlin relfection (Kotlin KClass -> Java Class).
- fun registerFlowInitiator(markerClass: KClass<*>, flowFactory: (Party) -> FlowLogic<*>)
+ @Deprecated(message = "Use overloaded method which uses Class instead of KClass. This is scheduled for removal in a future release.")
+ fun registerFlowInitiator(markerClass: KClass<*>, flowFactory: (Party) -> FlowLogic<*>) {
+ registerFlowInitiator(markerClass.java, flowFactory)
+ }
/**
* Return the flow factory that has been registered with [markerClass], or null if no factory is found.
diff --git a/core/src/main/kotlin/net/corda/core/node/services/Services.kt b/core/src/main/kotlin/net/corda/core/node/services/Services.kt
index a0685c2f41..96deae575d 100644
--- a/core/src/main/kotlin/net/corda/core/node/services/Services.kt
+++ b/core/src/main/kotlin/net/corda/core/node/services/Services.kt
@@ -203,15 +203,15 @@ interface VaultService {
onlyFromParties: Set? = null): Pair>
/**
- * Return [ContractState]s of a given [Contract] type and list of [Vault.StateStatus]
+ * Return [ContractState]s of a given [Contract] type and [Iterable] of [Vault.StateStatus].
*/
- fun states(clazzes: Set>, statuses: EnumSet): List>
+ fun states(clazzes: Set>, statuses: EnumSet): Iterable>
}
-inline fun VaultService.unconsumedStates(): List> =
+inline fun VaultService.unconsumedStates(): Iterable> =
states(setOf(T::class.java), EnumSet.of(Vault.StateStatus.UNCONSUMED))
-inline fun VaultService.consumedStates(): List> =
+inline fun VaultService.consumedStates(): Iterable> =
states(setOf(T::class.java), EnumSet.of(Vault.StateStatus.CONSUMED))
/** Returns the [linearState] heads only when the type of the state would be considered an 'instanceof' the given type. */
diff --git a/core/src/main/kotlin/net/corda/core/serialization/CordaClassResolver.kt b/core/src/main/kotlin/net/corda/core/serialization/CordaClassResolver.kt
index 4b6350ba03..a1743230f5 100644
--- a/core/src/main/kotlin/net/corda/core/serialization/CordaClassResolver.kt
+++ b/core/src/main/kotlin/net/corda/core/serialization/CordaClassResolver.kt
@@ -92,6 +92,24 @@ class CordaClassResolver(val whitelist: ClassWhitelist) : DefaultClassResolver()
return type.interfaces.any { it.isAnnotationPresent(CordaSerializable::class.java) || hasAnnotationOnInterface(it) }
|| (type.superclass != null && hasAnnotationOnInterface(type.superclass))
}
+
+ // Need to clear out class names from attachments.
+ override fun reset() {
+ super.reset()
+ // Kryo creates a cache of class name to Class<*> which does not work so well with multiple class loaders.
+ // TODO: come up with a more efficient way. e.g. segregate the name space by class loader.
+ if(nameToClass != null) {
+ val classesToRemove: MutableList = ArrayList(nameToClass.size)
+ for (entry in nameToClass.entries()) {
+ if (entry.value.classLoader is AttachmentsClassLoader) {
+ classesToRemove += entry.key
+ }
+ }
+ for (className in classesToRemove) {
+ nameToClass.remove(className)
+ }
+ }
+ }
}
interface ClassWhitelist {
diff --git a/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt b/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt
index d961cbe195..42d9f4719c 100644
--- a/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt
+++ b/core/src/main/kotlin/net/corda/core/serialization/DefaultKryoCustomizer.kt
@@ -17,6 +17,7 @@ import net.corda.core.utilities.NonEmptySetSerializer
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.objenesis.strategy.StdInstantiatorStrategy
+import org.slf4j.Logger
import java.io.BufferedInputStream
import java.util.*
@@ -34,7 +35,7 @@ object DefaultKryoCustomizer {
// for change to a class.
setDefaultSerializer(CompatibleFieldSerializer::class.java)
// Take the safest route here and allow subclasses to have fields named the same as super classes.
- fieldSerializerConfig.setCachedFieldNameStrategy(FieldSerializer.CachedFieldNameStrategy.EXTENDED)
+ fieldSerializerConfig.cachedFieldNameStrategy = FieldSerializer.CachedFieldNameStrategy.EXTENDED
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
// no-arg constructor available.
@@ -78,8 +79,10 @@ object DefaultKryoCustomizer {
register(MetaData::class.java, MetaDataSerializer)
register(BitSet::class.java, ReferencesAwareJavaSerializer)
+ addDefaultSerializer(Logger::class.java, LoggerSerializer)
+
val customization = KryoSerializationCustomization(this)
pluginRegistries.forEach { it.customizeSerialization(customization) }
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt
index cb6d20523c..ef93e233d9 100644
--- a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt
+++ b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt
@@ -3,9 +3,10 @@ package net.corda.core.serialization
import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
+import com.esotericsoftware.kryo.pool.KryoPool
import com.esotericsoftware.kryo.serializers.JavaSerializer
-import com.esotericsoftware.kryo.serializers.MapSerializer
import com.esotericsoftware.kryo.util.MapReferenceResolver
+import com.google.common.annotations.VisibleForTesting
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.node.AttachmentsClassLoader
@@ -15,6 +16,8 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.io.*
import java.lang.reflect.InvocationTargetException
import java.nio.file.Files
@@ -60,12 +63,9 @@ import kotlin.reflect.jvm.javaType
*/
// A convenient instance of Kryo pre-configured with some useful things. Used as a default by various functions.
-private val THREAD_LOCAL_KRYO: ThreadLocal = ThreadLocal.withInitial { createKryo() }
+fun p2PKryo(): KryoPool = kryoPool
// Same again, but this has whitelisting turned off for internal storage use only.
-private val INTERNAL_THREAD_LOCAL_KRYO: ThreadLocal = ThreadLocal.withInitial { createInternalKryo() }
-
-fun threadLocalP2PKryo(): Kryo = THREAD_LOCAL_KRYO.get()
-fun threadLocalStorageKryo(): Kryo = INTERNAL_THREAD_LOCAL_KRYO.get()
+fun storageKryo(): KryoPool = internalKryoPool
/**
* A type safe wrapper around a byte array that contains a serialised object. You can call [SerializedBytes.deserialize]
@@ -82,26 +82,34 @@ class SerializedBytes(bytes: ByteArray, val internalOnly: Boolean = fal
private val KryoHeaderV0_1: OpaqueBytes = OpaqueBytes("corda\u0000\u0000\u0001".toByteArray())
// Some extension functions that make deserialisation convenient and provide auto-casting of the result.
-fun ByteArray.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
+fun ByteArray.deserialize(kryo: KryoPool = p2PKryo()): T {
Input(this).use {
val header = OpaqueBytes(it.readBytes(8))
if (header != KryoHeaderV0_1) {
throw KryoException("Serialized bytes header does not match any known format.")
}
@Suppress("UNCHECKED_CAST")
- return kryo.readClassAndObject(it) as T
+ return kryo.run { k -> k.readClassAndObject(it) as T }
}
}
-fun OpaqueBytes.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
+// TODO: The preferred usage is with a pool. Try and eliminate use of this from RPC.
+fun ByteArray.deserialize(kryo: Kryo): T = deserialize(kryo.asPool())
+
+fun OpaqueBytes.deserialize(kryo: KryoPool = p2PKryo()): T {
return this.bytes.deserialize(kryo)
}
// The more specific deserialize version results in the bytes being cached, which is faster.
@JvmName("SerializedBytesWireTransaction")
-fun SerializedBytes.deserialize(kryo: Kryo = threadLocalP2PKryo()): WireTransaction = WireTransaction.deserialize(this, kryo)
+fun SerializedBytes.deserialize(kryo: KryoPool = p2PKryo()): WireTransaction = WireTransaction.deserialize(this, kryo)
-fun SerializedBytes.deserialize(kryo: Kryo = if (internalOnly) threadLocalStorageKryo() else threadLocalP2PKryo()): T = bytes.deserialize(kryo)
+fun SerializedBytes.deserialize(kryo: KryoPool = if (internalOnly) storageKryo() else p2PKryo()): T = bytes.deserialize(kryo)
+
+fun SerializedBytes.deserialize(kryo: Kryo): T = bytes.deserialize(kryo.asPool())
+
+// Internal adapter for use when we haven't yet converted to a pool, or for tests.
+private fun Kryo.asPool(): KryoPool = (KryoPool.Builder { this }.build())
/**
* A serialiser that avoids writing the wrapper class to the byte stream, thus ensuring [SerializedBytes] is a pure
@@ -122,7 +130,11 @@ object SerializedBytesSerializer : Serializer>() {
* Can be called on any object to convert it to a byte array (wrapped by [SerializedBytes]), regardless of whether
* the type is marked as serializable or was designed for it (so be careful!).
*/
-fun T.serialize(kryo: Kryo = threadLocalP2PKryo(), internalOnly: Boolean = false): SerializedBytes {
+fun T.serialize(kryo: KryoPool = p2PKryo(), internalOnly: Boolean = false): SerializedBytes {
+ return kryo.run { k -> serialize(k, internalOnly) }
+}
+
+fun T.serialize(kryo: Kryo, internalOnly: Boolean = false): SerializedBytes {
val stream = ByteArrayOutputStream()
Output(stream).use {
it.writeBytes(KryoHeaderV0_1.bytes)
@@ -399,14 +411,12 @@ object KotlinObjectSerializer : Serializer() {
}
// No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
-fun createInternalKryo(k: Kryo = CordaKryo(makeNoWhitelistClassResolver())): Kryo {
- return DefaultKryoCustomizer.customize(k)
-}
+private val internalKryoPool = KryoPool.Builder { DefaultKryoCustomizer.customize(CordaKryo(makeNoWhitelistClassResolver())) }.build()
+private val kryoPool = KryoPool.Builder { DefaultKryoCustomizer.customize(CordaKryo(makeStandardClassResolver())) }.build()
// No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
-fun createKryo(k: Kryo = CordaKryo(makeStandardClassResolver())): Kryo {
- return DefaultKryoCustomizer.customize(k)
-}
+@VisibleForTesting
+fun createTestKryo(): Kryo = DefaultKryoCustomizer.customize(CordaKryo(makeNoWhitelistClassResolver()))
/**
* We need to disable whitelist checking during calls from our Kryo code to register a serializer, since it checks
@@ -475,21 +485,20 @@ inline fun Kryo.noReferencesWithin() {
class NoReferencesSerializer(val baseSerializer: Serializer) : Serializer() {
override fun read(kryo: Kryo, input: Input, type: Class): T {
- val previousValue = kryo.setReferences(false)
- try {
- return baseSerializer.read(kryo, input, type)
- } finally {
- kryo.references = previousValue
- }
+ return kryo.withoutReferences { baseSerializer.read(kryo, input, type) }
}
override fun write(kryo: Kryo, output: Output, obj: T) {
- val previousValue = kryo.setReferences(false)
- try {
- baseSerializer.write(kryo, output, obj)
- } finally {
- kryo.references = previousValue
- }
+ kryo.withoutReferences { baseSerializer.write(kryo, output, obj) }
+ }
+}
+
+fun Kryo.withoutReferences(block: () -> T): T {
+ val previousValue = setReferences(false)
+ try {
+ return block()
+ } finally {
+ references = previousValue
}
}
@@ -518,20 +527,16 @@ object ReferencesAwareJavaSerializer : JavaSerializer() {
val ATTACHMENT_STORAGE = "ATTACHMENT_STORAGE"
-var Kryo.attachmentStorage: AttachmentStorage?
+val Kryo.attachmentStorage: AttachmentStorage?
get() = this.context.get(ATTACHMENT_STORAGE, null) as AttachmentStorage?
- set(value) {
- this.context.put(ATTACHMENT_STORAGE, value)
- }
-
-//TODO: It's a little workaround for serialization of HashMaps inside contract states.
-//Used in Merkle tree calculation. It doesn't cover all the cases of unstable serialization format.
-fun extendKryoHash(kryo: Kryo): Kryo {
- return kryo.apply {
- references = false
- register(LinkedHashMap::class.java, MapSerializer())
- register(HashMap::class.java, OrderedSerializer)
+fun Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage?, block: () -> T): T {
+ val priorAttachmentStorage = this.attachmentStorage
+ this.context.put(ATTACHMENT_STORAGE, attachmentStorage)
+ try {
+ return block()
+ } finally {
+ this.context.put(ATTACHMENT_STORAGE, priorAttachmentStorage)
}
}
@@ -582,3 +587,15 @@ object MetaDataSerializer : Serializer() {
return MetaData(schemeCodeName, versionID, signatureType, timestamp, visibleInputs, signedInputs, merkleRoot, publicKey)
}
}
+
+/** For serialising a Logger. */
+@ThreadSafe
+object LoggerSerializer : Serializer() {
+ override fun write(kryo: Kryo, output: Output, obj: Logger) {
+ output.writeString(obj.name)
+ }
+
+ override fun read(kryo: Kryo, input: Input, type: Class): Logger {
+ return LoggerFactory.getLogger(input.readString())
+ }
+}
diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt
index e2e0a16b4d..798db529d9 100644
--- a/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt
+++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationToken.kt
@@ -5,7 +5,7 @@ import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
-import java.util.*
+import com.esotericsoftware.kryo.pool.KryoPool
/**
* The interfaces and classes in this file allow large, singleton style classes to
@@ -36,8 +36,6 @@ interface SerializationToken {
/**
* A Kryo serializer for [SerializeAsToken] implementations.
- *
- * This is registered in [createKryo].
*/
class SerializeAsTokenSerializer : Serializer() {
override fun write(kryo: Kryo, output: Output, obj: T) {
@@ -76,8 +74,8 @@ class SerializeAsTokenSerializer : Serializer() {
* Then it is a case of using the companion object methods on [SerializeAsTokenSerializer] to set and clear context as necessary
* on the Kryo instance when serializing to enable/disable tokenization.
*/
-class SerializeAsTokenContext(toBeTokenized: Any, kryo: Kryo = createKryo()) {
- internal val tokenToTokenized = HashMap()
+class SerializeAsTokenContext(toBeTokenized: Any, kryoPool: KryoPool) {
+ internal val tokenToTokenized = mutableMapOf()
internal var readOnly = false
init {
@@ -90,9 +88,11 @@ class SerializeAsTokenContext(toBeTokenized: Any, kryo: Kryo = createKryo()) {
* accidental registrations from occuring as these could not be deserialized in a deserialization-first
* scenario if they are not part of this iniital context construction serialization.
*/
- SerializeAsTokenSerializer.setContext(kryo, this)
- toBeTokenized.serialize(kryo)
- SerializeAsTokenSerializer.clearContext(kryo)
+ kryoPool.run { kryo ->
+ SerializeAsTokenSerializer.setContext(kryo, this)
+ toBeTokenized.serialize(kryo)
+ SerializeAsTokenSerializer.clearContext(kryo)
+ }
readOnly = true
}
}
diff --git a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt
index 96609692c0..dd080f5b1b 100644
--- a/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt
+++ b/core/src/main/kotlin/net/corda/core/transactions/MerkleTransaction.kt
@@ -3,13 +3,12 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.serialization.CordaSerializable
-import net.corda.core.serialization.createKryo
-import net.corda.core.serialization.extendKryoHash
+import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.serialize
+import net.corda.core.serialization.withoutReferences
fun serializedHash(x: T): SecureHash {
- val kryo = extendKryoHash(createKryo()) // Dealing with HashMaps inside states.
- return x.serialize(kryo).hash
+ return p2PKryo().run { kryo -> kryo.withoutReferences { x.serialize(kryo).hash } }
}
/**
diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt
index 244d06b192..77e0c5cd28 100644
--- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt
+++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt
@@ -1,6 +1,6 @@
package net.corda.core.transactions
-import com.esotericsoftware.kryo.Kryo
+import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.MerkleTree
@@ -10,8 +10,8 @@ import net.corda.core.indexOfOrThrow
import net.corda.core.node.ServicesForResolution
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
+import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.serialize
-import net.corda.core.serialization.threadLocalP2PKryo
import net.corda.core.utilities.Emoji
import java.security.PublicKey
@@ -45,7 +45,7 @@ class WireTransaction(
override val id: SecureHash by lazy { merkleTree.hash }
companion object {
- fun deserialize(data: SerializedBytes, kryo: Kryo = threadLocalP2PKryo()): WireTransaction {
+ fun deserialize(data: SerializedBytes, kryo: KryoPool = p2PKryo()): WireTransaction {
val wtx = data.bytes.deserialize(kryo)
wtx.cachedBytes = data
return wtx
diff --git a/core/src/main/kotlin/net/corda/core/utilities/Emoji.kt b/core/src/main/kotlin/net/corda/core/utilities/Emoji.kt
index 0da89e4c8d..db70b2d6b8 100644
--- a/core/src/main/kotlin/net/corda/core/utilities/Emoji.kt
+++ b/core/src/main/kotlin/net/corda/core/utilities/Emoji.kt
@@ -7,7 +7,9 @@ import net.corda.core.codePointsString
*/
object Emoji {
// Unfortunately only Apple has a terminal that can do colour emoji AND an emoji font installed by default.
- val hasEmojiTerminal by lazy { listOf("Apple_Terminal", "iTerm.app").contains(System.getenv("TERM_PROGRAM")) }
+ val hasEmojiTerminal by lazy {
+ System.getenv("CORDA_FORCE_EMOJI") != null || System.getenv("TERM_PROGRAM") in listOf("Apple_Terminal", "iTerm.app")
+ }
@JvmStatic val CODE_SANTA_CLAUS: String = codePointsString(0x1F385)
@JvmStatic val CODE_DIAMOND: String = codePointsString(0x1F537)
@@ -32,14 +34,21 @@ object Emoji {
val diamond: String get() = if (emojiMode.get() != null) "$CODE_DIAMOND " else ""
val bagOfCash: String get() = if (emojiMode.get() != null) "$CODE_BAG_OF_CASH " else ""
val newspaper: String get() = if (emojiMode.get() != null) "$CODE_NEWSPAPER " else ""
- val rightArrow: String get() = if (emojiMode.get() != null) "$CODE_RIGHT_ARROW " else ""
val leftArrow: String get() = if (emojiMode.get() != null) "$CODE_LEFT_ARROW " else ""
val paperclip: String get() = if (emojiMode.get() != null) "$CODE_PAPERCLIP " else ""
val coolGuy: String get() = if (emojiMode.get() != null) "$CODE_COOL_GUY " else ""
val books: String get() = if (emojiMode.get() != null) "$CODE_BOOKS " else ""
+ // These have old/non-emoji symbols with better platform support.
+ val greenTick: String get() = if (emojiMode.get() != null) "$CODE_GREEN_TICK " else "✓"
+ val rightArrow: String get() = if (emojiMode.get() != null) "$CODE_RIGHT_ARROW " else "▶︎"
+ val skullAndCrossbones: String get() = if (emojiMode.get() != null) "$CODE_SKULL_AND_CROSSBONES " else "☂"
+ val noEntry: String get() = if (emojiMode.get() != null) "$CODE_NO_ENTRY " else "✘"
+
inline fun renderIfSupported(body: () -> T): T {
- emojiMode.set(this) // Could be any object.
+ if (hasEmojiTerminal)
+ emojiMode.set(this) // Could be any object.
+
try {
return body()
} finally {
diff --git a/core/src/main/kotlin/net/corda/core/utilities/Logging.kt b/core/src/main/kotlin/net/corda/core/utilities/Logging.kt
index b126728e6b..c34039a4c6 100644
--- a/core/src/main/kotlin/net/corda/core/utilities/Logging.kt
+++ b/core/src/main/kotlin/net/corda/core/utilities/Logging.kt
@@ -3,7 +3,6 @@ package net.corda.core.utilities
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.core.LoggerContext
-import org.apache.logging.log4j.core.appender.ConsoleAppender
import org.apache.logging.log4j.core.config.LoggerConfig
import org.slf4j.LoggerFactory
import kotlin.reflect.KClass
@@ -56,10 +55,7 @@ object LogHelper {
val loggerContext = LogManager.getContext(false) as LoggerContext
val config = loggerContext.configuration
val loggerConfig = LoggerConfig(name, level, false)
- val appender = config.appenders.map { it.value as? ConsoleAppender }.singleOrNull()
- appender?.let {
- loggerConfig.addAppender(appender, null, null)
- }
+ loggerConfig.addAppender(config.appenders["Console-Appender"], null, null)
config.removeLogger(name)
config.addLogger(name, loggerConfig)
loggerContext.updateLoggers(config)
diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt
index 924042f586..0f99b23ae4 100644
--- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt
+++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt
@@ -57,6 +57,7 @@ class ProgressTracker(vararg steps: Step) {
open fun childProgressTracker(): ProgressTracker? = null
}
+ // TODO: There's no actual way to create these steps anymore!
/** This class makes it easier to relabel a step on the fly, to provide transient information. */
open inner class RelabelableStep(currentLabel: String) : Step(currentLabel) {
override val changes: BehaviorSubject = BehaviorSubject.create()
@@ -88,7 +89,7 @@ class ProgressTracker(vararg steps: Step) {
@CordaSerializable
private data class Child(val tracker: ProgressTracker, @Transient val subscription: Subscription?)
- private val childProgressTrackers = HashMap()
+ private val childProgressTrackers = mutableMapOf()
init {
steps.forEach {
diff --git a/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt b/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt
index 7db3d496df..48ecc3da4e 100644
--- a/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt
+++ b/core/src/main/kotlin/net/corda/core/utilities/UntrustworthyData.kt
@@ -1,5 +1,6 @@
package net.corda.core.utilities
+import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowException
/**
@@ -18,6 +19,7 @@ class UntrustworthyData(private val fromUntrustedWorld: T) {
@Deprecated("Accessing the untrustworthy data directly without validating it first is a bad idea")
get() = fromUntrustedWorld
+ @Suspendable
@Throws(FlowException::class)
fun unwrap(validator: Validator) = validator.validate(fromUntrustedWorld)
@@ -26,6 +28,7 @@ class UntrustworthyData(private val fromUntrustedWorld: T) {
inline fun validate(validator: (T) -> R) = validator(data)
interface Validator {
+ @Suspendable
@Throws(FlowException::class)
fun validate(data: T): R
}
diff --git a/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java
new file mode 100644
index 0000000000..eebabbd7f5
--- /dev/null
+++ b/core/src/test/java/net/corda/core/flows/FlowsInJavaTest.java
@@ -0,0 +1,74 @@
+package net.corda.core.flows;
+
+import co.paralleluniverse.fibers.Suspendable;
+import net.corda.core.crypto.Party;
+import net.corda.testing.node.MockNetwork;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.Future;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+public class FlowsInJavaTest {
+
+ private final MockNetwork net = new MockNetwork();
+ private MockNetwork.MockNode node1;
+ private MockNetwork.MockNode node2;
+
+ @Before
+ public void setUp() {
+ MockNetwork.BasketOfNodes someNodes = net.createSomeNodes(2);
+ node1 = someNodes.getPartyNodes().get(0);
+ node2 = someNodes.getPartyNodes().get(1);
+ net.runNetwork();
+ }
+
+ @After
+ public void cleanUp() {
+ net.stopNodes();
+ }
+
+ @Test
+ public void suspendableActionInsideUnwrap() throws Exception {
+ node2.getServices().registerFlowInitiator(SendInUnwrapFlow.class, (otherParty) -> new OtherFlow(otherParty, "Hello"));
+ Future result = node1.getServices().startFlow(new SendInUnwrapFlow(node2.getInfo().getLegalIdentity())).getResultFuture();
+ net.runNetwork();
+ assertThat(result.get()).isEqualTo("Hello");
+ }
+
+ private static class SendInUnwrapFlow extends FlowLogic {
+ private final Party otherParty;
+
+ private SendInUnwrapFlow(Party otherParty) {
+ this.otherParty = otherParty;
+ }
+
+ @Suspendable
+ @Override
+ public String call() throws FlowException {
+ return receive(String.class, otherParty).unwrap(data -> {
+ send(otherParty, "Something");
+ return data;
+ });
+ }
+ }
+
+ private static class OtherFlow extends FlowLogic {
+ private final Party otherParty;
+ private final String payload;
+
+ private OtherFlow(Party otherParty, String payload) {
+ this.otherParty = otherParty;
+ this.payload = payload;
+ }
+
+ @Suspendable
+ @Override
+ public String call() throws FlowException {
+ return sendAndReceive(String.class, otherParty, payload).unwrap(data -> data);
+ }
+ }
+
+}
diff --git a/core/src/test/kotlin/net/corda/core/contracts/AmountTests.kt b/core/src/test/kotlin/net/corda/core/contracts/AmountTests.kt
index 00a7ca8d2f..59c15ff46c 100644
--- a/core/src/test/kotlin/net/corda/core/contracts/AmountTests.kt
+++ b/core/src/test/kotlin/net/corda/core/contracts/AmountTests.kt
@@ -23,4 +23,20 @@ class AmountTests {
assertEquals(expected, amount.toDecimal())
assertEquals(amount, Amount.fromDecimal(amount.toDecimal(), amount.token))
}
+
+ @Test
+ fun parsing() {
+ assertEquals(Amount(1234L, GBP), Amount.parseCurrency("£12.34"))
+ assertEquals(Amount(1200L, GBP), Amount.parseCurrency("£12"))
+ assertEquals(Amount(1000L, USD), Amount.parseCurrency("$10"))
+ assertEquals(Amount(5000L, JPY), Amount.parseCurrency("¥5000"))
+ assertEquals(Amount(500000L, RUB), Amount.parseCurrency("₽5000"))
+ assertEquals(Amount(1500000000L, CHF), Amount.parseCurrency("15,000,000 CHF"))
+ }
+
+ @Test
+ fun rendering() {
+ assertEquals("5000 JPY", Amount.parseCurrency("¥5000").toString())
+ assertEquals("50.12 USD", Amount.parseCurrency("$50.12").toString())
+ }
}
\ No newline at end of file
diff --git a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt
index 565992c896..e70285e5db 100644
--- a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt
+++ b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt
@@ -45,22 +45,22 @@ class CryptoUtilsTest {
val sphincsKeyPair = Crypto.generateKeyPair("SPHINCS-256_SHA512")
// not null private keys
- assertNotNull(rsaKeyPair.private);
- assertNotNull(ecdsaKKeyPair.private);
- assertNotNull(ecdsaRKeyPair.private);
- assertNotNull(eddsaKeyPair.private);
- assertNotNull(sphincsKeyPair.private);
+ assertNotNull(rsaKeyPair.private)
+ assertNotNull(ecdsaKKeyPair.private)
+ assertNotNull(ecdsaRKeyPair.private)
+ assertNotNull(eddsaKeyPair.private)
+ assertNotNull(sphincsKeyPair.private)
// not null public keys
- assertNotNull(rsaKeyPair.public);
- assertNotNull(ecdsaKKeyPair.public);
- assertNotNull(ecdsaRKeyPair.public);
- assertNotNull(eddsaKeyPair.public);
- assertNotNull(sphincsKeyPair.public);
+ assertNotNull(rsaKeyPair.public)
+ assertNotNull(ecdsaKKeyPair.public)
+ assertNotNull(ecdsaRKeyPair.public)
+ assertNotNull(eddsaKeyPair.public)
+ assertNotNull(sphincsKeyPair.public)
// fail on unsupported algorithm
try {
- val wrongKeyPair = Crypto.generateKeyPair("WRONG_ALG")
+ Crypto.generateKeyPair("WRONG_ALG")
fail()
} catch (e: Exception) {
// expected
@@ -117,15 +117,15 @@ class CryptoUtilsTest {
// test on malformed signatures (even if they change for 1 bit)
for (i in 0..signedData.size - 1) {
- val b = signedData.get(i)
- signedData.set(i,b.inc())
+ val b = signedData[i]
+ signedData[i] = b.inc()
try {
keyPair.verify(signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
}
- signedData.set(i,b.dec())
+ signedData[i] = b.dec()
}
}
@@ -176,7 +176,7 @@ class CryptoUtilsTest {
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
- signedData.set(0,signedData[0].inc())
+ signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
fail()
@@ -232,7 +232,7 @@ class CryptoUtilsTest {
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
- signedData.set(0, signedData[0].inc())
+ signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
fail()
@@ -288,7 +288,7 @@ class CryptoUtilsTest {
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
- signedData.set(0, signedData[0].inc())
+ signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
fail()
@@ -344,7 +344,7 @@ class CryptoUtilsTest {
assertTrue(verificationBig)
// test on malformed signatures (even if they change for 1 bit)
- signedData.set(0, signedData[0].inc())
+ signedData[0] = signedData[0].inc()
try {
keyPair.verify(signedData, testBytes)
fail()
@@ -357,7 +357,7 @@ class CryptoUtilsTest {
@Test
fun `Check supported algorithms`() {
val algList : List = Crypto.listSupportedSignatureSchemes()
- val expectedAlgSet = setOf("RSA_SHA256","ECDSA_SECP256K1_SHA256", "ECDSA_SECP256R1_SHA256", "EDDSA_ED25519_SHA512","SPHINCS-256_SHA512")
+ val expectedAlgSet = setOf("RSA_SHA256","ECDSA_SECP256K1_SHA256", "ECDSA_SECP256R1_SHA256", "EDDSA_ED25519_SHA512","SPHINCS-256_SHA512")
assertTrue { Sets.symmetricDifference(expectedAlgSet,algList.toSet()).isEmpty(); }
}
@@ -601,12 +601,12 @@ class CryptoUtilsTest {
@Test
fun `Failure test between K1 and R1 keys`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
- val (privK1, pubK1) = keyPairK1
+ val privK1= keyPairK1.private
val encodedPrivK1 = privK1.encoded
val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1)
val keyPairR1 = Crypto.generateKeyPair("ECDSA_SECP256R1_SHA256")
- val (privR1, pubR1) = keyPairR1
+ val privR1 = keyPairR1.private
val encodedPrivR1 = privR1.encoded
val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1)
@@ -616,7 +616,7 @@ class CryptoUtilsTest {
@Test
fun `Decoding Failure on randomdata as key`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
- val (privK1, pubK1) = keyPairK1
+ val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded
// Test on random encoded bytes.
@@ -626,7 +626,7 @@ class CryptoUtilsTest {
// fail on fake key.
try {
- val decodedFake = Crypto.decodePrivateKey(fakeEncodedKey)
+ Crypto.decodePrivateKey(fakeEncodedKey)
fail()
} catch (e: Exception) {
// expected
@@ -636,21 +636,20 @@ class CryptoUtilsTest {
@Test
fun `Decoding Failure on malformed keys`() {
val keyPairK1 = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
- val (privK1, pubK1) = keyPairK1
+ val privK1 = keyPairK1.private
val encodedPrivK1 = privK1.encoded
// fail on malformed key.
for (i in 0..encodedPrivK1.size - 1) {
- val b = encodedPrivK1.get(i)
- encodedPrivK1.set(i,b.inc())
+ val b = encodedPrivK1[i]
+ encodedPrivK1[i] = b.inc()
try {
- val decodedFake = Crypto.decodePrivateKey(encodedPrivK1)
- println("OK")
+ Crypto.decodePrivateKey(encodedPrivK1)
fail()
} catch (e: Exception) {
// expected
}
- encodedPrivK1.set(i,b.dec())
+ encodedPrivK1[i] = b.dec()
}
}
}
diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt
index fa68ffb1d6..f6dc90b2dc 100644
--- a/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt
+++ b/core/src/test/kotlin/net/corda/core/crypto/PartialMerkleTreeTest.kt
@@ -1,18 +1,20 @@
package net.corda.core.crypto
-import com.esotericsoftware.kryo.serializers.MapSerializer
+import com.esotericsoftware.kryo.KryoException
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash.Companion.zeroHash
-import net.corda.core.serialization.*
-import net.corda.core.transactions.*
-import net.corda.core.utilities.*
+import net.corda.core.serialization.p2PKryo
+import net.corda.core.serialization.serialize
+import net.corda.core.transactions.WireTransaction
+import net.corda.core.utilities.DUMMY_NOTARY
+import net.corda.core.utilities.DUMMY_PUBKEY_1
+import net.corda.core.utilities.TEST_TX_TIME
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.ledger
import org.junit.Test
-import java.util.*
import kotlin.test.*
class PartialMerkleTreeTest {
@@ -208,15 +210,12 @@ class PartialMerkleTreeTest {
assertFalse(pmt.verify(wrongRoot, inclHashes))
}
- @Test
- fun `hash map serialization`() {
+ @Test(expected = KryoException::class)
+ fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
- assert(serializedHash(hm1) == serializedHash(hm1.serialize().deserialize())) // It internally uses the ordered HashMap extension.
- val kryo = extendKryoHash(createKryo())
- assertTrue(kryo.getSerializer(HashMap::class.java) is OrderedSerializer)
- assertTrue(kryo.getSerializer(LinkedHashMap::class.java) is MapSerializer)
- val hm2 = hm1.serialize(kryo).deserialize(kryo)
- assert(hm1.hashCode() == hm2.hashCode())
+ p2PKryo().run { kryo ->
+ hm1.serialize(kryo)
+ }
}
private fun makeSimpleCashWtx(notary: Party, timestamp: Timestamp? = null, attachments: List = emptyList()): WireTransaction {
diff --git a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt
index d598b8e0cb..962459612f 100644
--- a/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt
+++ b/core/src/test/kotlin/net/corda/core/flows/ContractUpgradeFlowTest.kt
@@ -165,11 +165,11 @@ class ContractUpgradeFlowTest {
// Starts contract upgrade flow.
a.services.startFlow(ContractUpgradeFlow.Instigator(stateAndRef, CashV2::class.java))
mockNet.runNetwork()
- // Get contract state form the vault.
- val state = databaseTransaction(a.database) { a.vault.unconsumedStates() }
- assertTrue(state.single().state.data is CashV2.State, "Contract state is upgraded to the new version.")
- assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (state.first().state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
- assertEquals(listOf(a.info.legalIdentity.owningKey), (state.first().state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
+ // Get contract state from the vault.
+ val firstState = databaseTransaction(a.database) { a.vault.unconsumedStates().single() }
+ assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.")
+ assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
+ assertEquals(listOf(a.info.legalIdentity.owningKey), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
}
class CashV2 : UpgradedContract {
diff --git a/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt b/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt
index f37a1680c1..c2d3cd4545 100644
--- a/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt
+++ b/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt
@@ -1,5 +1,6 @@
package net.corda.core.node
+import com.esotericsoftware.kryo.Kryo
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
@@ -11,7 +12,9 @@ import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.MEGA_CORP
import net.corda.testing.node.MockAttachmentStorage
import org.apache.commons.io.IOUtils
+import org.junit.After
import org.junit.Assert
+import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -75,6 +78,16 @@ class AttachmentClassLoaderTests {
class ClassLoaderForTests : URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH), FilteringClassLoader)
+ lateinit var kryo: Kryo
+ lateinit var kryo2: Kryo
+
+ @Before
+ fun setup() {
+ // Do not release these back to the pool, since we do some unorthodox modifications to them below.
+ kryo = p2PKryo().borrow()
+ kryo2 = p2PKryo().borrow()
+ }
+
@Test
fun `dynamically load AnotherDummyContract from isolated contracts jar`() {
val child = ClassLoaderForTests()
@@ -205,7 +218,6 @@ class AttachmentClassLoaderTests {
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
- val kryo = createKryo()
kryo.classLoader = cl
kryo.addToWhitelist(contract.javaClass)
@@ -224,7 +236,6 @@ class AttachmentClassLoaderTests {
assertNotNull(data.contract)
- val kryo2 = createKryo()
kryo2.addToWhitelist(data.contract.javaClass)
val bytes = data.serialize(kryo2)
@@ -236,15 +247,25 @@ class AttachmentClassLoaderTests {
val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
- val kryo = createKryo()
kryo.classLoader = cl
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl))
val state2 = bytes.deserialize(kryo)
assertEquals(cl, state2.contract.javaClass.classLoader)
assertNotNull(state2)
+
+ // We should be able to load same class from a different class loader and have them be distinct.
+ val cl2 = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader)
+
+ kryo.classLoader = cl2
+ kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl2))
+
+ val state3 = bytes.deserialize(kryo)
+ assertEquals(cl2, state3.contract.javaClass.classLoader)
+ assertNotNull(state3)
}
+
@Test
fun `test serialization of WireTransaction with statically loaded contract`() {
val tx = ATTACHMENT_TEST_PROGRAM_ID.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
@@ -263,31 +284,30 @@ class AttachmentClassLoaderTests {
val contract = contractClass.newInstance() as DummyContractBackdoor
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage()
- val kryo = createKryo()
kryo.addToWhitelist(contract.javaClass)
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child))
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child))
// todo - think about better way to push attachmentStorage down to serializer
- kryo.attachmentStorage = storage
+ val bytes = kryo.withAttachmentStorage(storage) {
- val attachmentRef = importJar(storage)
+ val attachmentRef = importJar(storage)
- tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
+ tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
- val wireTransaction = tx.toWireTransaction()
+ val wireTransaction = tx.toWireTransaction()
- val bytes = wireTransaction.serialize(kryo)
-
- val kryo2 = createKryo()
+ wireTransaction.serialize(kryo)
+ }
// use empty attachmentStorage
- kryo2.attachmentStorage = storage
+ kryo2.withAttachmentStorage(storage) {
- val copiedWireTransaction = bytes.deserialize(kryo2)
+ val copiedWireTransaction = bytes.deserialize(kryo2)
- assertEquals(1, copiedWireTransaction.outputs.size)
- val contract2 = copiedWireTransaction.outputs[0].data.contract as DummyContractBackdoor
- assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
+ assertEquals(1, copiedWireTransaction.outputs.size)
+ val contract2 = copiedWireTransaction.outputs[0].data.contract as DummyContractBackdoor
+ assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0].data))
+ }
}
@Test
@@ -297,26 +317,24 @@ class AttachmentClassLoaderTests {
val contract = contractClass.newInstance() as DummyContractBackdoor
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage()
- val kryo = createKryo()
// todo - think about better way to push attachmentStorage down to serializer
- kryo.attachmentStorage = storage
-
val attachmentRef = importJar(storage)
+ val bytes = kryo.withAttachmentStorage(storage) {
- tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
+ tx.addAttachment(storage.openAttachment(attachmentRef)!!.id)
- val wireTransaction = tx.toWireTransaction()
+ val wireTransaction = tx.toWireTransaction()
- val bytes = wireTransaction.serialize(kryo)
-
- val kryo2 = createKryo()
- // use empty attachmentStorage
- kryo2.attachmentStorage = MockAttachmentStorage()
-
- val e = assertFailsWith(MissingAttachmentsException::class) {
- bytes.deserialize(kryo2)
+ wireTransaction.serialize(kryo)
+ }
+ // use empty attachmentStorage
+ kryo2.withAttachmentStorage(MockAttachmentStorage()) {
+
+ val e = assertFailsWith(MissingAttachmentsException::class) {
+ bytes.deserialize(kryo2)
+ }
+ assertEquals(attachmentRef, e.ids.single())
}
- assertEquals(attachmentRef, e.ids.single())
}
}
diff --git a/core/src/test/kotlin/net/corda/core/serialization/KryoTests.kt b/core/src/test/kotlin/net/corda/core/serialization/KryoTests.kt
index ac4e8ebf6a..d27c0cbb95 100644
--- a/core/src/test/kotlin/net/corda/core/serialization/KryoTests.kt
+++ b/core/src/test/kotlin/net/corda/core/serialization/KryoTests.kt
@@ -1,5 +1,6 @@
package net.corda.core.serialization
+import com.esotericsoftware.kryo.Kryo
import com.google.common.primitives.Ints
import net.corda.core.crypto.*
import net.corda.core.messaging.Ack
@@ -7,16 +8,27 @@ import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
+import org.slf4j.LoggerFactory
import java.io.InputStream
import java.security.Security
import java.time.Instant
import java.util.*
import kotlin.test.assertEquals
+import kotlin.test.assertTrue
class KryoTests {
- private val kryo = createKryo()
+ private lateinit var kryo: Kryo
+
+ @Before
+ fun setup() {
+ // We deliberately do not return this, since we do some unorthodox registering below and do not want to pollute the pool.
+ kryo = p2PKryo().borrow()
+ }
@Test
fun ok() {
@@ -112,6 +124,14 @@ class KryoTests {
assertEquals(meta2, meta)
}
+ @Test
+ fun `serialize - deserialize Logger`() {
+ val logger = LoggerFactory.getLogger("aName")
+ val logger2 = logger.serialize(storageKryo()).deserialize(storageKryo())
+ assertEquals(logger.name, logger2.name)
+ assertTrue(logger === logger2)
+ }
+
@CordaSerializable
private data class Person(val name: String, val birthday: Instant?)
diff --git a/core/src/test/kotlin/net/corda/core/serialization/SerializationTokenTest.kt b/core/src/test/kotlin/net/corda/core/serialization/SerializationTokenTest.kt
index 23ab02a725..7b16b654a1 100644
--- a/core/src/test/kotlin/net/corda/core/serialization/SerializationTokenTest.kt
+++ b/core/src/test/kotlin/net/corda/core/serialization/SerializationTokenTest.kt
@@ -15,12 +15,13 @@ class SerializationTokenTest {
@Before
fun setup() {
- kryo = threadLocalStorageKryo()
+ kryo = storageKryo().borrow()
}
@After
fun cleanup() {
SerializeAsTokenSerializer.clearContext(kryo)
+ storageKryo().release(kryo)
}
// Large tokenizable object so we can tell from the smaller number of serialized bytes it was actually tokenized
@@ -38,7 +39,7 @@ class SerializationTokenTest {
@Test
fun `write token and read tokenizable`() {
val tokenizableBefore = LargeTokenizable()
- val context = SerializeAsTokenContext(tokenizableBefore, kryo)
+ val context = SerializeAsTokenContext(tokenizableBefore, storageKryo())
SerializeAsTokenSerializer.setContext(kryo, context)
val serializedBytes = tokenizableBefore.serialize(kryo)
assertThat(serializedBytes.size).isLessThan(tokenizableBefore.numBytes)
@@ -51,7 +52,7 @@ class SerializationTokenTest {
@Test
fun `write and read singleton`() {
val tokenizableBefore = UnitSerializeAsToken()
- val context = SerializeAsTokenContext(tokenizableBefore, kryo)
+ val context = SerializeAsTokenContext(tokenizableBefore, storageKryo())
SerializeAsTokenSerializer.setContext(kryo, context)
val serializedBytes = tokenizableBefore.serialize(kryo)
val tokenizableAfter = serializedBytes.deserialize(kryo)
@@ -61,7 +62,7 @@ class SerializationTokenTest {
@Test(expected = UnsupportedOperationException::class)
fun `new token encountered after context init`() {
val tokenizableBefore = UnitSerializeAsToken()
- val context = SerializeAsTokenContext(emptyList(), kryo)
+ val context = SerializeAsTokenContext(emptyList(), storageKryo())
SerializeAsTokenSerializer.setContext(kryo, context)
tokenizableBefore.serialize(kryo)
}
@@ -69,9 +70,9 @@ class SerializationTokenTest {
@Test(expected = UnsupportedOperationException::class)
fun `deserialize unregistered token`() {
val tokenizableBefore = UnitSerializeAsToken()
- val context = SerializeAsTokenContext(emptyList(), kryo)
+ val context = SerializeAsTokenContext(emptyList(), storageKryo())
SerializeAsTokenSerializer.setContext(kryo, context)
- val serializedBytes = tokenizableBefore.toToken(SerializeAsTokenContext(emptyList(), kryo)).serialize(kryo)
+ val serializedBytes = tokenizableBefore.toToken(SerializeAsTokenContext(emptyList(), storageKryo())).serialize(kryo)
serializedBytes.deserialize(kryo)
}
@@ -84,7 +85,7 @@ class SerializationTokenTest {
@Test(expected = KryoException::class)
fun `deserialize non-token`() {
val tokenizableBefore = UnitSerializeAsToken()
- val context = SerializeAsTokenContext(tokenizableBefore, kryo)
+ val context = SerializeAsTokenContext(tokenizableBefore, storageKryo())
SerializeAsTokenSerializer.setContext(kryo, context)
val stream = ByteArrayOutputStream()
Output(stream).use {
@@ -106,7 +107,7 @@ class SerializationTokenTest {
@Test(expected = KryoException::class)
fun `token returns unexpected type`() {
val tokenizableBefore = WrongTypeSerializeAsToken()
- val context = SerializeAsTokenContext(tokenizableBefore, kryo)
+ val context = SerializeAsTokenContext(tokenizableBefore, storageKryo())
SerializeAsTokenSerializer.setContext(kryo, context)
val serializedBytes = tokenizableBefore.serialize(kryo)
serializedBytes.deserialize(kryo)
diff --git a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt
index 01fc410c0d..8b56b4ad13 100644
--- a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt
+++ b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt
@@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
-import net.corda.core.serialization.createInternalKryo
+import net.corda.core.serialization.createTestKryo
import net.corda.core.serialization.serialize
import org.junit.Before
import org.junit.Test
@@ -106,7 +106,7 @@ class ProgressTrackerTest {
}
}
- val kryo = createInternalKryo().apply {
+ val kryo = createTestKryo().apply {
// This is required to make sure Kryo walks through the auto-generated members for the lambda below.
fieldSerializerConfig.isIgnoreSyntheticFields = false
}
diff --git a/docs/build/html/_static/corda-introductory-whitepaper-jp.pdf b/docs/build/html/_static/corda-introductory-whitepaper-jp.pdf
new file mode 100644
index 0000000000..495ced5e2e
Binary files /dev/null and b/docs/build/html/_static/corda-introductory-whitepaper-jp.pdf differ
diff --git a/docs/source/_static/corda-introductory-whitepaper-jp.pdf b/docs/source/_static/corda-introductory-whitepaper-jp.pdf
new file mode 100644
index 0000000000..495ced5e2e
Binary files /dev/null and b/docs/source/_static/corda-introductory-whitepaper-jp.pdf differ
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index 8a7c0435aa..d6e5ce66a3 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -3,6 +3,26 @@ Changelog
Here are brief summaries of what's changed between each snapshot release.
+UNRELEASED
+----------
+
+API changes:
+
+* The new Jackson module provides JSON/YAML serialisers for common Corda datatypes. If you have previously been
+ using the JSON support in the standalone web server, please be aware that amounts are now serialised as strings
+ instead of { quantity, token } pairs as before. The old format is still accepted, but new JSON will be produced
+ using strings like "1000.00 USD" when writing. You can use any format supported by ``Amount.parseCurrency``
+ as input.
+
+
+Milestone 10
+------------
+
+* Configuration:
+ * Replace ``artemisPort`` with ``p2pPort`` in Gradle configuration
+ * Replace ``artemisAddress`` with ``p2pAddress`` in node configuration
+ * Added ``rpcAddress`` in node configuration
+
Milestone 9.1
-------------
diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst
index be85fe4ec9..eace15faad 100644
--- a/docs/source/corda-configuration-file.rst
+++ b/docs/source/corda-configuration-file.rst
@@ -38,8 +38,9 @@ NetworkMapService plus Simple Notary configuration file.
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
- artemisAddress : "localhost:12345"
- webAddress : "localhost:12346"
+ p2pAddress : "localhost:12345"
+ rpcAddress : "localhost:12346"
+ webAddress : "localhost:12347"
extraAdvertisedServiceIds : []
useHTTPS : false
devMode : true
@@ -74,13 +75,15 @@ path to the node's base directory.
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
the only configuration that has been tested, although in the future full support for other storage layers will be validated.
-:artemisAddress: The host and port on which the node is available for protocol operations over ArtemisMQ.
+:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
+
+:p2pAddress: The host and port on which the node is available for protocol operations over ArtemisMQ.
.. note:: In practice the ArtemisMQ messaging services bind to all local addresses on the specified port. However,
note that the host is the included as the advertised entry in the NetworkMapService. As a result the value listed
here must be externally accessible when running nodes across a cluster of machines.
-:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
+:rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC.
:webAddress: The host and port on which the bundled webserver will listen if it is started.
diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst
index 5286aa6187..98d551d1d5 100644
--- a/docs/source/creating-a-cordapp.rst
+++ b/docs/source/creating-a-cordapp.rst
@@ -57,9 +57,12 @@ case the ``node_dir`` is the location where your node server's JAR and configura
Starting your node
------------------
-Now you have a node server with your app installed, you can run it by navigating to ```` and running
+Now you have a node server with your app installed, you can run it by navigating to ```` and running:
- java -jar corda.jar
+.. code-block:: shell
+
+ Windows: java -jar corda.jar
+ UNIX: ./corda.jar
The plugin should automatically be registered and the configuration file used.
@@ -67,7 +70,7 @@ The plugin should automatically be registered and the configuration file used.
The configuration file and workspace paths can be overidden on the command line e.g.
-``java -jar corda.jar --config-file=test.conf --base-directory=/opt/r3corda/nodes/test``.
+``./corda.jar --config-file=test.conf --base-directory=/opt/r3corda/nodes/test``.
Otherwise the workspace folder for the node is the current working path.
@@ -99,35 +102,22 @@ at the present time, and should certainly be treated as read-only.
Building against Corda
----------------------
-.. warning:: This feature is subject to rapid change
-
-Corda now supports publishing to Maven local to build against it. To publish to Maven local run the following in the
-root directory of Corda
+To publish to your local Maven repository (in ``~/.m2`` on Unix and ``%HOMEPATH%\.m2`` on Windows) run the following
+in the root directory of the Corda code:
.. code-block:: shell
./gradlew install
This will publish corda-$version.jar, finance-$version.jar, core-$version.jar and node-$version.jar to the
-group net.corda. You can now depend on these as you normally would a Maven dependency.
-
-Gradle plugins for CorDapps
----------------------------
+group net.corda. You can now depend on these as you normally would a Maven dependency, using the group id
+``net.corda``.
There are several Gradle plugins that reduce your build.gradle boilerplate and make development of CorDapps easier.
The available plugins are in the gradle-plugins directory of the Corda repository.
-Building Gradle plugins
-~~~~~~~~~~~~~~~~~~~~~~~
-
-To install to your local Maven repository the plugins that CorDapp gradle files require, run the following from the
-root of the Corda project:
-
-.. code-block:: text
-
- ./gradlew install
-
-The plugins will now be installed to your local Maven repository in ~/.m2 on Unix and %HOMEPATH%\.m2 on Windows.
+To install to your local Maven repository the plugins that CorDapp gradle files require, enter the ``gradle-plugins``
+directory and then run ``../gradle install``. The plugins will now be installed to your local Maven repository.
Using Gradle plugins
~~~~~~~~~~~~~~~~~~~~
@@ -135,11 +125,6 @@ Using Gradle plugins
To use the plugins, if you are not already using the CorDapp template project, you must modify your build.gradle. Add
the following segments to the relevant part of your build.gradle.
-Template build.gradle
-~~~~~~~~~~~~~~~~~~~~~
-
-To build against Corda and the plugins that CorDapps use, update your build.gradle to contain the following:
-
.. code-block:: groovy
buildscript {
@@ -214,24 +199,27 @@ is a three node example;
name "Controller"
nearestCity "London"
advertisedServices = [ "corda.notary.validating" ]
- artemisPort 12345
- webPort 12346
+ p2pPort 10002
+ rpcPort 10003
+ webPort 10004
cordapps []
}
node {
name "NodeA"
nearestCity "London"
advertisedServices = []
- artemisPort 31337
- webPort 31339
+ p2pPort 10005
+ rpcPort 10006
+ webPort 10007
cordapps []
}
node {
name "NodeB"
nearestCity "New York"
advertisedServices = []
- artemisPort 31338
- webPort 31340
+ p2pPort 10008
+ rpcPort 10009
+ webPort 10010
cordapps []
}
}
@@ -239,10 +227,9 @@ is a three node example;
You can create more configurations with new tasks that extend Cordform.
New nodes can be added by simply adding another node block and giving it a different name, directory and ports. When you
-run this task it will install the nodes to the directory specified and a script will be generated (for UNIX users only
-at present) to run the nodes with one command (``runnodes``). On MacOS X this script will run each node in a new
-terminal tab, and on Linux it will open up a new XTerm for each node. On Windows the (``runnodes.bat``) script will run
-one node per window.
+run this task it will install the nodes to the directory specified and a script will be generated to run the nodes with
+one command (``runnodes``). On MacOS X this script will run each node in a new terminal tab, and on Linux it will open
+up a new XTerm for each node. On Windows the (``runnodes.bat``) script will run one node per window.
Other CorDapps can also be specified if they are already specified as classpath or compile dependencies in your
``build.gradle``.
diff --git a/docs/source/example-code/build.gradle b/docs/source/example-code/build.gradle
index 89cd1976ff..be45ebc6d9 100644
--- a/docs/source/example-code/build.gradle
+++ b/docs/source/example-code/build.gradle
@@ -40,7 +40,7 @@ compileTestJava.dependsOn tasks.getByPath(':node:capsule:buildCordaJAR')
dependencies {
compile project(':core')
- compile project(':client')
+ compile project(':client:jfx')
testCompile project(':test-utils')
compile "org.graphstream:gs-core:1.3"
@@ -49,7 +49,7 @@ dependencies {
}
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
- runtime project(path: ":node:webserver", configuration: 'runtimeArtifacts')
+ runtime project(path: ":node:webserver:webcapsule", configuration: 'runtimeArtifacts')
}
mainClassName = "net.corda.docs.ClientRpcTutorialKt"
@@ -80,16 +80,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary"
nearestCity "London"
advertisedServices = ["corda.notary.validating"]
- artemisPort 10002
- webPort 10003
+ p2pPort 10002
+ rpcPort 10003
+ webPort 10004
cordapps = []
}
node {
name "Alice"
nearestCity "London"
advertisedServices = []
- artemisPort 10004
- webPort 10005
+ p2pPort 10005
+ rpcPort 10006
+ webPort 10007
cordapps = []
rpcUsers = [
['user' : "user",
diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
index a5dd4b4640..2be6b47c58 100644
--- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
+++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt
@@ -4,13 +4,11 @@ import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.DOLLARS
-import net.corda.core.contracts.issuedBy
import net.corda.core.getOrThrow
import net.corda.core.messaging.startFlow
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.Vault
import net.corda.core.serialization.OpaqueBytes
-import net.corda.core.toFuture
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.driver.driver
@@ -92,10 +90,7 @@ class IntegrationTestingTutorial {
// START 5
for (i in 1 .. 10) {
- bobProxy.startFlow(::CashPaymentFlow,
- i.DOLLARS.issuedBy(alice.nodeInfo.legalIdentity.ref(issueRef)),
- alice.nodeInfo.legalIdentity
- ).returnValue.getOrThrow()
+ bobProxy.startFlow(::CashPaymentFlow, i.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.getOrThrow()
}
aliceVaultUpdates.expectEvents {
diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
index 2e923502f1..2f26d671ee 100644
--- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
+++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
@@ -122,7 +122,7 @@ fun generateTransactions(proxy: CordaRPCOps) {
ownedQuantity -= quantity
} else if (ownedQuantity > 1000 && n < 0.7) {
val quantity = Math.abs(random.nextLong() % Math.min(ownedQuantity, 2000))
- proxy.startFlow(::CashPaymentFlow, Amount(quantity, Issued(meAndRef, USD)), me)
+ proxy.startFlow(::CashPaymentFlow, Amount(quantity, USD), me)
} else {
val quantity = Math.abs(random.nextLong() % 1000)
proxy.startFlow(::CashIssueFlow, Amount(quantity, USD), issueRef, me, notary)
diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt
index 4e9d6a1ea3..98f13339b3 100644
--- a/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt
+++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/FxTransactionBuildTutorial.kt
@@ -24,7 +24,7 @@ import java.util.*
object FxTransactionDemoTutorial {
// Would normally be called by custom service init in a CorDapp
fun registerFxProtocols(pluginHub: PluginServiceHub) {
- pluginHub.registerFlowInitiator(ForeignExchangeFlow::class, ::ForeignExchangeRemoteFlow)
+ pluginHub.registerFlowInitiator(ForeignExchangeFlow::class.java, ::ForeignExchangeRemoteFlow)
}
}
diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt
index 1bed23f327..cb2190a265 100644
--- a/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt
+++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/WorkflowTransactionBuildTutorial.kt
@@ -17,7 +17,7 @@ import java.time.Duration
object WorkflowTransactionBuildTutorial {
// Would normally be called by custom service init in a CorDapp
fun registerWorkflowProtocols(pluginHub: PluginServiceHub) {
- pluginHub.registerFlowInitiator(SubmitCompletionFlow::class, ::RecordCompletionFlow)
+ pluginHub.registerFlowInitiator(SubmitCompletionFlow::class.java, ::RecordCompletionFlow)
}
}
diff --git a/docs/source/example-code/src/main/resources/example-network-map-node.conf b/docs/source/example-code/src/main/resources/example-network-map-node.conf
index 467c61d32d..854d38e60f 100644
--- a/docs/source/example-code/src/main/resources/example-network-map-node.conf
+++ b/docs/source/example-code/src/main/resources/example-network-map-node.conf
@@ -2,7 +2,7 @@ myLegalName : "Notary Service"
nearestCity : "London"
keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass"
-artemisAddress : "my-network-map:10000"
+p2pAddress : "my-network-map:10000"
webAddress : "localhost:10001"
extraAdvertisedServiceIds : []
useHTTPS : false
diff --git a/docs/source/example-code/src/main/resources/example-node.conf b/docs/source/example-code/src/main/resources/example-node.conf
index eb878520cc..2cc35d9bda 100644
--- a/docs/source/example-code/src/main/resources/example-node.conf
+++ b/docs/source/example-code/src/main/resources/example-node.conf
@@ -8,8 +8,9 @@ dataSourceProperties : {
"dataSource.user" : sa
"dataSource.password" : ""
}
-artemisAddress : "my-corda-node:10002"
-webAddress : "localhost:10003"
+p2pAddress : "my-corda-node:10002"
+rpcAddress : "my-corda-node:10003"
+webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : {
address : "my-network-map:10000"
@@ -21,4 +22,4 @@ rpcUsers : [
]
devMode : true
// Certificate signing service will be hosted by R3 in the near future.
-//certificateSigningService : "https://testnet.certificate.corda.net"
\ No newline at end of file
+//certificateSigningService : "https://testnet.certificate.corda.net"
diff --git a/docs/source/flow-state-machines.rst b/docs/source/flow-state-machines.rst
index 5380dea569..908d4be29f 100644
--- a/docs/source/flow-state-machines.rst
+++ b/docs/source/flow-state-machines.rst
@@ -413,9 +413,6 @@ This code is longer but no more complicated. Here are some things to pay attenti
As you can see, the flow logic is straightforward and does not contain any callbacks or network glue code, despite
the fact that it takes minimal resources and can survive node restarts.
-.. warning:: In the current version of the platform, exceptions thrown during flow execution are not propagated
- back to the sender. A thorough error handling and exceptions framework will be in a future version of the platform.
-
Progress tracking
-----------------
@@ -530,10 +527,8 @@ Future features
The flow framework is a key part of the platform and will be extended in major ways in future. Here are some of
the features we have planned:
-* Identity based addressing
* Exception management, with a "flow hospital" tool to manually provide solutions to unavoidable
problems (e.g. the other side doesn't know the trade)
-* Being able to interact with internal apps and tools via RPC
* Being able to interact with people, either via some sort of external ticketing system, or email, or a custom UI.
For example to implement human transaction authorisations.
* A standard library of flows that can be easily sub-classed by local developers in order to integrate internal
diff --git a/docs/source/index.rst b/docs/source/index.rst
index e017ed7ecb..544e66e54e 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -5,12 +5,12 @@ Welcome to the Corda documentation!
current state of the code. `Read the docs for milestone release M9.0 `_.
`Corda `_ is an open-source distributed ledger platform. The latest *milestone* (i.e. stable)
-release is M9.0. The codebase is on `GitHub `_, and our community can be found on
+release is M9.2. The codebase is on `GitHub `_, and our community can be found on
`Slack `_ and in our `forum `_.
If you're new to Corda, you should start by learning about its motivating vision and architecture. A good introduction
is the `Introduction to Corda webinar `_ and the `Introductory white paper`_. As
-they become more familiar with Corda, readers with a technical background will also want to dive into the `Technical white paper`_,
+you become more familiar with Corda, readers with a technical background will also want to dive into the `Technical white paper`_,
which describes the platform's envisioned end-state.
.. note:: Corda training is now available in London, New York and Singapore! `Learn more. `_
diff --git a/docs/source/inthebox.rst b/docs/source/inthebox.rst
index 7b67a7fe7e..aaf9dc0fbf 100644
--- a/docs/source/inthebox.rst
+++ b/docs/source/inthebox.rst
@@ -1,27 +1,51 @@
What's included?
================
-The Corda prototype currently includes:
+This Corda early access preview includes:
+* A collection of samples, for instance a web app demo that uses it to implement IRS trading.
+* A template app you can use to get started, and tutorial app that teaches you the basics.
* A peer to peer network with message persistence and delivery retries.
* Key data structures for defining contracts and states.
* Smart contracts, which you can find in the :doc:`contract-catalogue`.
-* Algorithms that work with them, such as serialising, hashing, signing, and verification of the signatures.
* API documentation and tutorials (what you're reading).
-* A business process orchestration framework.
+* A business process workflow framework.
* Notary infrastructure for precise timestamping, and elimination of double spending without a blockchain.
-* A simple REST API, and a web app demo that uses it to present a frontend for IRS trading.
+* A simple RPC API.
+* A user interface for administration.
Some things it does not currently include but should gain later are:
-* Sandboxing, distribution or publication of smart contract code
-* A user interface for administration
+* Sandboxing, distribution and publication of smart contract code.
+* A well specified wire protocol.
+* An identity framework.
-The prototype's goal is rapid exploration of ideas. Therefore in places it takes shortcuts that a production system
-would not in order to boost productivity:
+The open source version of Corda is designed for developers exploring how to write apps. It is not intended to
+be production grade software. For example it uses an embedded SQL database and doesn't yet have connectivity
+support for mainstream SQL vendors (Oracle, Postgres, MySQL, SQL Server etc). It hasn't been security audited
+and the APIs change in every release.
-* It uses an object graph serialization framework instead of a well specified, vendor neutral protocol.
-* There's currently no permissioning framework.
-* Some privacy techniques aren't implemented yet.
-* It uses an embedded SQL database and doesn't yet have connectivity support for mainstream SQL vendors (Oracle,
- Postgres, MySQL, SQL Server etc).
+Source tree layout
+------------------
+
+The Corda repository comprises the following folders:
+
+* **buildSrc** contains necessary gradle plugins to build Corda.
+* **client** contains libraries for connecting to a node, working with it remotely and binding server-side data to JavaFX UI.
+* **config** contains logging configurations and the default node configuration file.
+* **core** containing the core Corda libraries such as crypto functions, types for Corda's building blocks: states,
+ contracts, transactions, attachments, etc. and some interfaces for nodes and protocols.
+* **docs** contains the Corda docsite in restructured text format as well as the built docs in html. The docs can be
+ accessed via ``/docs/index.html`` from the root of the repo.
+* **finance** defines a range of elementary contracts (and associated schemas) and protocols, such as abstract fungible
+ assets, cash, obligation and commercial paper.
+* **gradle** contains the gradle wrapper which you'll use to execute gradle commands.
+* **gradle-plugins** contains some additional plugins which we use to deploy Corda nodes.
+* **lib** contains some dependencies.
+* **node** contains the core code of the Corda node (eg: node driver, servlets, node services, messaging, persistence).
+* **node-api** contains data structures shared between the node and the client module, e.g. types sent via RPC.
+* **node-schemas** contains entity classes used to represent relational database tables.
+* **samples** contains all our Corda demos and code samples.
+* **test-utils** contains some utilities for unit testing contracts ( the contracts testing DSL) and protocols (the
+ mock network) implementation.
+* **tools** contains the explorer which is a GUI front-end for Corda.
diff --git a/docs/source/key-concepts-consensus-notaries.rst b/docs/source/key-concepts-consensus-notaries.rst
index 5d13e4e552..bbbe7f28d8 100644
--- a/docs/source/key-concepts-consensus-notaries.rst
+++ b/docs/source/key-concepts-consensus-notaries.rst
@@ -105,11 +105,13 @@ Validation
One of the design decisions for a notary is whether or not to **validate** a transaction before accepting it.
-If a transaction is not checked for validity, it opens the platform to "denial of state" attacks, where anyone can build an invalid transaction consuming someone else's states and submit it to the notary to get the states "blocked".
-However, if the transaction is validated, this requires the notary to be able to see the full contents of the transaction in question and its dependencies.
-This is an obvious privacy leak.
+If a transaction is not checked for validity, it opens the platform to "denial of state" attacks, where anyone can build
+an invalid transaction consuming someone else's states and submit it to the notary to get the states blocked. However,
+if the transaction is validated, this requires the notary to be able to see the full contents of the transaction in
+question and its dependencies. This is an obvious privacy leak.
-The platform is flexible and currently supports both validating and non-validating notary implementations -- a party can select which one to use based on its own privacy requirements.
+The platform is flexible and currently supports both validating and non-validating notary implementations -- a
+party can select which one to use based on its own privacy requirements.
.. note:: In the non-validating model, the "denial of state" attack is partially alleviated by requiring the calling
party to authenticate and storing its identity for the request. The conflict information returned by the notary
@@ -117,9 +119,6 @@ The platform is flexible and currently supports both validating and non-validati
conflicting transaction is valid, the current one is aborted; if not, a dispute can be raised and the input states
of the conflicting invalid transaction are "un-committed" (via a legal process).
-.. note:: At present, all notaries can see the entire contents of a submitted transaction. A future piece of work
- will enable the processing of :doc:`merkle-trees`, thus providing data hiding of sensitive information.
-
Timestamping
------------
diff --git a/docs/source/key-concepts-data-model.rst b/docs/source/key-concepts-data-model.rst
index 43d43c7cd6..b5d0fcfbed 100644
--- a/docs/source/key-concepts-data-model.rst
+++ b/docs/source/key-concepts-data-model.rst
@@ -132,7 +132,7 @@ downloading any missing transactions into local storage and validating them. The
.. note:: Non-validating notaries assume transaction validity and do not request transaction data or their dependencies
beyond the list of states consumed.
-The tutorial " :doc:`tutorial-contract` "provides a hand-ons walk-through using these concepts.
+The tutorial ":doc:`tutorial-contract`" provides a hand-ons walk-through using these concepts.
Transaction Representation
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/docs/source/key-concepts-security-model.rst b/docs/source/key-concepts-security-model.rst
index f4d8076906..66be831da0 100644
--- a/docs/source/key-concepts-security-model.rst
+++ b/docs/source/key-concepts-security-model.rst
@@ -19,8 +19,8 @@ The security model plays a role in the following areas:
beyond the list of states consumed (and thus, their level of trust is much lower and exposed to malicious use of transaction inputs).
From an algorithm perspective, Corda currently provides a distributed notary implementation that uses Raft.
-.. note:: Future notary algorithms may include BFT and hardware assisted non-BFT algorithms (where non-BFT algorithms
- are converted into a more trusted form using remote attestation and hardware protection).
+.. note:: A byzantine fault tolerant notary based on the BFT-SMaRT algorithm is included in the code, but is
+ still incubating and is not yet ready for use.
* Authentication, authorisation and entitlements:
Network permissioning, including node to node authentication, is performed using TLS and certificates.
@@ -28,7 +28,6 @@ The security model plays a role in the following areas:
.. warning:: API level authentication (RPC, Web) is currently simple username/password for demonstration purposes and will be revised.
Similarly, authorisation is currently based on permission groups applied to flow execution.
- This is subject to design review with views to selecting a proven, mature entitlements solution.
Privacy techniques
diff --git a/docs/source/messaging.rst b/docs/source/messaging.rst
index 6d36f1333c..9823df1f50 100644
--- a/docs/source/messaging.rst
+++ b/docs/source/messaging.rst
@@ -104,8 +104,8 @@ validated user is the X.500 subject DN of the client TLS certificate and we assu
the peer. This allows the flow framework to authentically determine the ``Party`` initiating a new flow. For RPC clients
the validated user is the username itself and the RPC framework uses this to determine what permissions the user has.
-.. note:: ``Party`` lookup is currently done by the legal name which isn't guaranteed to be unique. A future version will
- use the full X.500 name as it can provide additional structures for uniqueness.
+.. note:: ``Party`` lookup is currently done by the legal name. A future version will use the full X.500 name as
+ it can provide additional structures for uniqueness.
The broker also does host verification when connecting to another peer. It checks that the TLS certificate common name
matches with the advertised legal name from the network map service.
diff --git a/docs/source/node-explorer.rst b/docs/source/node-explorer.rst
index 293d90987c..7cf76f6e9a 100644
--- a/docs/source/node-explorer.rst
+++ b/docs/source/node-explorer.rst
@@ -63,15 +63,15 @@ The Demo Nodes can be started in one of two modes:
.. note:: 5 Corda nodes will be created on the following port on localhost by default.
- * Notary -> 20001
- * Alice -> 20003
- * Bob -> 20005
- * UK Bank Plc -> 20008 (*Issuer node*)
- * USA Bank Corp -> 20009 (*Issuer node*)
+ * Notary -> 20003 (Does not accept logins)
+ * Alice -> 20006
+ * Bob -> 20009
+ * UK Bank Plc -> 20012 (*Issuer node*)
+ * USA Bank Corp -> 20015 (*Issuer node*)
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.
-Please note you are not allowed to connect to the notary.
+Please note you are not allowed to login to the notary.
.. note:: Alternatively, you may start the demo nodes from within IntelliJ using either of the run configurations
``Explorer - demo nodes`` or ``Explorer - demo nodes (simulation)``
diff --git a/docs/source/node-services.rst b/docs/source/node-services.rst
index c08e17ddda..578fc6c023 100644
--- a/docs/source/node-services.rst
+++ b/docs/source/node-services.rst
@@ -346,11 +346,13 @@ external legacy systems by insertion of unpacked data into existing
tables. To enable these features the contract state must implement the
``QueryableState`` interface to define the mappings.
-Node Web Server
----------------
+Corda Web Server
+----------------
-A web server comes bundled with the node by default, but is not started
-automatically. This web server exposes both RPC backed API calls and
-static content serving. The web server is not automatically started,
-you must explicitly start it in the node driver or define a web port
-in your `Cordformation`_ configuration.
+A simple web server is provided that embeds the Jetty servlet container.
+The Corda web server is not meant to be used for real, production-quality
+web apps. Instead it shows one example way of using Corda RPC in web apps
+to provide a REST API on top of the Corda native RPC mechanism.
+
+.. note:: The Corda web server may be removed in future and replaced with
+ sample specific webapps using a standard framework like Spring Boot.
\ No newline at end of file
diff --git a/docs/source/permissioning.rst b/docs/source/permissioning.rst
index 6691a818c3..acbb30565d 100644
--- a/docs/source/permissioning.rst
+++ b/docs/source/permissioning.rst
@@ -2,16 +2,13 @@ Network permissioning
=====================
The keystore located in ``/certificates/sslkeystore.jks`` is required to connect to the Corda network securely.
-In development mode (when ``devMode = true``, see :doc:`corda-configuration-file` for more information) a pre-configured
+In development mode (when ``devMode = true``, see ":doc:`corda-configuration-file`" for more information) a pre-configured
keystore will be used if the keystore does not exist. This is to ensure developers can get the nodes working as quickly
as possible.
However this is not secure for the real network. This documentation will explain the procedure of obtaining a signed
certificate for TestNet.
-.. warning:: The TestNet has not been setup yet as of Milestone 8 release. You will not be able to connect to the
- certificate signing server.
-
Initial Registration
--------------------
@@ -55,4 +52,3 @@ You can also specify the location of ``node.conf`` with ``--config-file`` flag i
A ``certificates`` folder containing the keystore and trust store will be created in the base directory when the process is completed.
.. warning:: The keystore is protected by the keystore password from the node configuration file. The password should kept safe to protect the private key and certificate.
-.. note:: Password encryption in node configuration will be supported in subsequent release.
diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst
index 7cfe93059c..bb9a73318e 100644
--- a/docs/source/release-notes.rst
+++ b/docs/source/release-notes.rst
@@ -4,6 +4,15 @@ Release notes
Here are release notes for each snapshot release from M9 onwards. This includes guidance on how to upgrade code from
the previous milestone release.
+Milestone 10
+------------
+
+Important: There are configuration changes in M10 due to the split of the Artemis port into separate P2P and RPC
+ports. To upgrade, you *must*:
+
+ 1. In Gradle build configurations replace any references to ``artemisPort`` with ``p2pPort``.
+ 2. In node configurations replace ``artemisAddress`` with ``p2pAddress``.
+
Milestone 9
-----------
@@ -46,4 +55,4 @@ clients.
There have also been dozens of bug fixes, performance improvements and usability tweaks. Upgrading is definitely
worthwhile and will only take a few minutes for most apps.
-For a full list of changes please see :doc:`change-log`.
\ No newline at end of file
+For a full list of changes please see :doc:`change-log`.
diff --git a/docs/source/running-the-demos.rst b/docs/source/running-the-demos.rst
index eccc1fda3a..86fa5196b4 100644
--- a/docs/source/running-the-demos.rst
+++ b/docs/source/running-the-demos.rst
@@ -17,9 +17,9 @@ The demos can be run either from the command line, or from inside IntelliJ. Runn
recommended if you just want to see the demos run, whereas using IntelliJ can be helpful if you want to debug or
extend the demos. For more details about running via the command line or from within IntelliJ, see :doc:`CLI-vs-IDE`.
-.. note:: If you are running the demos from the command line in Unix, you may have to install xterm.
+If any of the demos don't work, please raise an issue on GitHub.
-.. note:: If any of the demos don't work, please raise an issue on GitHub.
+.. note:: If you are running the demos from the command line in Linux (but not macOS), you may have to install xterm.
.. _trader-demo:
@@ -235,7 +235,7 @@ To run from IntelliJ:
4. Run ``Bank Of Corda Demo: Run Web Cash Issue`` to request issuance of some cash on behalf of Big Corporation via HTTP
.. note:: To verify that the Bank of Corda node is alive and running, navigate to the following URL:
- http://localhost:10005/api/bank/date
+ http://localhost:10007/api/bank/date
.. note:: The Bank of Corda node explicitly advertises with a node service type as follows:
``advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer"))))``
@@ -259,8 +259,8 @@ Launch the Explorer application to visualize the issuance and transfer of cash f
Using the following login details:
-- For the Bank of Corda node: localhost / port 10004 / username bankUser / password test
-- For the Big Corporation node: localhost / port 10006 / username bigCorpUser / password test
+- For the Bank of Corda node: localhost / port 10006 / username bankUser / password test
+- For the Big Corporation node: localhost / port 10009 / username bigCorpUser / password test
See https://docs.corda.net/node-explorer.html for further details on usage.
diff --git a/docs/source/setting-up-a-corda-network.rst b/docs/source/setting-up-a-corda-network.rst
index f45bd6b7ca..d7b57744a2 100644
--- a/docs/source/setting-up-a-corda-network.rst
+++ b/docs/source/setting-up-a-corda-network.rst
@@ -1,6 +1,6 @@
.. _log4j2: http://logging.apache.org/log4j/2.x/
-What is a corda network?
+What is a Corda network?
========================
A Corda network consists of a number of machines running nodes, including a single node operating as the network map
@@ -42,11 +42,12 @@ An example configuration:
The most important fields regarding network configuration are:
-* ``artemisAddress``: This specifies a host and port. Note that the address bound will **NOT** be ``my-corda-node``,
- but rather ``::`` (all addresses on all interfaces). The hostname specified is the hostname *that must be externally
- resolvable by other nodes in the network*. In the above configuration this is the resolvable name of a machine in a vpn.
-* ``webAddress``: The address the webserver should bind. Note that the port should be distinct from that of ``artemisAddress``
- if they are on the same machine.
+* ``p2pAddress``: This specifies a host and port to which Artemis will bind for messaging with other nodes. Note that the
+ address bound will **NOT** be ``my-corda-node``, but rather ``::`` (all addresses on all network interfaces). The hostname specified
+ is the hostname *that must be externally resolvable by other nodes in the network*. In the above configuration this is the
+ resolvable name of a machine in a VPN.
+* ``rpcAddress``: The address to which Artemis will bind for RPC calls.
+* ``webAddress``: The address the webserver should bind. Note that the port must be distinct from that of ``p2pAddress`` and ``rpcAddress`` if they are on the same machine.
* ``networkMapService``: Details of the node running the network map service. If it's this node that's running the service
then this field must not be specified.
diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst
index 35d0bac753..abfcbec64a 100644
--- a/docs/source/tutorial-cordapp.rst
+++ b/docs/source/tutorial-cordapp.rst
@@ -7,15 +7,11 @@
The example CorDapp
===================
-.. note:: The example CorDapp was previously hosted in the `cordapp-template `_ repository. It has now been
- moved into a new `cordapp-tutorial `_ repository. Going forward, the cordapp-template repo will contain an
- actual CorDapp template, which should be used as the basis for your CorDapps going forward.
-
This guide covers how to get started with the example CorDapp. Please note there are several Corda repositories:
* `corda `_ which contains the core platform code and sample CorDapps.
-* `cordapp-tutorial `_ which contains an example CorDapp you can use to bootstrap your own CorDapps. It is the subject of this tutorial and should help you understand the basics of building a CorDapp.
-* `cordapp-template `_ which contains a bare-bones template on which to build your own CorDapps.
+* `cordapp-tutorial `_ which contains an example CorDapp you can use to bootstrap your own CorDapps. It is the subject of this tutorial and should help you understand the basics.
+* `cordapp-template `_ which contains a bare-bones template designed for starting new projects by copying.
We recommend you read the non-technical white paper and technical white paper before you get started with Corda:
@@ -29,7 +25,7 @@ We recommend you read the non-technical white paper and technical white paper be
Background
----------
-The Example CorDapp implements a basic scenario where one party wishes to send an IOU to another party. The scenario
+The example CorDapp implements a basic scenario where one party wishes to send an IOU to another party. The scenario
defines four nodes:
* **Controller**, which hosts the network map service and validating notary service
@@ -105,27 +101,6 @@ Using a SNAPSHOT release
If you wish to build a CorDapp against the most current version of Corda, follow these instructions.
-The Corda repository comprises the following folders:
-
-* **buildSrc** contains necessary gradle plugins to build Corda.
-* **client** contains the RPC client framework.
-* **config** contains logging configurations and the default node configuration file.
-* **core** containing the core Corda libraries such as crypto functions, types for Corda's building blocks: states,
- contracts, transactions, attachments, etc. and some interfaces for nodes and protocols.
-* **docs** contains the Corda docsite in restructured text format as well as the built docs in html. The docs can be
- accessed via ``/docs/index.html`` from the root of the repo.
-* **finance** defines a range of elementary contracts (and associated schemas) and protocols, such as abstract fungible
- assets, cash, obligation and commercial paper.
-* **gradle** contains the gradle wrapper which you'll use to execute gradle commands.
-* **gradle-plugins** contains some additional plugins which we use to deploy Corda nodes.
-* **lib** contains some dependencies.
-* **node** contains anything specifically required for creating, running and managing nodes (eg: node driver, servlets,
- node services, messaging, persistence).
-* **samples** contains all our Corda demos and code samples.
-* **test-utils** contains some utilities for unit testing contracts ( the contracts testing DSL) and protocols (the
- mock network) implementation.
-* **tools** contains the explorer which is a GUI front-end for Corda.
-
Firstly navigate to the folder on your machine you wish to clone the Corda repository to. Then use the following command
to clone the Corda repository:
@@ -268,8 +243,8 @@ Unix/Mac OSX: ``./gradlew deployNodes``
Windows: ``gradlew.bat deployNodes``
This build process will build the example CorDapp defined in the example CorDapp source. CorDapps can be written in
-any language targeting the JVM. In our case, we've provided the example source in both Kotlin (``/kotlin/src``) and
-Java (``/java/src``) Since both sets of source files are functionally identical, we will refer to the Kotlin build
+any language targeting the JVM. In our case, we've provided the example source in both Kotlin (``/kotlin-source/src``) and
+Java (``/java-source/src``) Since both sets of source files are functionally identical, we will refer to the Kotlin build
throughout the documentation.
For more information on the example CorDapp see "The Example CorDapp" section below. Gradle will then grab all the
@@ -278,7 +253,7 @@ dependencies for you and build the example CorDapp.
The ``deployNodes`` Gradle task allows you easily create a formation of Corda nodes. In the case of the example CorDapp
we are creating ``four`` nodes.
-After the building process has finished to see the newly built nodes, you can navigate to the ``kotlin/build/nodes`` folder
+After the building process has finished to see the newly built nodes, you can navigate to the ``kotlin-source/build/nodes`` folder
located in the ``cordapp-tutorial`` root directory. You can ignore the other folders in ``/build`` for now. The ``nodes``
folder has the following structure:
@@ -323,10 +298,7 @@ start the build process and output progress to a console window in the IDE.
Running the example CorDapp
---------------------------
-Running the example CorDapp from the command line
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To run the sample CorDapp navigate to the ``kotlin/build/nodes`` folder and execute the ``runnodes`` shell script with:
+To run the sample CorDapp navigate to the ``kotlin-source/build/nodes`` folder and execute the ``runnodes`` shell script with:
Unix: ``./runnodes`` or ``sh runnodes``
@@ -345,7 +317,7 @@ message and some pertinent config information, see below:
--- DEVELOPER SNAPSHOT ------------------------------------------------------------
- Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-tutorial/kotlin/build/nodes/nodea/logs
+ Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-tutorial/kotlin-source/build/nodes/nodea/logs
Database connection URL is : jdbc:h2:tcp://10.18.0.196:50661/node
Node listening on address : localhost:10004
Loaded plugins : com.example.plugin.ExamplePlugin
@@ -397,7 +369,7 @@ down menu at the top right-hand side of the IDE and press the green arrow to sta
.. image:: resources/run-config-drop-down.png
:width: 400
-The node driver defined in ``/src/main/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like
+The node driver defined in ``/src/main/kotlin-source/com/example/Main.kt`` allows you to specify how many nodes you would like
to run and the various configuration settings for each node. With the example CorDapp, the Node driver starts four nodes
and sets up an RPC user for all but the "Controller" node (which hosts the notary Service and network map service):
@@ -429,7 +401,7 @@ The CorDapp defines a few HTTP API end-points and also serves some static web co
list IOUs and add IOUs.
The nodes can be found using the following port numbers, defined in build.gradle and the respective node.conf file for
-each node found in `kotlin/build/nodes/NodeX`` etc:
+each node found in `kotlin-source/build/nodes/NodeX`` etc:
* Controller: ``localhost:10003``
* NodeA: ``localhost:10005``
@@ -505,25 +477,25 @@ activity in the terminal windows for NodeA and NodeB (note: the green ticks are
.. sourcecode:: none
-✅ Generating transaction based on new IOU.
-✅ Verifying contract constraints.
-✅ Signing transaction with our private key.
-✅ Sending proposed transaction to recipient for review.
-✅ Done
+ ✅ Generating transaction based on new IOU.
+ ✅ Verifying contract constraints.
+ ✅ Signing transaction with our private key.
+ ✅ Sending proposed transaction to recipient for review.
+ ✅ Done
*NodeB:*
.. sourcecode:: none
-✅ Receiving proposed transaction from sender.
-✅ Verifying signatures and contract constraints.
-✅ Signing proposed transaction with our private key.
-✅ Obtaining notary signature and recording transaction.
- ✅ Requesting signature by notary service
- ✅ Requesting signature by Notary service
- ✅ Validating response from Notary service
- ✅ Broadcasting transaction to participants
-✅ Done
+ ✅ Receiving proposed transaction from sender.
+ ✅ Verifying signatures and contract constraints.
+ ✅ Signing proposed transaction with our private key.
+ ✅ Obtaining notary signature and recording transaction.
+ ✅ Requesting signature by notary service
+ ✅ Requesting signature by Notary service
+ ✅ Validating response from Notary service
+ ✅ Broadcasting transaction to participants
+ ✅ Done
*NodeC:*
@@ -575,7 +547,7 @@ query them using SQL.
Using the Example RPC client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The ``/src/main/kotlin/com/example/client/ExampleClientRPC.kt`` file is a simple utility which uses the client RPC library
+The ``/src/main/kotlin-source/com/example/client/ExampleClientRPC.kt`` file is a simple utility which uses the client RPC library
to connect to a node and log the created IOUs. It will log any existing IOUs and listen for any future
IOUs. If you haven't created any IOUs when you connect to one of the Nodes via RPC then the client will log
and future IOUs which are agreed.
@@ -717,13 +689,13 @@ In the file structure above, the most important files and directories to note ar
* **gradle** contains the gradle wrapper, which allows the use of Gradle without installing it yourself and worrying
about which version is required.
* **lib** contains the Quasar.jar which is required for runtime instrumentation of classes by Quasar.
-* **kotlin** contains the source code for the example CorDapp written in Kotlin.
- * **kotlin/src/main/kotlin** contains the source code for the example CorDapp.
- * **kotlin/src/main/python** contains a python script which accesses nodes via RPC.
- * **kotlin/src/main/resources** contains the certificate store, some static web content to be served by the nodes and the
+* **kotlin-source** contains the source code for the example CorDapp written in Kotlin.
+ * **kotlin-source/src/main/kotlin** contains the source code for the example CorDapp.
+ * **kotlin-source/src/main/python** contains a python script which accesses nodes via RPC.
+ * **kotlin-source/src/main/resources** contains the certificate store, some static web content to be served by the nodes and the
PluginServiceRegistry file.
- * **kotlin/src/test/kotlin** contains unit tests for protocols, contracts, etc.
-* **java** contains the same source code, but written in java. This is an aid for users who do not want to develop in
+ * **kotlin-source/src/test/kotlin** contains unit tests for protocols, contracts, etc.
+* **java-source** contains the same source code, but written in java. This is an aid for users who do not want to develop in
Kotlin, and serves as an example of how CorDapps can be developed in any language targeting the JVM.
Some elements are covered in more detail below.
@@ -801,22 +773,24 @@ like to deploy for testing. See further details below:
.. sourcecode:: groovy
task deployNodes(type: com.r3corda.plugins.Cordform, dependsOn: ['build']) {
- directory "./kotlin/build/nodes" // The output directory.
+ directory "./kotlin-source/build/nodes" // The output directory.
networkMap "Controller" // The artemis address of the node to be used as the network map.
node {
name "Controller" // Artemis name of node to be deployed.
nearestCity "London" // For use with the network visualiser.
advertisedServices = ["corda.notary.validating"] // A list of services you wish the node to offer.
- artemisPort 10002
- webPort 10003 // Usually 1 higher than the Artemis port.
+ p2pPort 10002
+ rpcPort 10003 // Usually 1 higher than the messaging port.
+ webPort 10004 // Usually 1 higher than the RPC port.
cordapps = [] // Add package names of CordaApps.
}
node { // Create an additional node.
name "NodeA"
nearestCity "London"
advertisedServices = []
- artemisPort 10004
- webPort 10005
+ p2pPort 10005
+ rpcPort 10006
+ webPort 10007
cordapps = []
}
...
@@ -844,7 +818,7 @@ Re-Deploying Your Nodes Locally
If you need to create any additional nodes you can do it via the ``build.gradle`` file as discussed above in
``the build.gradle file`` and in more detail in the "cordFormation" section.
-You may also wish to edit the ``/kotlin/build/nodes//node.conf`` files for your nodes. For more information on
+You may also wish to edit the ``/kotlin-source/build/nodes//node.conf`` files for your nodes. For more information on
doing this, see the :doc:`Corda configuration file ` page.
Once you have made some changes to your CorDapp you can redeploy it with the following command:
@@ -859,32 +833,25 @@ Running Nodes Across Machines
The nodes can also be set up to communicate between separate machines on the
same subnet.
-After deploying the nodes, navigate to the build folder (`kotlin/build/
-nodes` or `java/build/nodes`) and move some of the individual node folders to
-separate machines on the same subnet (e.g. using a USB key). It is important
-that no nodes - including the controller node - end up on more than one
-machine. Each computer should also have a copy of `runnodes` and
-`runnodes.bat`.
+After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes`` or ``java-source/build/nodes``)
+and move some of the individual node folders to separate machines on the same subnet (e.g. using a USB key).
+It is important that no nodes - including the controller node - end up on more than one machine. Each computer
+should also have a copy of ``runnodes`` and ``runnodes.bat``.
For example, you may end up with the following layout:
-* Machine 1: `controller`, `nodea`, `runnodes`, `runnodes.bat`
-* Machine 2: `nodeb`, `nodec`, `runnodes`, `runnodes.bat`
+* Machine 1: ``controller``, ``nodea``, ``runnodes``, ``runnodes.bat``
+* Machine 2: ``nodeb``, ``nodec``, ``runnodes``, ``runnodes.bat``
-You must now edit the configuration file for each node, including the
-controller. Open each node's config file (`[nodeName]/node.conf`), and make
-the following changes:
+You must now edit the configuration file for each node, including the controller. Open each node's config file,
+and make the following changes:
-* Change the artemis address to the machine's ip address (e.g.
-`artemisAddress="10.18.0.166:10006"`)
-* Change the network map service details to the ip address of the machine where the
-controller node is running and to its legal name (e.g. `networkMapService.address="10.18.0.166:10002"` and
-`networkMapService.legalName=controller`) (please note that the controller will not have the `networkMapService` config)
+* Change the Artemis messaging address to the machine's IP address (e.g. ``p2pAddress="10.18.0.166:10006"``)
+* Change the network map service details to the IP address of the machine where the
+ controller node is running and to its legal name (e.g. ``networkMapService.address="10.18.0.166:10002"`` and
+ ``networkMapService.legalName=controller``) (please note that the controller will not have the ``networkMapService`` config)
-Each machine should now run its nodes using `runnodes` or `runnodes.bat`
-files. Once they are up and running, the nodes should be able to create
-IOUs among themselves in the same way as when they were running on
-the same machine.
+Now run each node.
Debugging your CorDapp
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/source/whitepaper/corda-introductory-whitepaper-jp.pdf b/docs/source/whitepaper/corda-introductory-whitepaper-jp.pdf
new file mode 100644
index 0000000000..495ced5e2e
Binary files /dev/null and b/docs/source/whitepaper/corda-introductory-whitepaper-jp.pdf differ
diff --git a/docs/source/whitepaper/corda-introductory-whitepaper-jp.tex b/docs/source/whitepaper/corda-introductory-whitepaper-jp.tex
new file mode 100644
index 0000000000..75854c813b
--- /dev/null
+++ b/docs/source/whitepaper/corda-introductory-whitepaper-jp.tex
@@ -0,0 +1,201 @@
+\documentclass{article}
+\usepackage{xeCJK}
+\setCJKmainfont{MS Mincho} % for \rmfamily
+\setCJKsansfont{MS Gothic} % for \sffamily
+\author{Richard Gendal Brown, James Carlyle, Ian Grigg, Mike Hearn}
+\date{2016年8月}
+\title{Corda: イントロダクション}
+%%\setlength{\parskip}{\baselineskip}
+\usepackage{amsfonts}
+\usepackage{listings}
+\usepackage{color}
+\usepackage{epigraph}
+\usepackage{graphicx}
+\graphicspath{ {images/} }
+\usepackage[export]{adjustbox}
+\usepackage{float}
+\usepackage{hyperref}
+\usepackage[super,comma,sort&compress]{natbib}
+\usepackage[nottoc]{tocbibind}
+%\usepackage[natbibapa]{apacite}
+\renewcommand{\thefootnote}{\alph{footnote}}
+%\epigraphfontsize{\small\itshape}
+\setlength\epigraphwidth{4.5cm}
+\setlength\epigraphrule{0pt}
+\begin{document}
+\maketitle
+\begin{abstract}
+相互に信頼しないノードが共有する分散台帳により、金融機関や個人間における取引や債務を記録する、ひとつのグローバルデータベースが可能となるでしょう。現在、別々に管理されている台帳を同期するために実施している手作業の多くは削減されるでしょう。また、今よりもより多くのコードが共有され、金融サービスのコスト削減につながるでしょう。私たちは、これらの目標を達成するために設計したCordaをここに発表します。このペーパーでは、一般の読者向けに概要レベルのイントロダクションを提供します。続けて公表されるテクニカルホワイトペーパーにて、設計とCordaアーキテクチャー採用の背景を詳細します。
+\end{abstract}
+\newpage
+\tableofcontents
+\newpage
+\section{イントロダクション}
+R3は、分散台帳技術が金融業界を変革し、顧客や関係者に便益をもたらす可能性を秘めていると考えています。私たちは金融取引が自動でエラーなく記録・管理され、誰もがシームレスにどんな契約でも取引出来るようになる未来を思い描いています。金融取引の当事者が取引を記録し、共有し、協働で正確に台帳をメンテナンスするやり方に変わっていくと考えています。重複やリコンシリエーション、認識相違等は過去のものとなるでしょう。各社が別々に資産を記録することもなくなっていくでしょう。
+私たちは、信頼できるテクノロジーを活用することで、既存の法的枠組みの中で展開できる金融のユースケース向けに共有台帳を定義しようとしています。私たちの考えは三つのカテゴリーに細分化できます。金融機関の要件の実装、非機能要件へのフォーカス、そして拡張性です。
+このペーパーでは、私たちが規制された金融機関向けに最適だと考えるCordaプラットフォームの設計上の特徴について紹介させて頂きます。\footnote{著者のメールアドレス: Richard Gendal Brown \href{mailto:richard@r3cev.com}{(richard@r3cev.com)}, James Carlyle \href{mailto:james@r3cev.com}{(james@r3cev.com)}, Ian Grigg \href{mailto:iang@r3cev.com}{(iang@r3cev.com)}, Mike Hearn \href{mailto:mike@r3cev.com}{(mike@r3cev.com)}}
+\section{背景}
+銀行は世の中で思われいるよりも早くから情報技術を受け入れ、手作業の自動化、処理のデジタル化を成功させてきました。しかしながら今、そのコストと効率性を著しく向上させるアーキテクチャーが登場してきています。
+各金融機関は独自の台帳を維持しており、顧客や相手方との契約やポジションについてその会社目線での見え方を記録しています。一方、相手方は相手方の見え方で取引を記録しています。この重複作業が不一致を引き起こし、取引に関連する様々な当事者間でマッチング、リコンシリエーション、エラーの修正を必要とさせるのです。同じトランザクションに対する見え方の違いの程度によっては、潜在的にシステミックな危険を孕んでいるとも言えます。
+複数の金融機関で競争することは良いですが、複数のテクノロジーで複雑さとオペレーションリスクを増加させてしまうことは良くありません。しかしながら、近年までこれは避けられませんでした。中央集権型の市場インフラを除いて\footnote{例としてDepository Trust \& Clearing Corporation (DTCC)やContinuous Linked Settlement Group (CLS).}, 、企業間のテクノロジーを統合する良い方法は(会社の合併なしで)ほとんどありませんでした。
+中央集権型の市場インフラユーティリティーはデータ量の増加と企業間でのビジネスロジックの共有という観点である程度上手く行きました。しかし、webの登場以来、情報技術の分野で達成してきたレベルに比べ、金融取引分野で統合はまだ遅れを取っています。\cite{IT}
+
+私たちは、暗号化技術の成熟や、いわゆる”ブロックチェーン技術”に代表される技術が、新たな機会を提供すると考えています。すなわち、企業間で安全にレコードを共有するシステムの可能性です。このビジョンは金融機関の経済を変革するかもしれません。金融イベントの記録やビジネスロジックの処理のために、新たな共有プラットフォームを実装することで、(これだけではないですが)特にポストトレード分野における変革をもたらします。企業間における全ての取引が信頼できる唯一でグローバル論理台帳に記録するのです。このアーキテクチャーは業界の新たな共有プラットフォームを定義し、この上で、現行の金融機関、新規参入者、第三者機関が競合し、革新的な商品やサービスを展開することができます。
+\begin{figure}[H]
+\includegraphics[scale=.5, center]{sharedlogic}
+\caption{上記の図は、不一致や重複がありながらも共有する事実を独自に記録・管理している世界(\textit{``リコンサイルを除いた相対取引"})もしくは中央集権型ユーティリティーに権限と責任を委譲する世界(\textit{``第三者/市場インフラ"}), から、協働で共有レコードを維持し、一貫性を確保し、既存/新規事業者および市場インフラ事業者がオープンに競争し、サービスを利用する世界(\textit{``共有台帳ビジョン"}).への進化を表しています。}
+\end{figure}
+私たちは、高品質なデータが企業間で不一致を減らし、迅速に合意形成をする上で重要になると考えています。さらに、企業を跨ぐこの共通アーキテクチャーの実装により、新たなプラットフォームが定義され、既存および新規事業者は顧客ニーズに合ったサービスを提供するために、競合することになるでしょう。また、企業内では複数のシステムに同じ取引を記録するという課題がコストと複雑化を助長する要因となっていますが、これを解決するアプリケーションも出てくるでしょう。
+\section{ビジョン}
+長期的には、全ての経済主体が相互にやり取り出来る、``グローバル論理台帳" を思い描くことが出来ます。誰でも安全で一貫性があり、信頼できてプライバシーも確保された方法で取引を記録・管理することが可能となります。グローバルと呼んでいるのは、誰でも取引相手と同じデータを見ることが出来るという意味で、論理と呼んでいるのは、物理的な実装は企業によって異なってくるという意味です。将来像は、企業内で維持される記録システムから、\textit{企業間}で共有されるグローバルな記録システムとなるでしょう。
+\subsection{将来像を支える考え方}
+分散台帳技術を活用した将来像を支える基本的な考え方は以下となるでしょう。
+\begin{itemize}
+ \item 契約に従い台帳に記録される事実には、紛争時に適用出来る法的根拠が与えられる。
+ \item 台帳に記録される事実は、他の場所で保持されるマスターデータのコピーという位置付けよりも、むしろ権威あるものとして認識される。これにより、プラットフォーム間での決済は直に行われる。
+ \item 一度取引当事者が合意すれば、台帳上の事実はファイナリティーを与えられ、改ざん不可となる。エラーや巻き戻しは後続のトランザクションを通じて行われる。企業は内部プロセスの正確さと品質向上に向けた見直しを迫られる。
+ \item 信頼のある主体は、原則として、直接台帳に接続し、相手方との取引を記録する。相対取引を強制する訳ではないが、”重層化”もしくは階層化した市場モデルは少なくなるかもしれない。
+ \item オープンスタンダートを推進することで、既存および新規事業者が協業・競合し、差別化したサービスを提供することで、より多くの選択肢をもたらす。
+ \item 取引の内容を知る必要のある範囲の当事者だけが、当該金融取引にアクセスすべき正当な権限を持つ。
+\end{itemize}
+しかしながら、このビジョンは道半ばの状況を型取っており、まずはビジネスロジックの共有に専念するかもしれません。これは、今日私たちが使用しているシステムと当面の間共存する必要があるためです。統合や移行はソリューション設計上、重要な部分であると認識しています。道半ばではあるものの、長期的ビジョンのうち、リーガル面および他のテクニカル以外の部分を同時並行で取り組むことは、それだけでも大きな価値をもたらすでしょう。
+グローバル論理台帳という方向性を示しつつも、それが複数の台帳により実現される可能性がある点について今一度強調しておきたいと思います。おそらくこれは、アセットクラスごとに一つの台帳の形となるか、自発的に緩く結びついて、異なるビジネスサービス間で独立した機能・運営となるかもしれません。
+
+このビジョンを支えるアーキテクチャー上の戦略的な判断は以下になります。
+\begin{itemize}
+\item このシステムにより管理されるレコードは、資産や取引に対し正当な権利を持つ者だけがアクセス可能となる。
+\item 取引の挙動はシステムにより管理され、コンピューターコードにより表現される。コンピューターコードの正当性は参照する法律文書により確保する。\cite{Ricardian}
+\item コントラクトコード更新のサポート。紛争解決の手続きが成立しない契約をどう進めれば良いかをサポートする。これは、いくら自動化しても、契約上の紛争は発生し、結果としてテクニカルと人手の両方を必要とすることになるためである。
+\item このビジョンが実現すれば、コスト、リスクや規制対応負荷(資本、流動性およびオペレーション上の義務)が軽減され、新たな商品やサービスの開発に繋がる。
+\item 金融業界に広く普及するために、システムの一部はオープンにすべき。すなわち、オープンソース、オープンな開発プロセス、オープンスタンダードであること。
+\item このビジョンは”プラットフォーム”もしくは”システム”の観点で語られているが、事業者が競合・協業し、異なるレイヤーで異なる商品・サービスを実現出来るようになる考えている。垂直統合により我々が独占してしまうようなアプローチは思い描いていない。
+\item このビジョンはまた、一番上位のレイヤーで個別の企業やグループが独自のIPを保持することを想定している。
+\item このシステムはセキュリティが確保された環境の下で運営する。高まるサイバー犯罪の脅威は所与のものとして考慮されるべき。
+\end{itemize}
+このビジョンの実現に必要な基本技術は既に存在すると信じられていました。これらには(これだけに限りませんが)、暗号化技術、グローバルコミュニケーションネットワーク、金融商品の標準化や本格的な運営にも耐え得るアルゴリズムを含みます。
+このビジョンを可能とするものは、近年における分散台帳およびブロックチェーンへの関心の高まりです。このようなビジョンがオープンに議論され、複数の金融機関の協働を通じて、環境が整ってきました。ネットワークの参加者間でのIDインフラの構築が想定されていますが、具体的にどうオペレーションが改善されるかまでは想定を置いていません。当局の巻き込みがプロセス設計上の鍵となります。
+私たちは、金融機関の要件と既存の分散台帳プラットフォームを評価した結果、既存のプラットフォームでは、金融機関のニーズに対応出来ないとの結論に達しました。つまり、これまでの分散データベースの設計を支えるスレッドモデルは、相互に信頼しない法人格が合意に至るためのユースケースには合いませんでした。既存のブロックチェーンのアーキテクチャーは、個別の契約レベルで特定のデータを制限しシェアするという要件には合わないのです。その結果、私たちはCordaを設計・開発致しました。
+\section{Corda}
+Cordaは金融取引を記録・処理するための分散台帳プラットフォームであり、このドキュメントに記載しているビジョンを実装するために設計されました。
+Cordaは、Clack, Bakshi, Braineが定義するスマートコントラクトをサポートします。\cite{SCT} 私たちのスマートコントラクトでは、人による入力をもとに、コンピューターコードがその実行を自動化します。債権や債務は法律文書で表現されるように、法的に執行力を持ちます。スマートコントラクトは法律文書に関連したビジネスロジックとデータにリンクし、プラットフォーム上の金融取引に法的執行力を持たせ、曖昧さや不確実性、紛争時における明確な拠り所とすることが出来ます。
+\subsection{基本的な特徴}
+Cordaは規制されている金融機関向けの利用に特化しています。ブロックチェーンに大きく影響されていますが、多くの金融向けシナリオには不適切なブロックチェーンは採用しませんでした。
+Cordaはスマートコントラクトを実行するフレームワークであり、以下の特徴を持ちます。
+\begin{itemize}
+ \item{金融取引の遷移を記録・管理し、既存の法的枠組みの中で二者以上の当事者間で共有するデータが根付く土壌となり、既存および今後課される規制にも対応}
+ \item{中央集権型管理者なしで企業間におけるワークフローの振る舞いを管理}
+ \item{グローバルな台帳レベルではなく、個別取引レベルで合意形成をサポート}
+ \item{当局による監督者ノードをサポート}
+ \item{トランザクションの検証は当事者間だけで実施}
+ \item{様々なコンセンサスメカニズムをサポート}
+ \item{人が理解できる法律文書とスマートコントラクトのコードとの明確なリンクを記録}
+ \item{業界標準のツールを使用}
+ \item{資格を有する者もしくは正当な権限を持つ者だけに取引データへのアクセスを制限}
+\end{itemize}
+これらの特徴が、複雑な金融機関での利用に適したプラットフォームの設計に貢献しています。この設計では、ネイティブな仮想通貨を使ったりやグローバルなトランザクションの速度制限を課したりしません。
+\subsection{コンセプト}
+私たちは、信頼できる唯一のデータソースであるグローバル台帳という考え方から始めました。しかしながら、トランザクションと台帳への記帳をグローバルに見える化してしまうことは私たちのモデルには合いませんでした。トランザクションは当事者による小グループ内だけに留め、関連データはグループ内だけで維持出来るようにしたかったのです。
+私たちのコンセプトにおける一番基本的な概念は\textit{ステートオブジェクト}と言います。これは電子的ドキュメントであり、二者以上の当事者間における合意の存在、内容、現状のステータスを記録します。ステートオブジェクトは、参照する正当な理由を持った者だけの間で共有されます。全ての参加者に全てのデータが参照可能となっていない共有システムにおいて、グローバルで一貫性を保つために、私たちは当事者とデータを特定するために暗号化技術(ハッシュ)を活用しています。台帳は改ざん不能なステートオブジェクトの集まりとして定義されます。
+私たちは合意のステートオブジェクトという観点で考えていますが、目的はこのステートオブジェクトが遷移する度に、取引に関係する全当事者が合意形成することです。これはブロックチェーンのコンセプトだと言う人もいるかも知れませんが、単純な支払処理から複雑なスマートコントラクトの処理に至るまで、異なる主体により保持されるデータが、更新されても一貫性を保つことで、信頼できる取引基盤を形成することが出来るのです。
+\begin{figure}[H]
+\includegraphics[scale = .4, center]{partiesto}
+\caption{上記図では、ステートオブジェクトが商業銀行に対する100ポンドのキャッシュ請求権を表しています。キャッシュのオーナーは架空の船会社です。このステートオブジェクトは取引を管理する法律文書とその遷移を管理するコントラクトコードへのリンク(ハッシュ)を持っています。}\end{figure}
+参加者が台帳全体の状態もしくは仮想マシン全体でコンセンサスを取るシステムとは対照的に、私たちは合意のステートオブジェクトにフォーカスしています。Cordaはグローバルで分散された合意形成を実現するために、以下3つのツールを提供します。
+\begin{itemize}
+ \item 事前に合意したルールに則り、ステートオブジェクトの遷移が有効であるかを検証するスマートコントラクトのロジック
+ \item トランザクションを一時的に並べて衝突を回避するユニークネス(一意性)およびタイムスタンプサービス
+ \item 複数の異なる当事者間で、何ステップもの複雑なやり取りを書くプロセスを単純化するオーケストラフレームワーク
+ \end{itemize}
+\subsection{コンセンサス}
+Cordaでは、\textit{トランザクション}を使って更新が行われます。既に存在するステートオブジェクトを消費し、新しいステートオブジェクトを生成します。ここで二つ合意形成がなされます。
+\begin{enumerate}
+\item{トランザクションの妥当性:アウトプットのステートオブジェクトを定義する更新トランザクション(提案された状態)が有効であること、関連するコントラクトコードが問題なく実行され必要な署名がされていること、このトランザクションを参照する全てのトランザクションもまた有効であること、以上により、当事者は取引に確信を持ちます。}
+\item{トランザクションの一意性:インプットとなる全てのステートオブジェクトがそのトランザクションだけで消費されること、すなわち、同じステートオブジェクトを消費する他のトランザクションが過去に存在しないこと、これにより当事者は取引に確信を持ちます。}
+\end{enumerate}
+当事者は、別々に同じコントラクトコードとバリデーションロジックを走らせることで、トランザクションの妥当性に合意することができます。しかしながら、一意性に対するコンセンサスには事前に決めておいたオブザーバーが必要となります。多くの場合、独立したノードになるかと思います。
+\begin{figure}[H]
+ \includegraphics[scale = .5, center]{Consensus}
+ \caption{トランザクションの妥当性に対するコンセンサスは、そのトランザクションに関する当事者だけにより検証されます。そのため、データはそれを見る必要のある人だけに配信されます。他のプラットフォームでは、一般的に台帳レベルでコンセンサスを取ります。Cordaでは、システム全体として管理しているデータの一部分だけが当事者に共有されます。少なくとも二つの主体がデータの存在と詳細についてコンセンサスに至っているのであれば、それは``on-ledger"と言えます。Cordaでは、任意の主体の組み合わせで、あらゆるデータの合意形成プロセスを実施することが可能です。データが一つの主体により保持されている場合、``off-ledger"と言えます。}
+\end{figure}
+Cordaのユニークネスサービスは``追加可能"です。これにより、プライバシー、スケーラビリティー、リーガルシステムとの互換性\cite{EUC} やアルゴリズムを強化することができます。あるサービスはビザンティンフォールトトレランスアルゴリズムによって、相互に信頼しない多数のノードにより構成されるかもしれません。もしくはスタンドアローンのようにシンプルな作りになるかもしれません。ある場合は、ステートオブジェクトの遷移に全ての当事者の署名が必要となるかもしれません。その場合、ユニークネスサービスは全く必要なくなるでしょう。
+重要な点は、これらユニークネスサービスが、ステートオブジェクトがトランザクションにより事前に消費されているかどうかを検証するためだけに必要であるという点です。トランザクションそのものを検証するために必要なのではありません。それはトランザクションに関係する当事者間の問題となります。つまり、ユニークネスサービスはトランザクションの中身まで見る必要はないということです(また、見せるべきでもありません)。これにより、他の分散台帳やブロックチェーンに比べて、プライバシーとスケーラビリティーを著しく向上させています。この設計上の判断は、共有台帳アーキテクチャーにおけるトレードオフについて重大な影響を及ぼしています。今後発表するテクニカルホワイトペーパーの中で詳細は議論したいと思います。
+\subsection{ビジネスロジック}
+Cordaはスマートコントラクトのコードを通じてビジネスロジックを実行します。スマートコントラクトは、取引を受け入れるか拒否するかの機能を提供し、シンプルで再利用可能な機能で構成されています。トランザクションを解釈し、インプットとしてステートオブジェクトを受け取り、アプリケーションのコマンド(スマートコントラクト)によりアウトプットのステートオブジェクトを生成します。提案されたアクションが有効であれば、トランザクションは受け入れられます。コントラクトコードはビジネスロジックを定義し、また可動性があります。規制のある場面では署名したコードを使う想定ですが、ノードはデプロイ時にチェックなしで、サンドボックスの中のコントラクトコードをダウンロード・実行します。
+コントラクトコードの実行と検証のために、私たちはJava Virtual Machine\cite{JVM}を選択しました。既存のライブラリーが豊富で、スキルを持った開発者が多数いるからです。業界標準のツールを使うことで、銀行は既存のコードを簡単に再利用することができます。しかし、通常のJVMよりもより制限を厳しくしたカスタムサンドボックスを使うことで、セキュリティー要件だけでなく、確定的な実行環境を提供しています。イーサリアム6のように、バイトコードを標準化する選択肢が用意しており、ユーザーは好みによって、コントラクトコードの言語を変えたり、もしくはよく知られている言語を再利用することが出来ます。内部アプリケーションから直接コントラクトコードを利用することも簡単です。一度コントラクトコードがレビューされていれば、アプリケーションの開発はかなりシンプルなものになるでしょう。
+\subsection{コアとなる金融コンセプト}
+Cordaのアーキテクチャーは3つの設計上鍵となるユースケースにより強く影響されています。これらは解決すべき共通の課題を内包していると考えられています。そのユースケースとは、キャッシュ、証券化商品、そしてデリバティブ取引です。
+\begin{itemize}
+\item キャッシュの残高(例えば、``私は、百万ドルを預けていることを銀行と同意している")
+\item 預託している証券化商品(例えば、``私は、この会社の株を1,000株保有していることを銀行と同意している")
+\item 相対デリバティブ取引(例えば、``銀行Aと銀行Bは次の金利スワップに同意している。すなわち、次のキャッシュフロー(ネット)を事前に取り決めしたスケジュール、支払条件に従って交換すること。")
+\end{itemize}
+ここでは一つ例を取り上げます。Cordaのキャッシュの設計は”銀行の中にお金”のようなものは存在しないという現実をモデリングしており、銀行に対しキャッシュの請求権を所有者が持っているだけです。\cite{BOE}つまり、私たちの考えるキャッシュの契約はシンプルですがとても有用です。キャッシュ発行者である法人、通貨、金額、所有者を記録し(その他請求権の特徴を考慮した他の情報、またこの合意を管理する\textit{法律文書}への明確なリンクを付します。これは紛争時の解決手段として使用されることを想定しています)、これらはその他キャッシュ関連のコンセプト(支払、ネッティング等々)を構築する際に使用されます。
+\begin{figure}[H]
+\includegraphics[scale = .4, center]{cash}
+\caption{上記は、最もシンプルなCordaのトランザクション(キャッシュの発行)を示しています。商業銀行から架空の船会社に、新しいキャッシュのステートオブジェクトが発行されているのが分かります。この発行のトランザクションは発行銀行により署名されます。このようなシンプルなモデルから、支払、DVP決済や先日付の債務のような複雑なトランザクションに至るまで構築が可能です。}
+\end{figure}
+\subsection{Cordaモデルのまとめ}
+私たちのモデルにおけるコアコンセプトは以下の通りです。
+\begin{itemize}
+\item \textit{ステートオブジェクト}, 二者以上の当事者間における合意を表し、機械が読み込める\textit{コントラクトコード}により管理されます。そしてコントラクトコードは人間が読み込める\textit{法律文書}を参照し、実装されるものです。
+\item \textit{トランザクション}, ステートオブジェクトを遷移させていきます。
+\item \textit{トランザクションプロトコル}または\textit{ビジネスフロー}, 中央管理者なしで、取引の当事者が次のアクションを調整することを可能とします。\end{itemize}
+プログラミングのテクニックによりステートオブジェクトの共有を最小限の範囲に留めつつ、取引の確定を実現します。
+ステートオブジェクト(データ)、コントラクトコード(許容するオペレーション)、トランザクションプロトコル(ビジネスロジックの振る舞い)、その他必要なAPI、ウォレットプラグインやUIコンポーネントの組み合わせにより、共有台帳アプリケーション、もしくはCorda分散アプリケーション(\textit{``CorDapp"})は構成されます。これは開発者がプラットフォーム上で作ることとなる、コンポーネントのセットになります。
+%\begin{figure}[H!]
+%\includegraphics[scale = .4, center]{image4}
+%\caption{Current thinking on the applications in the Corda-driven ecosystem.}
+%\label{fig:figure4}
+%\end{figure}
+%\begin{figure}[H!]
+%\includegraphics[scale = .25, center]{image5}
+%\caption{Another visual representation on how Corda will interact with the financial ecosystem.}
+%\label{fig:figure5}
+%\end{figure}
+\section{他プラットフォームとの比較}
+Cordaは金融機関の実務担当者の協力により制作され、金融機関の要件に基づき設計されました。しかしながら、そのデザインはTodd Boyle と Ian Grigg による triple entry accounting\cite{Triple}やBitcoin\cite{Bitcoin}やイーサリアムのような既存の分散台帳プラットフォームにもインスパイアされています。そのため、これらのプラットフォームとの関係においてCordaを理解した方が分かりやすいかもしれません。
+\subsection{ビットコインとの比較}
+Cordaはビットコインと非常に似ている部分があります。
+\begin{itemize}
+\item{トランザクションにより生成・消費される改ざん不能なステートオブジェクト。}
+\item{トランザクションは複数のインプットとアウトプットを持っています。ビットコインは未使用トランザクションアウトプット(UTXO)として台帳を参照します。}
+\item{コントラクトコードは単なる機能です。ストレージは持っていませんし、他とやり取りすることも出来ません。同じトランザクションを想定すると、コントラクトコードの”検証”機能は常に同じ結果を出力します。}
+\end{itemize}
+しかしながら、ビットコインのトランザクションは単一で固定したデータフォーマットであり、ビットコインの量と関連するルール(スクリプト)以外のデータを保持することはほぼ出来ません。コントラクトコードのカスタマイズ可能な箇所にデータを組み込むことで、この制限を回避しようとする試みもあります。パターンマッチングを通じてデータを抜き取ることが出来ますが、これは良いアプローチとは言えません。これに対し、ステートオブジェクトは任意のデータタイプを扱うことができます。加えて、トランザクションはインプットとなるコントラクトだけでなく、アウトプットとなるコントラクトも起動できます。ビットコインのトランザクションは、消費されたインプットのステートの中のコントラクトコードによりコントロールされます。私たちが呼ぶ”コントラクト”という用語は、トランザクションの検証以上に、多彩なタスクをハンドリング出来るビジネスロジックのことを指しています。例えば、現在、私たちのコントラクトもまた有効なトランザクションを生成するコードを含んでいます。(これはビットコインの”ウォレットコード”に当たります)
+ビットコインのスクリプトはインプットとして、固定のバイト配列を受け取ります。これは、コントラクトがトランザクション全体の構造を精査する他の方法がないことを意味しており、コントラクトに制限を課しています。私たちのコントラクトはチューリング完全であり、JVM上で動く普通のプログラミング言語で書かれています。Cordaは、ブロックがマイニングする時間に左右されず、任意の時間に正確にトランザクションを実行することが可能です(信頼できるタイムスタンパーによる認証が必要)。私たちがサポートする多くの契約が正確なタイミングを必要としていることを考えると、私たちの合意形成の方式である、ブロックを使わずに衝突を解決するアルゴリズムは重要となります。Cordaは、プルーフ・オブ・ワークを利用せず、``マイニング"のコンセプトも持っていないのです。
+\subsection{イーサリアムとの比較}
+イーサリアムでは、コードは仮想マシンの中で実行され、複雑なロジックを実装可能です。コントラクトのプログラミングには、ノンアセンブリベースの言語が使用されます。様々な種類の金融取引のモデリングを想定しています。
+しかしながら、イーサリアムにおける``コントラクト"という用語は、全ての参加者ノードによりメンテされ、複製されるプログラムの初期化を意味します。この初期化はオブジェクト指向言語のオブジェクトのようなものです。メッセージを送受信したり、ローカルのストレージを更新したり出来ます。一方、私たちのスマートコントラクトの実装は一連の機能を意味しており、そのうちの一つがシステムを同期させる機能になります(\textit{検証機能})。この機能は全くのステートレスです(すなわち、実行中に他システムとやり取りしません)。コントラクトコードは可変のストレージを持っていないため、``メッセージ"という考え方はありません。イーサリアムは金融のロジックだけでなく、文字通りどんなアプリケーションにも対応するプラットフォームであると主張しています。私たちのプラットフォームは、少なくとも最初は非金融向けアプリケーションをスコープ外としております。
+
+%\section{Worked Example: Cash}
+%A state object (depicted ) represents an agreement shared between parties.
+
+%States contain arbitrary data, but they always contain at least a reference to the hash of a contract code, which is a program expressed in some byte code that runs sandboxed inside a virtual machine, and a reference to the hash of a legal prose document, which provides legal context in a form that is recognised by a judicial or other dispute resolution system. \textit{Contract code} (or just âContractsâ in the rest of this document) are globally shared pieces of business logic. Contracts always define a verify function, which is a pure function that is able to determine whether the transition for the State is valid, regardless of which party calls the function. The verify function does not check that a transaction is in the interests of either party, only that its constraints are enforced, and nodes must separately satisfy themselves the transaction is in their own interests. This is discussed more later.
+
+%In the diagram above, we see a stylised \textit{cash state object}, which represents the ownership of 100 USD. The object refers to its governing contract and associated legal prose. As this is a cash contract, the legal prose document will specify the terms and conditions that pertain to a cash liability issued by one party to another: under what circumstances can the cash object be redeemed for a balance in a traditional bank account or in exchange for a wire transfer elsewhere? How will disputes be resolved? And so forth. In addition, the legal prose will specify those rights, obligations and conditions associated with the contract which the parties agree will be automated through computer code. We will explicitly delegate from the legal prose domain to the code domain.
+
+%Furthermore, and critically, the legal prose will also leave several pieces of information unspecified: who is the issuer and what is the currency? The state object contains these fields (we see the issuer here is Barclays Bank PLC and the currency is USD).The combination of the legal prose, contract code and parameters captured in the state object plus relevant digital signatures collectively define the agreement between the parties.
+
+%In what follows, we describe how an instance of a state object, such as the one above, can be evolved, processed and transferred between parties using the other components of the Corda architecture.
+
+%Note: At a very high level, this design is analogous to a "UTXO model" such as bitcoin's. However, it also has some significant differences. The choice of a UTXO-style model rather than an account/balance model such as that used in Ethereum is a conscious and deliberate choice and is foundational to the privacy and scalability characteristics of the platform
+
+\section{ロードマップ}
+現在の設計思想に辿り着くまで、私たちは当初Cordaのプロトタイプを開発し、シミュレーションして、コンセプトを検証しました。網羅的ではありませんが、短中期的に実装しようとしているCordaの追加機能を以下に示します。
+\begin{itemize}
+\item トランザクションの分割と一意性の向上:ユニークネスサービスからの読み込みを困難とするトランザクションの部分的な不可視化メカニズムの取り込み
+\item コントラクト検証サンドボックス:コントラクト実行のための最小限Javaライブラリーセットのホワイトリスト
+\item 残高等ポジション計算のためのプラグインベースウォレット
+\item 参加者が利用できる特定ビジネスロジックへのゲートウェイやオラクル(仲介ノードや評価エンジン等)
+\item ユーザーのID管理が出来るCordaモデルの利用
+\item 相互運用可能性とデータ統合、特にFpML, ISO20022。他データフォーマットや他プラットフォームとの統合/相互運用可能性のサポート
+\item レファレンスデータのためのアプリケーション構築
+\item アドレスのランダム化、ゼロ知識証明、資産再発行スキームなどの技術を使ったプライバシーの向上
+\item より多くの金融商品の実装
+\item ステートオブジェクトの集約など、ポートフォリオレベルのビジネスロジックのサポート
+\end{itemize}
+\section{結論}
+今日における既存の分散台帳とブロックチェーン技術とは異なり、Cordaは金融機関における取引の記録・執行という明確な目的のために開発されました。汎用的なソリューションで、全ての課題を解決しようとしている訳ではありません。そのため、データ配信とトランザクションセマンティクスに独自のアプローチを採用しております。一方で、金融機関にとって魅力的な分散台帳の特徴も維持しています。すなわち、金融取引を自動でかつ実効性のある方法で実行するという特徴です。
+\bibliographystyle{unsrt}
+\bibliography{Ref}
+\end{document}
\ No newline at end of file
diff --git a/docs/source/whitepaper/corda-introductory-whitepaper.pdf b/docs/source/whitepaper/corda-introductory-whitepaper.pdf
new file mode 100644
index 0000000000..cde8a10e5c
Binary files /dev/null and b/docs/source/whitepaper/corda-introductory-whitepaper.pdf differ
diff --git a/experimental/build.gradle b/experimental/build.gradle
index 8c55819c4d..42acd2995d 100644
--- a/experimental/build.gradle
+++ b/experimental/build.gradle
@@ -3,8 +3,6 @@ version '1.0-SNAPSHOT'
apply plugin: 'kotlin'
-sourceCompatibility = 1.5
-
repositories {
mavenLocal()
mavenCentral()
@@ -28,6 +26,9 @@ dependencies {
compile project(':core')
compile project(':finance')
+ // ObjectWeb Asm: a library for synthesising and working with JVM bytecode.
+ compile "org.ow2.asm:asm:5.0.4"
+
testCompile "junit:junit:$junit_version"
testCompile project(':test-utils')
}
diff --git a/experimental/src/main/kotlin/net/corda/carpenter/ClassCarpenter.kt b/experimental/src/main/kotlin/net/corda/carpenter/ClassCarpenter.kt
new file mode 100644
index 0000000000..7641b9c6ea
--- /dev/null
+++ b/experimental/src/main/kotlin/net/corda/carpenter/ClassCarpenter.kt
@@ -0,0 +1,275 @@
+package net.corda.carpenter
+
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes.*
+import org.objectweb.asm.Type
+import java.lang.Character.*
+import java.util.*
+
+/**
+ * Any object that implements this interface is expected to expose its own fields via the [get] method, exactly
+ * as if `this.class.getMethod("get" + name.capitalize()).invoke(this)` had been called. It is intended as a more
+ * convenient alternative to reflection.
+ */
+interface SimpleFieldAccess {
+ operator fun get(name: String): Any?
+}
+
+/**
+ * A class carpenter generates JVM bytecodes for a class given a schema and then loads it into a sub-classloader.
+ * The generated classes have getters, a toString method and implement a simple property access interface. The
+ * resulting class can then be accessed via reflection APIs, or cast to one of the requested interfaces.
+ *
+ * Additional interfaces may be requested if they consist purely of get methods and the schema matches.
+ *
+ * # Discussion
+ *
+ * This class may initially appear pointless: why create a class at runtime that simply holds data and which
+ * you cannot compile against? The purpose is to enable the synthesis of data classes based on (AMQP) schemas
+ * when the app that originally defined them is not available on the classpath. Whilst the getters and setters
+ * are not usable directly, many existing reflection based frameworks like JSON/XML processors, Swing property
+ * editor sheets, Groovy and so on can work with the JavaBean ("POJO") format. Feeding these objects to such
+ * frameworks can often be useful. The generic property access interface is helpful if you want to write code
+ * that accesses these schemas but don't want to actually define/depend on the classes themselves.
+ *
+ * # Usage notes
+ *
+ * This class is not thread safe.
+ *
+ * The generated class has private final fields and getters for each field. The constructor has one parameter
+ * for each field. In this sense it is like a Kotlin data class.
+ *
+ * The generated class implements [SimpleFieldAccess]. The get method takes the name of the field, not the name
+ * of a getter i.e. use .get("someVar") not .get("getSomeVar") or in Kotlin you can use square brackets syntax.
+ *
+ * The generated class implements toString() using Google Guava to simplify formatting. Make sure it's on the
+ * classpath of the generated classes.
+ *
+ * Generated classes can refer to each other as long as they're defined in the right order. They can also
+ * inherit from each other. When inheritance is used the constructor requires parameters in order of superclasses
+ * first, child class last.
+ *
+ * You cannot create boxed primitive fields with this class: fields are always of primitive type.
+ *
+ * Nullability information is not emitted.
+ *
+ * Each [ClassCarpenter] defines its own classloader and thus, its own namespace. If you create multiple
+ * carpenters, you can load the same schema with the same name and get two different classes, whose objects
+ * will not be interoperable.
+ *
+ * Equals/hashCode methods are not yet supported.
+ */
+class ClassCarpenter {
+ // TODO: Array types.
+ // TODO: Generics.
+ // TODO: Sandbox the generated code when a security manager is in use.
+ // TODO: Generate equals/hashCode.
+ // TODO: Support annotations.
+ // TODO: isFoo getter patterns for booleans (this is what Kotlin generates)
+
+ /**
+ * A Schema represents a desired class.
+ */
+ class Schema(val name: String, fields: Map>, val superclass: Schema? = null, val interfaces: List> = emptyList()) {
+ val fields = LinkedHashMap(fields) // Fix the order up front if the user didn't.
+ val descriptors = fields.map { it.key to Type.getDescriptor(it.value) }.toMap()
+
+ fun fieldsIncludingSuperclasses(): Map> = (superclass?.fieldsIncludingSuperclasses() ?: emptyMap()) + LinkedHashMap(fields)
+ fun descriptorsIncludingSuperclasses(): Map = (superclass?.descriptorsIncludingSuperclasses() ?: emptyMap()) + LinkedHashMap(descriptors)
+ }
+
+ class DuplicateName : RuntimeException("An attempt was made to register two classes with the same name within the same ClassCarpenter namespace.")
+ class InterfaceMismatch(msg: String) : RuntimeException(msg)
+
+ private class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
+ fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
+ }
+ private val classloader = CarpenterClassLoader()
+
+ private val _loaded = HashMap