Merge remote-tracking branch 'open/master' into os-merge-2907250

# Conflicts:
#	build.gradle
#	client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt
#	node-api/build.gradle
#	node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/custom/StringBufferSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/AllButBlacklisted.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/AttachmentsClassLoader.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/DefaultWhitelist.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/GeneratedAttachment.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/OrdinalIO.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/SerializationFormat.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/SerializeAsTokenContextImpl.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/SharedContexts.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/UseCaseAwareness.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPDescriptorRegistry.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPPrimitiveSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ArraySerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CollectionSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CorDappCustomSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/CustomSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializationInput.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializedGenericArrayType.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/DeserializedParameterizedType.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumEvolutionSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EnumSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Envelope.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/FingerPrinter.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/MapSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertySerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertySerializers.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/Schema.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationOutput.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SingletonSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SupportedTransforms.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformTypes.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/TransformsSchema.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/BigDecimalSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/BigIntegerSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/BitSetSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/CertPathSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ClassSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ContractAttachmentSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/CurrencySerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/DurationSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/EnumSetSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/InputStreamSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/InstantSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalDateTimeSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/LocalTimeSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/MonthDaySerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetDateTimeSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OffsetTimeSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/OpaqueBytesSubSequenceSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PeriodSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PrivateKeySerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/PublicKeySerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/SimpleStringSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ThrowableSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/X509CRLSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/X509CertificateSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearMonthSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/YearSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZoneIdSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/amqp/custom/ZonedDateTimeSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Exceptions.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/MetaCarpenter.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/Schema.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/SchemaFields.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClassResolver.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/kryo/CordaClosureSerializer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/kryo/DefaultKryoCustomizer.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/kryo/Kryo.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/kryo/KryoSerializationScheme.kt
#	serialization/src/main/kotlin/net/corda/serialization/internal/kryo/SerializeAsTokenSerializer.kt
#	serialization/src/test/java/net/corda/serialization/internal/ForbiddenLambdaSerializationTests.java
#	serialization/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/ErrorMessageTests.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/JavaGenericsTest.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/JavaPrivatePropertyTests.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerialiseEnumTests.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/ListsSerializationJavaTest.java
#	serialization/src/test/java/net/corda/serialization/internal/amqp/SetterConstructorTests.java
#	serialization/src/test/kotlin/net/corda/serialization/internal/AttachmentsClassLoaderTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/ContractAttachmentSerializerTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/PrivateKeySerializationTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/CorDappSerializerTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeAndReturnEnvelopeTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeMapTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeSimpleTypesTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializedParameterizedTypeTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumEvolvabilityTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumEvolveTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/ErrorMessagesTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerGetterTesting.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/FingerPrinterTesting.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/GenericsTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/OverridePKSerializerTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/PrivatePropertyTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationPropertyOrdering.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializeAndReturnSchemaTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterWhitelistTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CompositeMemberCompositeSchemaToClassCarpenterTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/EnumClassTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/InheritanceSchemaToClassCarpenterTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/MultiMemberCompositeSchemaToClassCarpenterTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/SingleMemberCompositeSchemaToClassCarpenterTests.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoStreamsTest.kt
#	serialization/src/test/kotlin/net/corda/serialization/internal/kryo/KryoTests.kt
#	settings.gradle
This commit is contained in:
Shams Asari 2018-05-21 10:31:57 +01:00
commit 152848730d
312 changed files with 1823 additions and 1237 deletions

2
.idea/compiler.xml generated
View File

@ -141,6 +141,8 @@
<module name="samples_test" target="1.8" />
<module name="sandbox_main" target="1.8" />
<module name="sandbox_test" target="1.8" />
<module name="serialization_main" target="1.8" />
<module name="serialization_test" target="1.8" />
<module name="shell_integrationTest" target="1.8" />
<module name="shell_main" target="1.8" />
<module name="shell_test" target="1.8" />

View File

@ -1,11 +1,11 @@
# List of Contributors
We'd like to thank the following people for contributing to Corda, either by
contributing to the design of Corda during the architecture review sessions of the
R3 Architecture Working Group and during design reviews since Corda has been
open-sourced, or by contributing code via pull requests. Some people have
moved to a different organisation since their contribution. Please forgive any
omissions, and create a pull request, or email <james@r3.com>, if you wish to
We'd like to thank the following people for contributing to Corda, either by
contributing to the design of Corda during the architecture review sessions of the
R3 Architecture Working Group and during design reviews since Corda has been
open-sourced, or by contributing code via pull requests. Some people have
moved to a different organisation since their contribution. Please forgive any
omissions, and create a pull request, or email <james@r3.com>, if you wish to
see changes to this list.
* acetheultimate
@ -164,6 +164,7 @@ see changes to this list.
* Simon Taylor (Barclays)
* Sofus Mortensen (Digital Asset Holdings)
* stevenroose
* Stanly Johnson (Servntire Global)
* Szymon Sztuka (R3)
* tb-pq
* Thiago Rafael Ferreira (Scorpius IT Solutions)
@ -175,7 +176,7 @@ see changes to this list.
* tomconte
* Tommy Lillehagen (R3)
* tomtau
* Tudor Malene (R3)
* Tudor Malene (R3)
* Tushar Singh Bora
* varunkm
* verymahler

View File

@ -190,6 +190,7 @@ allprojects {
apiVersion = "1.2"
jvmTarget = "1.8"
javaParameters = true // Useful for reflection.
freeCompilerArgs = ['-Xenable-jvm-default']
}
}
@ -378,7 +379,7 @@ bintrayConfig {
projectUrl = 'https://github.com/corda/corda'
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
publications = ['corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver', 'corda-confidential-identities', 'doorman', 'doorman-hsm', 'corda-shell', 'corda-bridgeserver', 'corda-ptflows', 'jmeter-corda', 'migration-tool']
publications = ['corda-jfx', 'corda-mock', 'corda-rpc', 'corda-core', 'corda', 'corda-finance', 'corda-node', 'corda-node-api', 'corda-test-common', 'corda-test-utils', 'corda-jackson', 'corda-verifier', 'corda-webserver-impl', 'corda-webserver', 'corda-node-driver', 'corda-confidential-identities', 'doorman', 'doorman-hsm', 'corda-shell', 'corda-serialization', 'corda-bridgeserver', 'corda-ptflows', 'jmeter-corda', 'migration-tool']
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'

View File

@ -15,7 +15,7 @@ apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory'
dependencies {
compile project(':core')
compile project(':serialization')
testCompile project(':test-utils')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

View File

@ -10,17 +10,18 @@
package net.corda.client.jackson
import com.fasterxml.jackson.annotation.*
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.*
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.convertValue
import net.corda.client.jackson.internal.CordaModule
import net.corda.client.jackson.internal.ToStringSerialize
import net.corda.client.jackson.internal.jsonObject
import net.corda.client.jackson.internal.readValueAs
import net.corda.core.CordaInternal
@ -30,23 +31,24 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.*
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.CertRole
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.*
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.parsePublicKeyBase58
import net.corda.core.utilities.toBase58String
import org.bouncycastle.asn1.x509.KeyPurposeId
import java.lang.reflect.Modifier
import java.math.BigDecimal
@ -103,31 +105,9 @@ object JacksonSupport {
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? = null
}
val cordaModule: Module by lazy {
SimpleModule("core").apply {
setMixInAnnotation(BigDecimal::class.java, BigDecimalMixin::class.java)
setMixInAnnotation(X500Principal::class.java, X500PrincipalMixin::class.java)
setMixInAnnotation(X509Certificate::class.java, X509CertificateMixin::class.java)
setMixInAnnotation(PartyAndCertificate::class.java, PartyAndCertificateSerializerMixin::class.java)
setMixInAnnotation(NetworkHostAndPort::class.java, NetworkHostAndPortMixin::class.java)
setMixInAnnotation(CordaX500Name::class.java, CordaX500NameMixin::class.java)
setMixInAnnotation(Amount::class.java, AmountMixin::class.java)
setMixInAnnotation(AbstractParty::class.java, AbstractPartyMixin::class.java)
setMixInAnnotation(AnonymousParty::class.java, AnonymousPartyMixin::class.java)
setMixInAnnotation(Party::class.java, PartyMixin::class.java)
setMixInAnnotation(PublicKey::class.java, PublicKeyMixin::class.java)
setMixInAnnotation(ByteSequence::class.java, ByteSequenceMixin::class.java)
setMixInAnnotation(SecureHash.SHA256::class.java, SecureHashSHA256Mixin::class.java)
setMixInAnnotation(SerializedBytes::class.java, SerializedBytesMixin::class.java)
setMixInAnnotation(DigitalSignature.WithKey::class.java, ByteSequenceWithPropertiesMixin::class.java)
setMixInAnnotation(DigitalSignatureWithCert::class.java, ByteSequenceWithPropertiesMixin::class.java)
setMixInAnnotation(TransactionSignature::class.java, ByteSequenceWithPropertiesMixin::class.java)
setMixInAnnotation(SignedTransaction::class.java, SignedTransactionMixin2::class.java)
setMixInAnnotation(WireTransaction::class.java, WireTransactionMixin::class.java)
setMixInAnnotation(CertPath::class.java, CertPathMixin::class.java)
setMixInAnnotation(NodeInfo::class.java, NodeInfoMixin::class.java)
}
}
@Suppress("unused")
@Deprecated("Do not use this as it's not thread safe. Instead get a ObjectMapper instance with one of the create*Mapper methods.")
val cordaModule: Module by lazy(::CordaModule)
/**
* Creates a Jackson ObjectMapper that uses RPC to deserialise parties from string names.
@ -182,15 +162,16 @@ object JacksonSupport {
registerModule(JavaTimeModule().apply {
addSerializer(Date::class.java, DateSerializer)
})
registerModule(cordaModule)
registerModule(CordaModule())
registerModule(KotlinModule())
addMixIn(BigDecimal::class.java, BigDecimalMixin::class.java)
addMixIn(X500Principal::class.java, X500PrincipalMixin::class.java)
addMixIn(X509Certificate::class.java, X509CertificateMixin::class.java)
addMixIn(CertPath::class.java, CertPathMixin::class.java)
}
}
@JacksonAnnotationsInside
@JsonSerialize(using = com.fasterxml.jackson.databind.ser.std.ToStringSerializer::class)
private annotation class ToStringSerialize
@ToStringSerialize
@JsonDeserialize(using = NumberDeserializers.BigDecimalDeserializer::class)
private interface BigDecimalMixin
@ -201,77 +182,6 @@ object JacksonSupport {
}
}
@ToStringSerialize
@JsonDeserialize(using = NetworkHostAndPortDeserializer::class)
private interface NetworkHostAndPortMixin
private class NetworkHostAndPortDeserializer : JsonDeserializer<NetworkHostAndPort>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): NetworkHostAndPort {
return NetworkHostAndPort.parse(parser.text)
}
}
@JsonSerialize(using = PartyAndCertificateSerializer::class)
// TODO Add deserialization which follows the same lookup logic as Party
private interface PartyAndCertificateSerializerMixin
private class PartyAndCertificateSerializer : JsonSerializer<PartyAndCertificate>() {
override fun serialize(value: PartyAndCertificate, gen: JsonGenerator, serializers: SerializerProvider) {
gen.jsonObject {
writeObjectField("name", value.name)
writeObjectField("owningKey", value.owningKey)
// TODO Add configurable option to output the certPath
}
}
}
@JsonSerialize(using = SignedTransactionSerializer::class)
@JsonDeserialize(using = SignedTransactionDeserializer::class)
private interface SignedTransactionMixin2
private class SignedTransactionSerializer : JsonSerializer<SignedTransaction>() {
override fun serialize(value: SignedTransaction, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeObject(SignedTransactionWrapper(value.txBits.bytes, value.sigs))
}
}
private class SignedTransactionDeserializer : JsonDeserializer<SignedTransaction>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): SignedTransaction {
val wrapper = parser.readValueAs<SignedTransactionWrapper>()
return SignedTransaction(SerializedBytes(wrapper.txBits), wrapper.signatures)
}
}
private class SignedTransactionWrapper(val txBits: ByteArray, val signatures: List<TransactionSignature>)
@JsonSerialize(using = SerializedBytesSerializer::class)
@JsonDeserialize(using = SerializedBytesDeserializer::class)
private class SerializedBytesMixin
private class SerializedBytesSerializer : JsonSerializer<SerializedBytes<*>>() {
override fun serialize(value: SerializedBytes<*>, gen: JsonGenerator, serializers: SerializerProvider) {
val deserialized = value.deserialize<Any>()
gen.jsonObject {
writeStringField("class", deserialized.javaClass.name)
writeObjectField("deserialized", deserialized)
}
}
}
private class SerializedBytesDeserializer : JsonDeserializer<SerializedBytes<*>>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): SerializedBytes<Any> {
return if (parser.currentToken == JsonToken.START_OBJECT) {
val mapper = parser.codec as ObjectMapper
val json = parser.readValueAsTree<ObjectNode>()
val clazz = context.findClass(json["class"].textValue())
val pojo = mapper.convertValue(json["deserialized"], clazz)
pojo.serialize()
} else {
SerializedBytes(parser.binaryValue)
}
}
}
@ToStringSerialize
private interface X500PrincipalMixin
@ -358,13 +268,6 @@ object JacksonSupport {
}
}
@JsonDeserialize(using = PartyDeserializer::class)
private interface AbstractPartyMixin
@JsonSerialize(using = AnonymousPartySerializer::class)
@JsonDeserialize(using = AnonymousPartyDeserializer::class)
private interface AnonymousPartyMixin
@Deprecated("This is an internal class, do not use")
object AnonymousPartySerializer : JsonSerializer<AnonymousParty>() {
override fun serialize(value: AnonymousParty, generator: JsonGenerator, provider: SerializerProvider) {
@ -379,9 +282,6 @@ object JacksonSupport {
}
}
@JsonSerialize(using = PartySerializer::class)
private interface PartyMixin
@Deprecated("This is an internal class, do not use")
object PartySerializer : JsonSerializer<Party>() {
override fun serialize(value: Party, generator: JsonGenerator, provider: SerializerProvider) {
@ -416,10 +316,6 @@ object JacksonSupport {
}
}
@ToStringSerialize
@JsonDeserialize(using = CordaX500NameDeserializer::class)
private interface CordaX500NameMixin
@Deprecated("This is an internal class, do not use")
object CordaX500NameDeserializer : JsonDeserializer<CordaX500Name>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): CordaX500Name {
@ -431,10 +327,6 @@ object JacksonSupport {
}
}
@JsonIgnoreProperties("legalIdentities") // This is already covered by legalIdentitiesAndCerts
@JsonDeserialize(using = NodeInfoDeserializer::class)
private interface NodeInfoMixin
@Deprecated("This is an internal class, do not use")
object NodeInfoDeserializer : JsonDeserializer<NodeInfo>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): NodeInfo {
@ -444,10 +336,6 @@ object JacksonSupport {
}
}
@ToStringSerialize
@JsonDeserialize(using = SecureHashDeserializer::class)
private interface SecureHashSHA256Mixin
@Deprecated("This is an internal class, do not use")
class SecureHashDeserializer<T : SecureHash> : JsonDeserializer<T>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): T {
@ -459,10 +347,6 @@ object JacksonSupport {
}
}
@JsonSerialize(using = PublicKeySerializer::class)
@JsonDeserialize(using = PublicKeyDeserializer::class)
private interface PublicKeyMixin
@Deprecated("This is an internal class, do not use")
object PublicKeySerializer : JsonSerializer<PublicKey>() {
override fun serialize(value: PublicKey, generator: JsonGenerator, provider: SerializerProvider) {
@ -481,10 +365,6 @@ object JacksonSupport {
}
}
@ToStringSerialize
@JsonDeserialize(using = AmountDeserializer::class)
private interface AmountMixin
@Deprecated("This is an internal class, do not use")
object AmountDeserializer : JsonDeserializer<Amount<*>>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): Amount<*> {
@ -499,22 +379,6 @@ object JacksonSupport {
private data class CurrencyAmountWrapper(val quantity: Long, val token: Currency)
@JsonDeserialize(using = OpaqueBytesDeserializer::class)
private interface ByteSequenceMixin {
@Suppress("unused")
@JsonValue
fun copyBytes(): ByteArray
}
@JsonIgnoreProperties("offset", "size")
@JsonSerialize
@JsonDeserialize
private interface ByteSequenceWithPropertiesMixin {
@Suppress("unused")
@JsonValue(false)
fun copyBytes(): ByteArray
}
@Deprecated("This is an internal class, do not use")
object OpaqueBytesDeserializer : JsonDeserializer<OpaqueBytes>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): OpaqueBytes {

View File

@ -0,0 +1,194 @@
@file:Suppress("DEPRECATION")
package net.corda.client.jackson.internal
import com.fasterxml.jackson.annotation.JsonValue
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier
import net.corda.client.jackson.JacksonSupport
import net.corda.core.contracts.Amount
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.*
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.*
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.constructorForDeserialization
import net.corda.serialization.internal.amqp.createSerializerFactoryFactory
import net.corda.serialization.internal.amqp.propertiesForSerialization
import java.security.PublicKey
class CordaModule : SimpleModule("corda-core") {
override fun setupModule(context: SetupContext) {
super.setupModule(context)
context.addBeanSerializerModifier(CordaSerializableBeanSerializerModifier())
context.setMixInAnnotations(PartyAndCertificate::class.java, PartyAndCertificateSerializerMixin::class.java)
context.setMixInAnnotations(NetworkHostAndPort::class.java, NetworkHostAndPortMixin::class.java)
context.setMixInAnnotations(CordaX500Name::class.java, CordaX500NameMixin::class.java)
context.setMixInAnnotations(Amount::class.java, AmountMixin::class.java)
context.setMixInAnnotations(AbstractParty::class.java, AbstractPartyMixin::class.java)
context.setMixInAnnotations(AnonymousParty::class.java, AnonymousPartyMixin::class.java)
context.setMixInAnnotations(Party::class.java, PartyMixin::class.java)
context.setMixInAnnotations(PublicKey::class.java, PublicKeyMixin::class.java)
context.setMixInAnnotations(ByteSequence::class.java, ByteSequenceMixin::class.java)
context.setMixInAnnotations(SecureHash.SHA256::class.java, SecureHashSHA256Mixin::class.java)
context.setMixInAnnotations(SerializedBytes::class.java, SerializedBytesMixin::class.java)
context.setMixInAnnotations(DigitalSignature.WithKey::class.java, ByteSequenceWithPropertiesMixin::class.java)
context.setMixInAnnotations(DigitalSignatureWithCert::class.java, ByteSequenceWithPropertiesMixin::class.java)
context.setMixInAnnotations(TransactionSignature::class.java, ByteSequenceWithPropertiesMixin::class.java)
context.setMixInAnnotations(SignedTransaction::class.java, SignedTransactionMixin2::class.java)
context.setMixInAnnotations(WireTransaction::class.java, JacksonSupport.WireTransactionMixin::class.java)
context.setMixInAnnotations(NodeInfo::class.java, NodeInfoMixin::class.java)
}
}
/**
* Use the same properties that AMQP serialization uses if the POJO is @CordaSerializable
*/
private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier() {
// We need a SerializerFactory when scanning for properties but don't actually use it so any will do
private val serializerFactory = SerializerFactory(AllWhitelist, Thread.currentThread().contextClassLoader)
override fun changeProperties(config: SerializationConfig,
beanDesc: BeanDescription,
beanProperties: MutableList<BeanPropertyWriter>): MutableList<BeanPropertyWriter> {
// TODO We're assuming here that Jackson gives us a superset of all the properties. Either confirm this or
// make sure the returned beanProperties are exactly the AMQP properties
if (beanDesc.beanClass.isAnnotationPresent(CordaSerializable::class.java)) {
val ctor = constructorForDeserialization(beanDesc.beanClass)
val amqpProperties = propertiesForSerialization(ctor, beanDesc.beanClass, serializerFactory).serializationOrder
beanProperties.removeIf { bean -> amqpProperties.none { amqp -> amqp.serializer.name == bean.name } }
}
return beanProperties
}
}
@ToStringSerialize
@JsonDeserialize(using = NetworkHostAndPortDeserializer::class)
private interface NetworkHostAndPortMixin
private class NetworkHostAndPortDeserializer : JsonDeserializer<NetworkHostAndPort>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext) = NetworkHostAndPort.parse(parser.text)
}
@JsonSerialize(using = PartyAndCertificateSerializer::class)
// TODO Add deserialization which follows the same lookup logic as Party
private interface PartyAndCertificateSerializerMixin
private class PartyAndCertificateSerializer : JsonSerializer<PartyAndCertificate>() {
override fun serialize(value: PartyAndCertificate, gen: JsonGenerator, serializers: SerializerProvider) {
gen.jsonObject {
writeObjectField("name", value.name)
writeObjectField("owningKey", value.owningKey)
// TODO Add configurable option to output the certPath
}
}
}
@JsonSerialize(using = SignedTransactionSerializer::class)
@JsonDeserialize(using = SignedTransactionDeserializer::class)
private interface SignedTransactionMixin2
private class SignedTransactionSerializer : JsonSerializer<SignedTransaction>() {
override fun serialize(value: SignedTransaction, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeObject(SignedTransactionWrapper(value.txBits.bytes, value.sigs))
}
}
private class SignedTransactionDeserializer : JsonDeserializer<SignedTransaction>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): SignedTransaction {
val wrapper = parser.readValueAs<SignedTransactionWrapper>()
return SignedTransaction(SerializedBytes(wrapper.txBits), wrapper.signatures)
}
}
private class SignedTransactionWrapper(val txBits: ByteArray, val signatures: List<TransactionSignature>)
@JsonSerialize(using = SerializedBytesSerializer::class)
@JsonDeserialize(using = SerializedBytesDeserializer::class)
private class SerializedBytesMixin
private class SerializedBytesSerializer : JsonSerializer<SerializedBytes<*>>() {
override fun serialize(value: SerializedBytes<*>, gen: JsonGenerator, serializers: SerializerProvider) {
val deserialized = value.deserialize<Any>()
gen.jsonObject {
writeStringField("class", deserialized.javaClass.name)
writeObjectField("deserialized", deserialized)
}
}
}
private class SerializedBytesDeserializer : JsonDeserializer<SerializedBytes<*>>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): SerializedBytes<Any> {
return if (parser.currentToken == JsonToken.START_OBJECT) {
val mapper = parser.codec as ObjectMapper
val json = parser.readValueAsTree<ObjectNode>()
val clazz = context.findClass(json["class"].textValue())
val pojo = mapper.convertValue(json["deserialized"], clazz)
pojo.serialize()
} else {
SerializedBytes(parser.binaryValue)
}
}
}
@JsonDeserialize(using = JacksonSupport.PartyDeserializer::class)
private interface AbstractPartyMixin
@JsonSerialize(using = JacksonSupport.AnonymousPartySerializer::class)
@JsonDeserialize(using = JacksonSupport.AnonymousPartyDeserializer::class)
private interface AnonymousPartyMixin
@JsonSerialize(using = JacksonSupport.PartySerializer::class)
private interface PartyMixin
@ToStringSerialize
@JsonDeserialize(using = JacksonSupport.CordaX500NameDeserializer::class)
private interface CordaX500NameMixin
@JsonDeserialize(using = JacksonSupport.NodeInfoDeserializer::class)
private interface NodeInfoMixin
@ToStringSerialize
@JsonDeserialize(using = JacksonSupport.SecureHashDeserializer::class)
private interface SecureHashSHA256Mixin
@JsonSerialize(using = JacksonSupport.PublicKeySerializer::class)
@JsonDeserialize(using = JacksonSupport.PublicKeyDeserializer::class)
private interface PublicKeyMixin
@ToStringSerialize
@JsonDeserialize(using = JacksonSupport.AmountDeserializer::class)
private interface AmountMixin
@JsonDeserialize(using = JacksonSupport.OpaqueBytesDeserializer::class)
private interface ByteSequenceMixin {
@Suppress("unused")
@JsonValue
fun copyBytes(): ByteArray
}
@JsonSerialize
@JsonDeserialize
private interface ByteSequenceWithPropertiesMixin {
@Suppress("unused")
@JsonValue(false)
fun copyBytes(): ByteArray
}

View File

@ -1,19 +1,14 @@
package net.corda.client.jackson.internal
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
import com.fasterxml.jackson.module.kotlin.convertValue
inline fun <reified T : Any> SimpleModule.addSerAndDeser(serializer: JsonSerializer<in T>, deserializer: JsonDeserializer<T>) {
addSerializer(T::class.java, serializer)
addDeserializer(T::class.java, deserializer)
}
inline fun JsonGenerator.jsonObject(fieldName: String? = null, gen: JsonGenerator.() -> Unit) {
fieldName?.let { writeFieldName(it) }
writeStartObject()
@ -24,3 +19,7 @@ inline fun JsonGenerator.jsonObject(fieldName: String? = null, gen: JsonGenerato
inline fun <reified T> JsonParser.readValueAs(): T = readValueAs(T::class.java)
inline fun <reified T : Any> JsonNode.valueAs(mapper: ObjectMapper): T = mapper.convertValue(this)
@JacksonAnnotationsInside
@JsonSerialize(using = ToStringSerializer::class)
annotation class ToStringSerialize

View File

@ -415,6 +415,15 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
testToStringSerialisation(X500Principal("CN=Common,L=London,O=Org,C=UK"))
}
@Test
fun `@CordaSerializable class which has non-c'tor properties`() {
val data = NonCtorPropertiesData(4434)
val json = mapper.valueToTree<ObjectNode>(data)
val (value) = json.assertHasOnlyFields("value")
assertThat(value.intValue()).isEqualTo(4434)
assertThat(mapper.convertValue<NonCtorPropertiesData>(json)).isEqualTo(data)
}
private fun makeDummyStx(): SignedTransaction {
val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1))
.toWireTransaction(services)
@ -442,6 +451,12 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
@CordaSerializable
private data class SubTestData(val value: Int)
@CordaSerializable
private data class NonCtorPropertiesData(val value: Int) {
@Suppress("unused")
val nonCtor: Int get() = value
}
private class TestPartyObjectMapper : JacksonSupport.PartyObjectMapper {
val identities = ArrayList<Party>()
val nodes = ArrayList<NodeInfo>()

View File

@ -80,7 +80,8 @@ class NodeMonitorModel {
// Only execute using "runLater()" if JavaFX been initialized.
// It may not be initialized in the unit test.
if(initialized.value.get()) {
// Also if we are already in the JavaFX thread - perform direct invocation without postponing it.
if(initialized.value.get() && !Platform.isFxApplicationThread()) {
Platform.runLater(op)
} else {
op()

View File

@ -10,10 +10,11 @@
package net.corda.client.rpc
import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.CordaRPCClientConfigurationImpl
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.core.context.Actor
import net.corda.core.context.Trace
import net.corda.core.messaging.CordaRPCOps
@ -22,7 +23,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.nodeapi.ArtemisTcpTransport.Companion.tcpTransport
import net.corda.nodeapi.ConnectionDirection
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
import java.time.Duration
/**

View File

@ -7,12 +7,12 @@ import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.nodeapi.internal.serialization.*
import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
import java.util.concurrent.ConcurrentHashMap
import net.corda.nodeapi.internal.serialization.amqp.custom.RxNotificationSerializer
import net.corda.serialization.internal.*
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.amqpMagic
import java.util.concurrent.ConcurrentHashMap
/**
* When set as the serialization scheme for a process, sets it to be the Corda AMQP implementation.

View File

@ -2,9 +2,8 @@ package net.corda.client.rpc.internal.serialization.amqp
import net.corda.core.concurrent.CordaFuture
import net.corda.core.toFuture
import net.corda.core.toObservable
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import rx.Observable
import java.io.NotSerializableException

View File

@ -5,7 +5,7 @@ import net.corda.client.rpc.internal.ObservableContext
import net.corda.core.context.Trace
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.serialization.amqp.*
import net.corda.serialization.internal.amqp.*
import org.apache.qpid.proton.codec.Data
import rx.Notification
import rx.Observable

View File

@ -31,7 +31,7 @@
<PatternLayout pattern="%msg%n" />
</Console>
<!-- Will generate up to 10 log files for a given day. During every rollover it will delete
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete
those that are older than 60 days, but keep the most recent 10 GB -->
<RollingFile name="RollingFile-Appender"
fileName="${sys:log-path}/${log-name}.log"
@ -44,7 +44,7 @@
<SizeBasedTriggeringPolicy size="10MB"/>
</Policies>
<DefaultRolloverStrategy min="1" max="10">
<DefaultRolloverStrategy min="1" max="100">
<Delete basePath="${archive}" maxDepth="1">
<IfFileName glob="${log-name}*.log.gz"/>
<IfLastModified age="60d">

View File

@ -62,7 +62,6 @@ class FinalityFlow(val transaction: SignedTransaction,
// Lookup the resolved transactions and use them to map each signed transaction to the list of participants.
// Then send to the notary if needed, record locally and distribute.
val parties = getPartiesToSend(verifyTx())
progressTracker.currentStep = NOTARISING
val notarised = notariseAndRecord()
// Each transaction has its own set of recipients, but extra recipients get them all.
@ -80,6 +79,7 @@ class FinalityFlow(val transaction: SignedTransaction,
@Suspendable
private fun notariseAndRecord(): SignedTransaction {
val notarised = if (needsNotarySignature(transaction)) {
progressTracker.currentStep = NOTARISING
val notarySignatures = subFlow(NotaryFlow.Client(transaction))
transaction + notarySignatures
} else {

View File

@ -59,6 +59,8 @@ object Emoji {
val CODE_DEVELOPER: String = codePointsString(0x1F469, 0x200D, 0x1F4BB)
@JvmStatic
val CODE_WARNING_SIGN: String = codePointsString(0x26A0, 0xFE0F)
@JvmStatic
val CROSS_MARK_BUTTON: String = codePointsString(0x274E)
/**
* When non-null, toString() methods are allowed to use emoji in the output as we're going to render them to a
@ -86,6 +88,7 @@ object Emoji {
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 ""
val notRun: String get() = if (emojiMode.get() != null) "$CROSS_MARK_BUTTON " else "-"
inline fun <T> renderIfSupported(body: () -> T): T {
if (hasEmojiTerminal)

View File

@ -158,8 +158,15 @@ class ProgressTracker(vararg steps: Step) {
val currentStepRecursive: Step
get() = getChildProgressTracker(currentStep)?.currentStepRecursive ?: currentStep
/** Returns the current step, descending into children to find the deepest started step we are up to. */
private val currentStartedStepRecursive: Step
get() {
val step = getChildProgressTracker(currentStep)?.currentStartedStepRecursive ?: currentStep
return if (step == UNSTARTED) currentStep else step
}
private fun currentStepRecursiveWithoutUnstarted(): Step {
val stepRecursive = getChildProgressTracker(currentStep)?.currentStepRecursive
val stepRecursive = getChildProgressTracker(currentStep)?.currentStartedStepRecursive
return if (stepRecursive == null || stepRecursive == UNSTARTED) currentStep else stepRecursive
}

View File

@ -12,12 +12,12 @@ package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.PublicKeySerializer
import net.corda.testing.core.DUMMY_BANK_A_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.TestIdentity

View File

@ -13,9 +13,9 @@ package net.corda.core.utilities
import com.esotericsoftware.kryo.KryoException
import net.corda.core.crypto.random63BitValue
import net.corda.core.serialization.*
import net.corda.nodeapi.internal.serialization.KRYO_CHECKPOINT_CONTEXT
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import net.corda.serialization.internal.KRYO_CHECKPOINT_CONTEXT
import net.corda.serialization.internal.SerializationContextImpl
import net.corda.serialization.internal.kryo.kryoMagic
import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
@ -34,8 +34,8 @@ class KotlinUtilsTest {
@Rule
val expectedEx: ExpectedException = ExpectedException.none()
val KRYO_CHECKPOINT_NOWHITELIST_CONTEXT = SerializationContextImpl(kryoMagic,
SerializationDefaults.javaClass.classLoader,
private val KRYO_CHECKPOINT_NOWHITELIST_CONTEXT = SerializationContextImpl(kryoMagic,
javaClass.classLoader,
EmptyWhitelist,
emptyMap(),
true,

View File

@ -141,6 +141,41 @@ class ProgressTrackerTest {
assertThat(stepsTreeNotification).isEmpty()
}
@Test
fun `steps tree index counts two levels of children steps`() {
pt.setChildProgressTracker(SimpleSteps.FOUR, pt2)
pt2.setChildProgressTracker(ChildSteps.SEA, pt3)
val allSteps = pt.allSteps
// Capture notifications.
val stepsIndexNotifications = LinkedList<Int>()
pt.stepsTreeIndexChanges.subscribe {
stepsIndexNotifications += it
}
val stepsTreeNotification = LinkedList<List<Pair<Int, String>>>()
pt.stepsTreeChanges.subscribe {
stepsTreeNotification += it
}
fun assertCurrentStepsTree(index: Int, step: ProgressTracker.Step) {
assertEquals(index, pt.stepsTreeIndex)
assertEquals(step, allSteps[pt.stepsTreeIndex].second)
}
pt.currentStep = SimpleSteps.ONE
assertCurrentStepsTree(0, SimpleSteps.ONE)
pt.currentStep = SimpleSteps.FOUR
assertCurrentStepsTree(3, SimpleSteps.FOUR)
pt2.currentStep = ChildSteps.SEA
assertCurrentStepsTree(6, ChildSteps.SEA)
// Assert no structure changes and proper steps propagation.
assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 3, 6))
assertThat(stepsTreeNotification).isEmpty()
}
@Test
fun `structure changes are pushed down when progress trackers are added`() {
pt.setChildProgressTracker(SimpleSteps.TWO, pt2)

View File

@ -50,7 +50,7 @@ be structured one parameter per line.
Code is vertically dense, blank lines in methods are used sparingly. This is so more code can fit on screen at once.
We use spaces and not tabs.
We use spaces and not tabs, with indents being 4 spaces wide.
1.2 Naming
----------

View File

@ -1,40 +0,0 @@
# Design Decision: Notary Backend - Galera or Permazen Raft
## Background / Context
We have evaluated Galera and Permazen as a possible replacement for Atomix CopyCat for the storage backend of our Notary
Service, more specificalyl the Uniqueness Provider.
## Options Analysis
### A. Galera Cluster
#### Advantages
1. Wider user base. In a survey of 478 OpenStack deployments, 32% decided to use Galera Cluster in production, see p. 47
of the [survey](https://www.openstack.org/assets/survey/April2017SurveyReport.pdf).
2. Very little additional work needed.
3. Entrerprise support.
#### Disadvantages
1. Harder to customize.
### B. Permazen Raft KV Database
#### Advantages
1. Customizable.
2. Slightly faster in our tests.
3. Simpler to deploy (embedded in the Corda node).
#### Disadvantages
1. Not ready out of the box, needs rate limiting, follower might run OOM during snapshot transfer.
2. No large community behind it.
## Recommendation and justification
Proceed with Option A

View File

@ -1,226 +0,0 @@
# HA Notary Service
## Overview
The distributed notary service tracks spent contract states and prevents double spending. For high-availability (HA),
the backing data store is replicated across a cluster of machines in different data centers. In this model, the cluster
is meant to be operated by a single party, and only crash faults are tolerated.
## Background
We have an existing HA notary service based on Atomix CopyCat, which an open source state machine replication library
that implemets the Raft consensus algorithm. However, it doesn't scale well with the number of spent input states, since
CopyCat takes periodic snapshots of the state machine and the snapshots have to fit in memory.
As an alternative, we propose using a more traditional MySQL database-based approach, using Galera Cluster, which
provides synchronous multi-master replication. Galera Cluster is based on a MySQL server with Write-Set replication
(wsrep) API, and the Galera Replication Plugin. Through the wsrep API Galera provides [certification-based replication](http://galeracluster.com/documentation-webpages/certificationbasedreplication.html). It works roughly as
follows:
1. A single database node executes a transaction optimistically until it reaches the commit point.
2. Changes made by the trasaction are collected into a write-set.
3. The write-set broadcasted to the cluster.
4. Every other node determines whether it can apply the write-set without conflicts.
5. In case of conflict, the initial node rolls back the transaction.
There are different Galera Cluster implementations, and we chose the Percona XtraDB cluster, as they were historically
more focused on performance than the competition.
### Decisions
- We are replacing the Atomix CopyCat Raft service.
- We are using a Percona cluster for Corda Connect.
- We keep investigating a more scalable solution, based on Permazen or a custom implementation.
- In the long term, we are interested in providing a BFT solution, perhaps leveraging SGX.
.. toctree::
decisions/decision.md
#### Advantages of Percona
- Production ready
- Works out of the box
- Backed by a company, enterprise and a community support are available
- Running stable at 30 tx/second (with 10 input states / tx), see figure below, in the section about the long running test
#### Disadvantages of Percona
- Performance deteriorates over time. This happens because Galera only works with the InnoDB storage engine, internally
backed by a B+ tree. Since we use state references as primary keys, table insterts results in random B+ tree inserts,
which doesn't scale well.
## Scope
### Goals
* We need a stable notary implementation.
* The implementation has to be easy to operate.
* We know that the switching costs to a more scalable solution are minimal.
* We take periodic backups of the consumed states and we test the recovery.
* We remain flexible and open to future requirements.
### Non-Goals
* For the time being, we don't need a solution that is shardable (for now, all replicas can hold all the state).
* We don't require a solution that can handle throughput beyond 15 tx/second.
* We don't design and implement a custom solution in the short term.
* We don't need rate limiting and fairness.
## Design
![Overview](overview.svg)
The HA notary service relies on the underlying MySQL uniqueness provider on top of a Percona XtraDB Cluster to prevent
double spending of input states. The exact data center locations are to be determined. Our notary service replicas
connect via JDBC to the replicated MySQL service.
Percona XtraDB Cluster is based on Percona Server and the Galera replication library that provides a multi master
cluster based on synchronous replication. The cluster is as good as its slowest node.
## Main Data Structure
The table below details the data base schema.
| Field name | Type | Description |
| --------------------- | ------------ | ---------------------------------------- |
| issue_tx_id | Binary(32) | The ID of the transaction that created the state |
| issue_tx_output_id | Int unsigned | Where in the transaction the state was created |
| consuming_tx_id | Binary(32) | The ID of the transaction that consumes the input state |
| consuming_tx_input_id | Int unsigned | Where in the transaction the state is consumed |
| consuming_party | Blob | Who is requesting the notarisation (~1 kByte) |
| commit_time | Timestamp | When this row is committed |
## Functional
The notary service relies on the MySQL uniqueness provider to prevent double spending. The MySQL database holds a single
table as described above. For HA, the data is synchronously replicated to several nodes by the Galera replication
plugin.
During notarisation, the uniqueness provider attempts to commit all input states of the Corda transaction in a single
database transaction. If at least one input state has been previously spent, the entire database transaction fails with
a batch exception. Unspent states can still be spent in a different later transaction. In case of double spend attempts,
the uniqueness provider queries the database for details where the conflicting states have been spent. The consuming
transaction ID, position of the input in the transaction and the requesting party are collected for all conflicting
inputs, wrapped in a uniqueness exception, thrown by the uniqueness provider. This exception is handled by the notary
service and turned into a notary exception.
We are using the Hikari connection pool to connect the notary services to all nodes of our Percona cluster. The
connection pool can be monitored via JMX.
### Deployment
We are planning to run a five node Percona cluster that can tolerate two simultaneous node failures. In case we need to
provide more storage or upgrade to better hardware we can take a single node down for maintenance and still tolerate one
unplanned failure.
#### Monitoring cluster membership changes
We setup a [notification command](http://galeracluster.com/documentation-webpages/notificationcmd.html) that gets called
whenever the node registers a change.
### Management
#### Disaster Recovery
Our disaster recovery strategy covers the following risks:
1. **Host Failure**. For the 5 node cluster we can tolerate 2 host failures without interrupting operation. This includes both machine and disk failures.
2. **DC Failure**. The cluster will be distributed across 3 data centers in a 2+2+1 configuration. A loss of one data center can be tolerated without interrupting service operation.
3. **Data Corruption/Loss**. In cases of data corruption or loss that is replicated across the cluster (for example, accidental data deletion or modification by an administrator) backups will be used to restore the cluster state. In this scenario service downtime will be incurred.
#### Backup and Recovery
Recovery Point Objective: 0
Recovery Time Objective: 1h
Any data loss incurred by the notary service will lead to a compromised ledger, since participants would be able to
double-spend already notarised states. Note that the backup & recovery procedure is only required for mitigating data
loss that gets replicated to the entire cluster.
This can be achieved by combining periodic backups of the entire database state, and the MySQL [binary
log](https://dev.mysql.com/doc/refman/5.7/en/binary-log.html). The binary log contains a log of all executed SQL
statements, which can be replayed onto a backup to restore the most up-to-date state. In case of an accidental statement
that removes data (e.g. DROP TABLE), the binary log can be replayed only up to the offending statement.
Scenarios where data corruption is caused by a malicious administrator selectively modifying or removing table rows are
out of scope.
See [Galera's backup documentation](http://galeracluster.com/documentation-webpages/backingupthecluster.html)
#### Monitoring
See the [Percona Management and Monitoring](https://www.percona.com/doc/percona-monitoring-and-management/index.html) documentation.
* Throughput in Tx / second
* Throughput in Input states / second
* Double spend attempts / time
* High level statistics, e.g. number of double spend attempts in the last 24 hours by two parties
* Double spend attempts per party
* Latency p50, p99
* Number of input states in DB
* Size of DB
* Replication Queues, see [monitoring Galera](http://galeracluster.com/documentation-webpages/monitoringthecluster.html)
#### Alerting
Alerts are triggered based on relevant metrics, like number of active members in the cluster and size of write queues of
individual nodes. We are configuring PMM to forward alerts to PagerDuty, where we do the routing to the operators who
are on call. We configure email alerting and slack integration as additional channels.
## Security
SSL encrypted links between the nodes of the Galera cluster and the notary service and the Galera cluster. See the [SSL
config documentation](http://galeracluster.com/documentation-webpages/sslconfig.html).
The managed disks on Azure [are encrypted](https://azure.microsoft.com/en-gb/blog/azure-managed-disks-sse/) with keys
managed by Microsoft. We have to trust our cloud provider anyways, so we don't do our own disk encryption.
## Testing the throughput of the uniqueness provider
We are using a custom load test flow that includes double spend attempts. The application metrics are forwarded to
Graphite and our Percona cluster is monitored by Percona's metrics and monitoring tool (PMM).
In our tests, the number of input states is Poisson-distributed with an average four input states per transaction. To
increase throughput in terms of notarised input states per second, we could batch transactions in the future. We tested
batching with batch sizes of up to 1000 input states per batch. And reached a throughput of 2k input states / second for
batch sizes 250-1000. When we detect a double spend attempt, we could send through individual transactions to find the
source of the conflict or bisect the batch.
## Long running test
![throughput](txs.png)
The figure above shows the throughput in transactions per second over four days, while writing to the cluster with up to
three clients. The dips occur while we take nodes off-line to simulate failure and to upgrade the disks. In the last
phase of the test all nodes were equipped with managed 1TB SSDs and and the cluster notarised at more than 300 input
states per second while holding more than 100 M input states in the DB.
Glitches in throughput can occur when the write queue of a node is filling up. I'm assuming this is due to increased
disk latency when the cloud SAN disk is busy with other operations. When the maximum write queue size is reached, the
slow node isn't accepting writes any more and sends out flow control messages to its peers to stop replicating (I'm
assuming this leads to messages being queued in their send queue). The queue sizes are monitored by the PMM tool and we
can setup alerts based on a configured maximum write queue size or when we see "flow control messages".
We found that managed SSDs of 1TB in size performed better than a RAID 10 array of four 128GB SSDs. The latency of the
1TB SSDs was stable around 8ms, while we have observed latency spikes up to 64ms on the smaller SSDs. The disk load on
the slowest node in terms of disk latency was around 6-8 outstanding writes during the last phase of the test. Setting
up a RAID 10 was a mistake, for best performance we should have used a RAID 0 configuration, since the Azure disks are
replicated.
![disk load](diskload.png)
### Recommended Cloud Server Configuration
We recommend `Standard DS13 v2 (8 cores, 56 GB memory)` servers with 1 TB managed SSD disks attached. To make the setup
more cost effective, we can run on more affordable cloud instances, when we have lower demands in terms of throughput.
The optimum is yet to be found. It is possible to upgrade or downgrade the nodes of the cluster, one node at a time.
Be prepared to kill and replace the slowest node of the cluster, especially in the cloud, since the Galera cluster will
not perform better than the slowest node. The same goes for SAN disks. If you are unlucky and your disk has high
latency, try replacing it with a new one. Maybe your get better performance with your new disk.
### Disk upgrade using LVM
We recommend using LVM in production for convenience and flexibility. During our long running test we performed a hot
disk upgrade using LVM.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

View File

@ -1,30 +0,0 @@
graph G {
concentrate=true;
graph [dpi=100, fontname="helvetica" ];
node [fontname="helvetica"];
edge [fontname="helvetica"];
subgraph cluster0 {
label="Galera Cluster";
color="lightgrey";
GaleraNode1 [label="Node 1\nSouth UK"];
GaleraNode2 [label="Node 2\nIreland"];
GaleraNode3 [label="Node 3\nNetherlands"];
GaleraNode1 -- GaleraNode2;
GaleraNode2 -- GaleraNode3;
GaleraNode1 -- GaleraNode3;
}
subgraph cluster1 {
label="Corda";
color="lightgrey";
NotaryNode [label="Notary Node\nJDBC Uniqueness Provider"];
}
NotaryNode -- GaleraNode1;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 KiB

View File

@ -64,7 +64,6 @@ We look forward to seeing what you can do with Corda!
design/hadr/design.md
design/kafka-notary/design.md
design/monitoring-management/design.md
design/notary-service-ha/design.md
.. toctree::
:caption: Participate

View File

@ -4,8 +4,8 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.EncodingWhitelist
import net.corda.core.serialization.SerializationEncoding
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.*
import net.corda.serialization.internal.SerializationFactoryImpl
import net.corda.serialization.internal.amqp.*
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.amqp.DescribedType
import org.apache.qpid.proton.amqp.Symbol

View File

@ -1,10 +1,10 @@
package net.corda.blobinspector
import net.corda.core.serialization.SerializedBytes
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
import org.junit.Test

View File

@ -0,0 +1,121 @@
# Introduction
This project holds different Corda-related utility code.
## Utils
Utils.kt contains various extension functions and other short utility code that aid
development on Corda. The code is mostly self-explanatory -- the only exception may
be `StateRefHere` which can be used in situations where multiple states are produced
in one transaction, and one state needs to refer to the others, e.g. something like this:
```
val tx = TransactionBuilder(//...
// ...
tx.addOutputState(innerState, contractClassName)
val innerStateRef = StateRefHere(null, tx.outputStates().count() - 1)
tx.addOutputState(OuterState(innerStateRef = innerStateRef), contractClassName)
// ...
```
## StatusTransitions
StatusTransitions.kt contains utility code related to FSM-style defining possible transactions that can happen
with the respect to the contained status and roles of participants. Here's a simple example for illustration.
We are going to track package delivery status, so we first define all roles of participants and possible statuses
each package could have:
```
enum class PackageDeliveryRole {
Sender,
Receiver,
Courier
}
enum class DeliveryStatus {
InTransit,
Delivered,
Returned
}
```
The information about each package is held in PackageState: it contains its involved parties, status, linearId,
current location, and information related to delivery attempts:
```
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import java.time.Instant
data class PackageState(val sender: Party,
val receiver: Party,
val deliveryCompany: Party,
val currentLocation: String,
override val status: DeliveryStatus,
val deliveryAttempts: Int = 0,
val lastDeliveryAttempt: Instant? = null,
override val linearId: UniqueIdentifier): LinearState, StatusTrackingContractState<DeliveryStatus, PackageDeliveryRole> {
override fun roleToParty(role: PackageDeliveryRole): Party {
return when (role) {
PackageDeliveryRole.Sender -> sender
PackageDeliveryRole.Receiver -> receiver
PackageDeliveryRole.Courier -> deliveryCompany
}
}
override val participants: List<AbstractParty> = listOf(sender, receiver, deliveryCompany)
}
```
We can then define operations one can do with this state, who can do them and under what circumstances (i.e. from what status):
```
sealed class DeliveryCommand: CommandData {
object Send: DeliveryCommand()
object Transport: DeliveryCommand()
object ConfirmReceipt: DeliveryCommand()
object AttemptedDelivery: DeliveryCommand()
object Return: DeliveryCommand()
}
class PackageDelivery: Contract {
companion object {
val transitions = StatusTransitions(PackageState::class,
DeliveryCommand.Send.txDef(PackageDeliveryRole.Sender, null, listOf(DeliveryStatus.InTransit)),
DeliveryCommand.Transport.txDef(PackageDeliveryRole.Courier, DeliveryStatus.InTransit, listOf(DeliveryStatus.InTransit)),
DeliveryCommand.AttemptedDelivery.txDef(PackageDeliveryRole.Courier, DeliveryStatus.InTransit, listOf(DeliveryStatus.InTransit)),
DeliveryCommand.ConfirmReceipt.txDef(PackageDeliveryRole.Receiver, DeliveryStatus.InTransit, listOf(DeliveryStatus.Delivered)),
DeliveryCommand.Return.txDef(PackageDeliveryRole.Courier, DeliveryStatus.InTransit, listOf(DeliveryStatus.Returned)))
}
override fun verify(tx: LedgerTransaction) {
transitions.verify(tx)
// ...
// other checks -- linearId is preserved, attributes are updated correctly for given commands, return is only allowed after 3 attempts, etc.
}
}
```
This definition gives us some basic generic verification -- e.g. that package receipt confirmations need to be signed by package receivers.
In addition that, we could visualize the defined transitions in a PUML diagram:
```
PackageDelivery.transitions.printGraph().printedPUML
```
Which will result in:
```
@startuml
title PackageState
[*] --> InTransit : Send (by Sender)
InTransit --> InTransit : Transport (by Courier)
InTransit --> InTransit : AttemptedDelivery (by Courier)
InTransit --> Delivered : ConfirmReceipt (by Receiver)
InTransit --> Returned : Return (by Courier)
@enduml
```
![Generated PlantUML model](http://www.plantuml.com:80/plantuml/png/VSsn2i8m58NXlK-HKOM-W8DKwk8chPiunEOemIGDjoU5lhqIHP12jn_k-RZLG2rCtXMqT50dtJtr0oqrKLmsLrMMEtKCPz5Xi5HRrI8OjRfDEI3hudUSJNF5NfZtTP_4BeCz2Hy9Su2p8sHQWjyDp1lMVRXRyGqwsCYiSezpre19GbQV_FzH8PZatGi0)
## Future plans
Depending on particular use cases, this utility library may be enhanced in different ways. Here are a few ideas:
* More generic verification (e.g. verifying numbers of produced and consumed states of a particular type)
* More convenient syntax, not abusing nulls so much, etc.
* ...

View File

@ -0,0 +1,27 @@
apply plugin: 'kotlin'
apply plugin: 'idea'
sourceSets {
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile project(':core')
compile project(':node-api')
testCompile project(':test-utils')
testCompile project(':node-driver')
testCompile "junit:junit:$junit_version"
}

View File

@ -0,0 +1,105 @@
package io.cryptoblk.core
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import kotlin.reflect.KClass
/**
* Contract state that records changes of some [status] on the ledger and roles of parties that are participants
* in that state using [roleToParty].
*/
interface StatusTrackingContractState<out S, in R> : ContractState {
val status: S
fun roleToParty(role: R): Party
}
/**
* Definition of finite state transition: for a particular command in a TX, it defines what transitions can be done
* [from] what status [to] what statuses, and who needs to sign them ([signer]).
* If [from] is null, it means there doesn't need to be any input; if [to] is null, it mean there doesn't need to be any output.
* If [signer] is null, it means anyone can sign it.
*/
data class TransitionDef<out S, out R>(val cmd: Class<*>, val signer: R?, val from: S?, val to: List<S?>)
/**
* Holds visualized PUML graph in [printedPUML] and the relevant state class name in [stateClassName].
*/
data class PrintedTransitionGraph(val stateClassName: String, val printedPUML: String)
/**
* Shorthand for defining transitions directly from the command class
*/
fun <S, R> CommandData.txDef(signer: R? = null, from: S?, to: List<S?>):
TransitionDef<S, R> = TransitionDef(this::class.java, signer, from, to)
/**
* For a given [stateClass] that tracks a status, it holds all possible transitions in [ts].
* This can be used for generic [verify] in contract code as well as for visualizing the state transition graph in PUML ([printGraph]).
*/
class StatusTransitions<out S, in R, T : StatusTrackingContractState<S, R>>(private val stateClass: KClass<T>,
private vararg val ts: TransitionDef<S, R>) {
private val allowedCmds = ts.map { it.cmd }.toSet()
private fun matchingTransitions(input: S?, output: S?, command: CommandData): List<TransitionDef<S, R>> {
val options = ts.filter {
(it.from == input) && (output in it.to) && (it.cmd == command.javaClass)
}
if (options.isEmpty()) throw IllegalStateException("Transition [$input -(${command.javaClass.simpleName})-> $output] not allowed")
return options
}
/**
* Generic verification based on provided [TransitionDef]s
*/
fun verify(tx: LedgerTransaction) {
val relevantCmds = tx.commands.filter { allowedCmds.contains(it.value.javaClass) }
require(relevantCmds.isNotEmpty()) { "Transaction must have at least one Command relevant to its defined transitions" }
relevantCmds.forEach { cmd ->
val ins = tx.inputsOfType(stateClass.java)
val inputStates = if (ins.isEmpty()) listOf(null) else ins
val outs = tx.outputsOfType(stateClass.java)
val outputStates = if (outs.isEmpty()) listOf(null) else outs
// for each combination of in x out which should normally be at most 1...
inputStates.forEach { inp ->
outputStates.forEach { outp ->
assert((inp != null) || (outp != null))
val options = matchingTransitions(inp?.status, outp?.status, cmd.value)
val signerGroup = options.groupBy { it.signer }.entries.singleOrNull()
?: throw IllegalStateException("Cannot have different signers in StatusTransitions for the same command.")
val signer = signerGroup.key
if (signer != null) {
// which state determines who is the signer? by default the input, unless it's the initial transition
val state = (inp ?: outp)!!
val signerParty = state.roleToParty(signer)
if (!cmd.signers.contains(signerParty.owningKey))
throw IllegalStateException("Command ${cmd.value.javaClass} must be signed by $signer")
}
}
}
}
}
fun printGraph(): PrintedTransitionGraph {
val sb = StringBuilder()
sb.append("@startuml\n")
if (stateClass.simpleName != null) sb.append("title ${stateClass.simpleName}\n")
ts.forEach { txDef ->
val fromStatus = txDef.from?.toString() ?: "[*]"
txDef.to.forEach { to ->
val toStatus = (to ?: "[*]").toString()
val cmd = txDef.cmd.simpleName
val signer = txDef.signer?.toString() ?: "anyone involved"
sb.append("$fromStatus --> $toStatus : $cmd (by $signer)\n")
}
}
sb.append("@enduml")
return PrintedTransitionGraph(stateClass.simpleName ?: "", sb.toString())
}
}

View File

@ -0,0 +1,75 @@
package io.cryptoblk.core
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowLogic
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
inline fun <reified T : ContractState> ServiceHub.queryStateByRef(ref: StateRef): StateAndRef<T> {
val results = vaultService.queryBy<T>(QueryCriteria.VaultQueryCriteria(stateRefs = kotlin.collections.listOf(ref)))
return results.states.firstOrNull() ?: throw IllegalArgumentException("State (type=${T::class}) corresponding to the reference $ref not found (or is spent).")
}
/**
* Shorthand when a single party signs a TX and then returns a result that uses the signed TX (e.g. includes the TX id)
*/
@Suspendable
fun <R> FlowLogic<R>.finalize(tx: TransactionBuilder, returnWithSignedTx: (stx: SignedTransaction) -> R): R {
val stx = serviceHub.signInitialTransaction(tx)
subFlow(FinalityFlow(stx)) // it'll send to all participants in the state by default
return returnWithSignedTx(stx)
}
/**
* Corda fails when it tries to store the same attachment hash twice. And it's convenient to also do nothing if no attachment is provided.
* This doesn't fix the same-attachment problem completely but should at least help in testing with the same file.
*/
fun TransactionBuilder.addAttachmentOnce(att: SecureHash?): TransactionBuilder {
if (att == null) return this
if (att !in this.attachments())
this.addAttachment(att)
return this
}
// checks the instance type, so the cast is safe
@Suppress("UNCHECKED_CAST")
inline fun <reified T : ContractState> List<StateAndRef<ContractState>>.entriesOfType(): List<StateAndRef<T>> = this.mapNotNull {
if (T::class.java.isInstance(it.state.data)) it as StateAndRef<T> else null
}
/**
* Used when multiple objects may be created in the same transaction and need to refer to each other. If a state
* contains this object as a reference to another object and txhash is null, the same txhash as of the containing/outer state
* should be used. If txhash is not null, then this works exactly like StateRef.
*
* WARNING:
* - if the outer state gets updated but its referenced state does not (in the same tx) then
* - this reference in parent state must be updated with the real txhash: [StateRefHere.copyWith]
* - otherwise it will be unresolvable (could be solved by disallowing copy on this)
*/
// do not make it a data class
@CordaSerializable
class StateRefHere(val txhash: SecureHash?, val index: Int) {
constructor(ref: StateRef) : this(ref.txhash, ref.index)
fun toStateRef(parent: SecureHash) = StateRef(txhash ?: parent, index)
// not standard copy
fun copyWith(parent: SecureHash): StateRefHere {
return StateRefHere(txhash ?: parent, index)
}
override fun equals(other: Any?): Boolean {
if (other !is StateRefHere) return false
return (this.txhash == other.txhash) && (this.index == other.index)
}
}

View File

@ -0,0 +1,301 @@
package io.cryptoblk.core
import net.corda.core.contracts.Contract
import net.corda.core.contracts.TypeOnlyCommandData
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.transactions.LedgerTransaction
import net.corda.testing.core.TestIdentity
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.junit.Test
private val ALICE_ID = TestIdentity(CordaX500Name.parse("L=London,O=Alice Ltd,OU=Trade,C=GB"))
private val BOB_ID = TestIdentity(CordaX500Name.parse("L=London,O=Bob Ltd,OU=Trade,C=GB"))
private val BIGCORP_ID = TestIdentity(CordaX500Name.parse("L=New York,O=Bigcorp Ltd,OU=Trade,C=US"))
private val ALICE = ALICE_ID.party
private val BOB = BOB_ID.party
private val BIG_CORP = BIGCORP_ID.party
private val ALICE_PUBKEY = ALICE_ID.publicKey
private val BOB_PUBKEY = BOB_ID.publicKey
private val BIG_CORP_PUBKEY = BIGCORP_ID.publicKey
private enum class PartyRole {
Adder,
MultiplierAndRandomiser,
Randomiser
}
private class IntegerTestState(override val status: String) : StatusTrackingContractState<String, PartyRole> {
override fun roleToParty(role: PartyRole): Party {
return when (role) {
PartyRole.Adder -> BIG_CORP
PartyRole.MultiplierAndRandomiser -> BOB
PartyRole.Randomiser -> ALICE
}
}
override val participants: List<AbstractParty>
get() = listOf(ALICE, BOB, BIG_CORP)
}
private sealed class Operations: TypeOnlyCommandData() {
object Add1 : Operations()
object Add10 : Operations()
object Multiply2 : Operations()
object Randomise : Operations()
object Close : Operations()
object AnotherCommand : Operations()
}
class TestIntegerContract: Contract {
companion object {
private val fsTransitions = StatusTransitions(IntegerTestState::class,
Operations.Add1.txDef(PartyRole.Adder, null, listOf("1")),
Operations.Add1.txDef(PartyRole.Adder, "1", listOf("2")),
Operations.Add10.txDef(PartyRole.Adder, "1", listOf("11")),
Operations.Multiply2.txDef(PartyRole.MultiplierAndRandomiser, "2", listOf("4")),
Operations.Multiply2.txDef(PartyRole.MultiplierAndRandomiser, "11", listOf("22")),
Operations.Randomise.txDef(PartyRole.Randomiser, "2", listOf("8", "9", "1", "11")),
Operations.Randomise.txDef(PartyRole.Randomiser, "11", listOf("2", "11", "4")),
Operations.Randomise.txDef(PartyRole.MultiplierAndRandomiser, "11", listOf("22")),
Operations.Close.txDef(PartyRole.Randomiser, "9", listOf(null))
)
}
override fun verify(tx: LedgerTransaction) {
fsTransitions.verify(tx)
}
}
private class TestOwnedIntegerState(override val status: String): StatusTrackingContractState<String, PartyRole> {
override val participants: List<AbstractParty>
get() = listOf(ALICE, BOB)
override fun roleToParty(role: PartyRole): Party {
return if (status == "0") ALICE else BOB
}
}
class TestOwnedIntegerContract: Contract {
companion object {
private val fsTransitions = StatusTransitions(TestOwnedIntegerState::class,
Operations.Add1.txDef(PartyRole.Adder, null, listOf("0")),
Operations.Add1.txDef(PartyRole.Adder, "0", listOf("1")),
Operations.Add1.txDef(PartyRole.Adder, "1", listOf("2")),
Operations.Multiply2.txDef(PartyRole.MultiplierAndRandomiser, "10", listOf("20")),
Operations.Multiply2.txDef(PartyRole.Adder, "10", listOf("20")) // bug for the test
)
}
override fun verify(tx: LedgerTransaction) {
fsTransitions.verify(tx)
}
}
class StatusTransitionsTest {
companion object {
private val integerContract = TestIntegerContract::class.qualifiedName!!
private val ownedIntegerContract = TestOwnedIntegerContract::class.qualifiedName!!
private val ledgerServices = MockServices(ALICE_ID, BOB_ID, BIGCORP_ID)
}
@Test
fun `basic correct cases`() {
ledgerServices.ledger {
transaction {
output(integerContract, IntegerTestState("1"))
command(BIG_CORP_PUBKEY, Operations.Add1)
verifies()
}
transaction {
input(integerContract, IntegerTestState("1"))
output(integerContract, IntegerTestState("2"))
command(BIG_CORP_PUBKEY, Operations.Add1)
verifies()
}
transaction {
input(integerContract, IntegerTestState("2"))
output(integerContract, IntegerTestState("9"))
command(ALICE_PUBKEY, Operations.Randomise)
verifies()
}
transaction {
input(integerContract, IntegerTestState("9"))
command(ALICE_PUBKEY, Operations.Close)
verifies()
}
}
}
@Test
fun `disallowed output`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("1"))
output(integerContract, IntegerTestState("3"))
command(BIG_CORP_PUBKEY, Operations.Add1)
fails()
}
}
}
@Test
fun `disallowed command`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("1"))
output(integerContract, IntegerTestState("2"))
command(BIG_CORP_PUBKEY, Operations.Multiply2)
fails()
}
}
}
@Test
fun `disallowed signer`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("1"))
output(integerContract, IntegerTestState("2"))
command(ALICE_PUBKEY, Operations.Add1)
fails()
}
}
}
@Test
fun `irrelevant commands fail`() {
ledgerServices.ledger {
transaction {
output(integerContract, IntegerTestState("8"))
command(ALICE_PUBKEY, Operations.AnotherCommand)
failsWith("at least one Command relevant")
}
}
}
@Test
fun `multiple relevant commands accepted`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("11"))
output(integerContract, IntegerTestState("22"))
command(BOB_PUBKEY, Operations.Randomise)
command(BOB_PUBKEY, Operations.Multiply2)
verifies()
}
}
}
@Test
fun `multiple relevant commands failed`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("2"))
output(integerContract, IntegerTestState("4"))
command(BOB_PUBKEY, Operations.Randomise)
command(BOB_PUBKEY, Operations.Multiply2)
fails()
}
}
}
@Test
fun `multiple inputs failed`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("1"))
input(integerContract, IntegerTestState("2"))
output(integerContract, IntegerTestState("11"))
command(BIG_CORP_PUBKEY, Operations.Add10)
fails()
}
}
}
@Test
fun `multiple outputs failed`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("1"))
output(integerContract, IntegerTestState("2"))
output(integerContract, IntegerTestState("11"))
command(BIG_CORP_PUBKEY, Operations.Add10)
fails()
}
}
}
@Test
fun `role change signer correct`() {
ledgerServices.ledger {
transaction {
output(ownedIntegerContract, TestOwnedIntegerState("0"))
command(ALICE_PUBKEY, Operations.Add1)
verifies()
}
transaction {
input(ownedIntegerContract, TestOwnedIntegerState("0"))
output(ownedIntegerContract, TestOwnedIntegerState("1"))
command(ALICE_PUBKEY, Operations.Add1)
verifies()
}
transaction {
input(ownedIntegerContract, TestOwnedIntegerState("1"))
output(ownedIntegerContract, TestOwnedIntegerState("2"))
command(ALICE_PUBKEY, Operations.Add1)
fails()
}
transaction {
input(ownedIntegerContract, TestOwnedIntegerState("1"))
output(ownedIntegerContract, TestOwnedIntegerState("2"))
command(BOB_PUBKEY, Operations.Add1)
verifies()
}
}
}
@Test
fun `multiple signers disallowed`() {
ledgerServices.ledger {
transaction {
input(ownedIntegerContract, TestOwnedIntegerState("10"))
output(ownedIntegerContract, TestOwnedIntegerState("20"))
command(ALICE_PUBKEY, Operations.Multiply2)
failsWith("Cannot have different signers")
}
}
}
@Test
fun `spend disallowed`() {
ledgerServices.ledger {
transaction {
input(integerContract, IntegerTestState("1"))
command(ALICE_PUBKEY, Operations.Close)
fails()
}
}
}
}

View File

@ -162,7 +162,7 @@ abstract class OnLedgerAsset<T : Any, out C : CommandData, S : FungibleAsset<T>>
delta > 0 -> {
// The states from the current issuer more than covers this payment.
outputStates += deriveState(templateState, Amount(remainingToPay, token), party)
remainingFromEachIssuer[0] = Pair(token, Amount(delta, token))
remainingFromEachIssuer[remainingFromEachIssuer.lastIndex] = Pair(token, Amount(delta, token))
remainingToPay = 0
}
delta == 0L -> {

View File

@ -24,6 +24,7 @@ import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Test
import java.util.Collections.nCopies
import kotlin.test.assertNotNull
class CashSelectionH2ImplTest {
private val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", "net.corda.core.schemas", "net.corda.finance.sampleschemas"))
@ -63,4 +64,19 @@ class CashSelectionH2ImplTest {
assertThatThrownBy { flow2.getOrThrow() }.isInstanceOf(CashException::class.java)
assertThatThrownBy { flow3.getOrThrow() }.isInstanceOf(CashException::class.java)
}
@Test
fun `select pennies amount from cash states with more than two different issuers and expect change`() {
val node = mockNet.createNode()
val notary = mockNet.defaultNotaryIdentity
// Issue some cash
node.startFlow(CashIssueFlow(1.POUNDS, OpaqueBytes.of(1), notary)).getOrThrow()
node.startFlow(CashIssueFlow(1.POUNDS, OpaqueBytes.of(2), notary)).getOrThrow()
node.startFlow(CashIssueFlow(1000.POUNDS, OpaqueBytes.of(3), notary)).getOrThrow()
// Make a payment
val paymentResult = node.startFlow(CashPaymentFlow(999.POUNDS, node.info.legalIdentities[0], false)).getOrThrow()
assertNotNull(paymentResult.recipient)
}
}

View File

@ -19,10 +19,10 @@ description 'Corda node API'
dependencies {
compile project(":core")
compile project(":serialization") // TODO Remove this once the NetworkBootstrapper class is moved into the tools:bootstrapper module
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them
compile "org.apache.commons:commons-collections4:${commons_collections_version}"
@ -41,12 +41,6 @@ dependencies {
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:$typesafe_config_version"
// Kryo: object graph serialization.
compile "com.esotericsoftware:kryo:4.0.0"
compile "de.javakaffee:kryo-serializers:0.41"
compile "org.ow2.asm:asm:$asm_version"
// For AMQP serialisation.
compile "org.apache.qpid:proton-j:$protonj_version"
// SQL connection pooling library
@ -61,12 +55,9 @@ dependencies {
shadow "org.apache.curator:curator-recipes:${curator_version}"
testCompile "org.apache.curator:curator-test:${curator_version}"
// FastClasspathScanner: classpath scanning - needed for the NetworkBootstrapper and AMQP.
// FastClasspathScanner: classpath scanning - needed for the NetworkBootstrapper.
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
// Pure-Java Snappy compression
compile "org.iq80.snappy:snappy:$snappy_version"
compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
// For caches rather than guava
@ -75,6 +66,7 @@ dependencies {
// Unit testing helpers.
testCompile "junit:junit:$junit_version"
testCompile "org.assertj:assertj-core:$assertj_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile project(':node-driver')
compile ("org.apache.activemq:artemis-amqp-protocol:${artemis_version}") {

View File

@ -10,9 +10,11 @@
package net.corda.nodeapi.internal.network
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import net.corda.cordform.CordformNode
import net.corda.core.contracts.ContractClassName
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.*
import net.corda.core.internal.concurrent.fork
@ -28,18 +30,15 @@ import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.utilities.days
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.ContractsJar
import net.corda.nodeapi.internal.ContractsJarFile
import net.corda.nodeapi.internal.DEV_ROOT_CA
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.*
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier.Companion.NODE_INFO_FILE_NAME_PREFIX
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
import net.corda.serialization.internal.CordaSerializationMagic
import net.corda.serialization.internal.SerializationFactoryImpl
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.amqpMagic
import net.corda.serialization.internal.kryo.AbstractKryoSerializationScheme
import net.corda.serialization.internal.kryo.kryoMagic
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
@ -47,6 +46,10 @@ import java.time.Instant
import java.util.concurrent.Executors
import java.util.concurrent.TimeoutException
import kotlin.streams.toList
import kotlin.collections.HashSet
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
/**
* Class to bootstrap a local network of Corda nodes on the same filesystem.
@ -72,6 +75,43 @@ class NetworkBootstrapper {
}
}
sealed class NotaryCluster {
data class BFT(val name: CordaX500Name) : NotaryCluster()
data class CFT(val name: CordaX500Name) : NotaryCluster()
}
data class DirectoryAndConfig(val directory: Path, val config: Config)
private fun notaryClusters(configs: Map<Path, Config>): Map<NotaryCluster, List<Path>> {
val clusteredNotaries = configs.flatMap { (path, config) ->
if (config.hasPath("notary.serviceLegalName")) {
listOf(CordaX500Name.parse(config.getString("notary.serviceLegalName")) to DirectoryAndConfig(path, config))
} else {
emptyList()
}
}
return clusteredNotaries.groupBy { it.first }.map { (k, vs) ->
val cs = vs.map { it.second.config }
if (cs.any { it.hasPath("notary.bftSMaRt") }) {
require(cs.all { it.hasPath("notary.bftSMaRt") }) { "Mix of BFT and non-BFT notaries with service name $k" }
NotaryCluster.BFT(k) to vs.map { it.second.directory }
} else {
NotaryCluster.CFT(k) to vs.map { it.second.directory }
}
}.toMap()
}
private fun generateServiceIdentitiesForNotaryClusters(configs: Map<Path, Config>) {
notaryClusters(configs).forEach { (cluster, directories) ->
when (cluster) {
is NotaryCluster.BFT ->
DevIdentityGenerator.generateDistributedNotaryCompositeIdentity(directories, cluster.name, threshold = 1 + 2 * directories.size / 3)
is NotaryCluster.CFT ->
DevIdentityGenerator.generateDistributedNotarySingularIdentity(directories, cluster.name)
}
}
}
fun bootstrap(directory: Path, cordappJars: List<Path>) {
directory.createDirectories()
println("Bootstrapping local network in $directory")
@ -79,18 +119,22 @@ class NetworkBootstrapper {
val nodeDirs = directory.list { paths -> paths.filter { (it / "corda.jar").exists() }.toList() }
require(nodeDirs.isNotEmpty()) { "No nodes found" }
println("Nodes found in the following sub-directories: ${nodeDirs.map { it.fileName }}")
val configs = nodeDirs.associateBy({ it }, { ConfigFactory.parseFile((it / "node.conf").toFile()) })
generateServiceIdentitiesForNotaryClusters(configs)
val processes = startNodeInfoGeneration(nodeDirs)
initialiseSerialization()
try {
println("Waiting for all nodes to generate their node-info files...")
val nodeInfoFiles = gatherNodeInfoFiles(processes, nodeDirs)
println("Checking for duplicate nodes")
checkForDuplicateLegalNames(nodeInfoFiles)
println("Distributing all node-info files to all nodes")
distributeNodeInfos(nodeDirs, nodeInfoFiles)
print("Loading existing network parameters... ")
val existingNetParams = loadNetworkParameters(nodeDirs)
println(existingNetParams ?: "none found")
println("Gathering notary identities")
val notaryInfos = gatherNotaryInfos(nodeInfoFiles)
val notaryInfos = gatherNotaryInfos(nodeInfoFiles, configs)
println("Generating contract implementations whitelist")
val newWhitelist = generateWhitelist(existingNetParams, readExcludeWhitelist(directory), cordappJars.map(::ContractsJarFile))
val netParams = installNetworkParameters(notaryInfos, newWhitelist, existingNetParams, nodeDirs)
@ -170,12 +214,28 @@ class NetworkBootstrapper {
}
}
private fun gatherNotaryInfos(nodeInfoFiles: List<Path>): List<NotaryInfo> {
/*the function checks for duplicate myLegalName in the all the *_node.conf files
All the myLegalName values are added to a HashSet - this helps detect duplicate values.
If a duplicate name is found the process is aborted with an error message
*/
private fun checkForDuplicateLegalNames(nodeInfoFiles: List<Path>) {
val legalNames = HashSet<String>()
for (nodeInfoFile in nodeInfoFiles) {
val nodeConfig = ConfigFactory.parseFile((nodeInfoFile.parent / "node.conf").toFile())
val legalName = nodeConfig.getString("myLegalName")
if(!legalNames.add(legalName)){
println("Duplicate Node Found - ensure every node has a unique legal name");
throw IllegalArgumentException("Duplicate Node Found - $legalName");
}
}
}
private fun gatherNotaryInfos(nodeInfoFiles: List<Path>, configs: Map<Path, Config>): List<NotaryInfo> {
return nodeInfoFiles.mapNotNull { nodeInfoFile ->
// The config contains the notary type
val nodeConfig = ConfigFactory.parseFile((nodeInfoFile.parent / "node.conf").toFile())
val nodeConfig = configs[nodeInfoFile.parent]!!
if (nodeConfig.hasPath("notary")) {
val validating = nodeConfig.getConfig("notary").getBoolean("validating")
val validating = nodeConfig.getBoolean("notary.validating")
// And the node-info file contains the notary's identity
val nodeInfo = nodeInfoFile.readObject<SignedNodeInfo>().verified()
NotaryInfo(nodeInfo.notaryIdentity(), validating)

View File

@ -1,7 +1,7 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import rx.Notification
class RxNotificationSerializer(

View File

@ -15,4 +15,4 @@ import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
/**
* A serializer for [StringBuffer].
*/
object StringBufferSerializer : CustomSerializer.ToString<StringBuffer>(StringBuffer::class.java)
object StringBufferSerializer : CustomSerializer.ToString<StringBuffer>(StringBuffer::class.java)

View File

@ -21,10 +21,10 @@ import net.corda.core.serialization.serialize
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.createDevKeyStores
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
import net.corda.serialization.internal.SerializationContextImpl
import net.corda.serialization.internal.SerializationFactoryImpl
import net.corda.serialization.internal.amqp.amqpMagic
import net.corda.serialization.internal.AllWhitelist
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.TestIdentity

View File

@ -52,7 +52,7 @@ import net.corda.nodeapi.internal.ShutdownHook
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.bridging.BridgeControlListener
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.serialization.*
import net.corda.serialization.internal.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import rx.Scheduler

View File

@ -43,7 +43,7 @@ import net.corda.node.VersionInfo
import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.coreContractClasses
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
import net.corda.serialization.internal.DefaultWhitelist
import org.apache.commons.collections4.map.LRUMap
import java.lang.reflect.Modifier
import java.net.JarURLConnection

View File

@ -4,11 +4,10 @@ import net.corda.core.cordapp.Cordapp
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.amqp.custom.RxNotificationSerializer
import net.corda.serialization.internal.CordaSerializationMagic
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.SerializerFactory
import java.util.concurrent.ConcurrentHashMap
/**

View File

@ -2,8 +2,8 @@ package net.corda.node.serialization.amqp
import net.corda.core.concurrent.CordaFuture
import net.corda.core.toObservable
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import rx.Observable
import java.io.NotSerializableException

View File

@ -6,14 +6,12 @@ import net.corda.core.utilities.loggerFor
import net.corda.node.services.messaging.ObservableContextInterface
import net.corda.node.services.messaging.ObservableSubscription
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.serialization.amqp.*
import net.corda.serialization.internal.amqp.*
import org.apache.qpid.proton.codec.Data
import rx.Notification
import rx.Observable
import rx.Subscriber
import java.io.NotSerializableException
import java.lang.reflect.Type
/**

View File

@ -12,9 +12,9 @@ package net.corda.node.serialization.kryo
import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import net.corda.serialization.internal.CordaSerializationMagic
import net.corda.serialization.internal.kryo.AbstractKryoSerializationScheme
import net.corda.serialization.internal.kryo.kryoMagic
class KryoServerSerializationScheme : AbstractKryoSerializationScheme() {
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean {

View File

@ -24,8 +24,8 @@ import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
import net.corda.serialization.internal.SerializationFactoryImpl
import rx.Observable
import rx.Scheduler
import java.nio.file.Path

View File

@ -47,8 +47,8 @@ import net.corda.node.services.statemachine.transitions.StateMachine
import net.corda.node.services.statemachine.transitions.StateMachineConfiguration
import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl
import net.corda.nodeapi.internal.serialization.withTokenContext
import net.corda.serialization.internal.SerializeAsTokenContextImpl
import net.corda.serialization.internal.withTokenContext
import org.apache.activemq.artemis.utils.ReusableLatch
import rx.Observable
import rx.subjects.PublishSubject

View File

@ -40,7 +40,7 @@ import net.corda.core.utilities.debug
import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.currentDBSession
import net.corda.nodeapi.internal.serialization.CordaSerializationEncoding
import net.corda.serialization.internal.CordaSerializationEncoding
import java.time.Clock
/**

View File

@ -69,7 +69,7 @@ class CordappLoaderTest {
assertThat(actualCordapp.schedulableFlows).isEmpty()
assertThat(actualCordapp.services).isEmpty()
assertThat(actualCordapp.serializationWhitelists).hasSize(1)
assertThat(actualCordapp.serializationWhitelists.first().javaClass.name).isEqualTo("net.corda.nodeapi.internal.serialization.DefaultWhitelist")
assertThat(actualCordapp.serializationWhitelists.first().javaClass.name).isEqualTo("net.corda.serialization.internal.DefaultWhitelist")
assertThat(actualCordapp.jarPath).isEqualTo(isolatedJAR)
}

View File

@ -1,23 +1,20 @@
package net.corda.node.internal.serialization
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
import net.corda.core.internal.ThreadBox
import net.corda.core.context.Trace
import net.corda.node.internal.serialization.testutils.AMQPRoundTripRPCSerializationScheme
import net.corda.node.internal.serialization.testutils.TestObservableContext as ServerObservableContext
import net.corda.node.services.messaging.ObservableSubscription
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
import co.paralleluniverse.common.util.SameThreadExecutor
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.RemovalListener
import com.nhaarman.mockito_kotlin.mock
import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableSerializer
import net.corda.core.context.Trace
import net.corda.core.internal.ThreadBox
import net.corda.node.internal.serialization.testutils.AMQPRoundTripRPCSerializationScheme
import net.corda.node.internal.serialization.testutils.serializationContext
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
import net.corda.node.services.messaging.ObservableSubscription
import net.corda.nodeapi.RPCApi
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.SerializationOutput
import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.Test
import rx.Notification
@ -28,6 +25,8 @@ import java.time.Instant
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
import net.corda.node.internal.serialization.testutils.TestObservableContext as ServerObservableContext
class RoundTripObservableSerializerTests {
private fun getID() = Trace.InvocationId("test1", Instant.now())

View File

@ -4,13 +4,13 @@ import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.context.Trace
import net.corda.node.internal.serialization.testutils.*
import net.corda.node.internal.serialization.testutils.TestObservableContext
import net.corda.node.internal.serialization.testutils.serializationContext
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
import net.corda.node.services.messaging.ObservableSubscription
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.AllWhitelist
import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.Test
import rx.Observable

View File

@ -2,17 +2,15 @@ package net.corda.node.internal.serialization.testutils
import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableSerializer
import net.corda.core.context.Trace
import net.corda.core.cordapp.Cordapp
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.util.concurrent.ConcurrentHashMap
import net.corda.serialization.internal.CordaSerializationMagic
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.AllWhitelist
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
/**

View File

@ -1,9 +1,9 @@
package net.corda.node.internal.serialization.testutils
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.SerializationContextImpl
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
import net.corda.serialization.internal.SerializationContextImpl
import net.corda.serialization.internal.amqp.amqpMagic
import net.corda.serialization.internal.AllWhitelist
val serializationProperties: MutableMap<Any, Any> = mutableMapOf()

View File

@ -96,10 +96,5 @@ class BFTNotaryCordform : CordformDefinition() {
}
override fun setup(context: CordformContext) {
DevIdentityGenerator.generateDistributedNotaryCompositeIdentity(
notaryNames.map { context.baseDirectory(it.toString()) },
clusterName,
minCorrectReplicas(clusterSize)
)
}
}

View File

@ -89,9 +89,5 @@ class RaftNotaryCordform : CordformDefinition() {
}
override fun setup(context: CordformContext) {
DevIdentityGenerator.generateDistributedNotarySingularIdentity(
notaryNames.map { context.baseDirectory(it.toString()) },
clusterName
)
}
}

View File

@ -14,7 +14,7 @@ import com.opengamma.strata.product.common.BuySell
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.packageName
import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.internal.serialization.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.testing.core.DUMMY_BANK_A_NAME
import net.corda.testing.core.DUMMY_BANK_B_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME

View File

@ -0,0 +1,58 @@
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
description 'Corda serialization'
dependencies {
compile project(":core")
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.apache.activemq:artemis-commons:${artemis_version}"
// Kryo: object graph serialization.
compile "com.esotericsoftware:kryo:4.0.0"
compile "de.javakaffee:kryo-serializers:0.41"
compile "org.ow2.asm:asm:$asm_version"
// For AMQP serialisation.
compile "org.apache.qpid:proton-j:$protonj_version"
// FastClasspathScanner: classpath scanning
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
// Pure-Java Snappy compression
compile "org.iq80.snappy:snappy:$snappy_version"
// For caches rather than guava
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
// Unit testing helpers.
testCompile "junit:junit:$junit_version"
testCompile "org.assertj:assertj-core:$assertj_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile project(':node-driver')
}
configurations {
testArtifacts.extendsFrom testRuntime
}
task testJar(type: Jar) {
classifier "tests"
from sourceSets.test.output
}
artifacts {
testArtifacts testJar
}
jar {
baseName 'corda-serialization'
}
publish {
name jar.baseName
}

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import net.corda.core.serialization.ClassWhitelist
import sun.misc.Unsafe
@ -42,7 +42,7 @@ import kotlin.collections.LinkedHashSet
* Inheritance works for blacklisted items, but one can specifically exclude classes from blacklisting as well.
* Note: Custom serializer registration trumps white/black lists. So if a given type has a custom serializer and has its name
* in the blacklist - it will still be serialized as specified by custom serializer.
* For more details, see [net.corda.nodeapi.internal.serialization.CordaClassResolver.getRegistration]
* For more details, see [net.corda.serialization.internal.CordaClassResolver.getRegistration]
*/
object AllButBlacklisted : ClassWhitelist {

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal
package net.corda.serialization.internal
import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment

View File

@ -1,6 +1,6 @@
@file:JvmName("ByteBufferStreams")
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import net.corda.core.internal.LazyPool
import java.io.ByteArrayOutputStream

View File

@ -0,0 +1,11 @@
package net.corda.serialization.internal
import net.corda.core.serialization.ClassWhitelist
interface MutableClassWhitelist : ClassWhitelist {
fun add(entry: Class<*>)
}
object AllWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = true
}

View File

@ -10,12 +10,14 @@
@file:JvmName("ClientContexts")
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import net.corda.serialization.internal.amqp.amqpMagic
import net.corda.serialization.internal.kryo.BuiltInExceptionsWhitelist
import net.corda.serialization.internal.kryo.GlobalTransientClassWhiteList
import net.corda.serialization.internal.kryo.kryoMagic
/*
* Serialisation contexts for the client.

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import com.esotericsoftware.kryo.KryoException
import net.corda.core.serialization.SerializationWhitelist

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import net.corda.core.crypto.sha256
import net.corda.core.internal.AbstractAttachment

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import java.io.EOFException
import java.io.InputStream
@ -18,9 +18,9 @@ import java.nio.ByteBuffer
class OrdinalBits(private val ordinal: Int) {
interface OrdinalWriter {
val bits: OrdinalBits
val encodedSize get() = 1
fun writeTo(stream: OutputStream) = stream.write(bits.ordinal)
fun putTo(buffer: ByteBuffer) = buffer.put(bits.ordinal.toByte())!!
@JvmDefault val encodedSize get() = 1
@JvmDefault fun writeTo(stream: OutputStream) = stream.write(bits.ordinal)
@JvmDefault fun putTo(buffer: ByteBuffer) = buffer.put(bits.ordinal.toByte())!!
}
init {

View File

@ -8,13 +8,13 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import net.corda.core.internal.VisibleForTesting
import net.corda.core.serialization.SerializationEncoding
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.serialization.OrdinalBits.OrdinalWriter
import net.corda.serialization.internal.OrdinalBits.OrdinalWriter
import org.iq80.snappy.SnappyFramedInputStream
import org.iq80.snappy.SnappyFramedOutputStream
import java.io.InputStream

View File

@ -8,7 +8,7 @@
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization
package net.corda.serialization.internal
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
@ -17,9 +17,8 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.internal.copyBytes
import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.AttachmentsClassLoader
import net.corda.nodeapi.internal.serialization.amqp.amqpMagic
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import net.corda.serialization.internal.amqp.amqpMagic
import net.corda.serialization.internal.kryo.kryoMagic
import org.slf4j.LoggerFactory
import java.io.NotSerializableException
import java.util.*

Some files were not shown because too many files have changed in this diff Show More