Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORE-16983 / CORE-16967 Atomic Swap with Facade #4691

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
26df684
Jackson serializers and deserializers for DigitalSignatureAndMetadat…
szymonsztuka Sep 8, 2023
d8d8cc0
Remove unnecessary comment
szymonsztuka Sep 8, 2023
0526ce0
Merge branch 'release/interop/syntax' into interop/jmac/CORE-16983
jmacmahonr3 Sep 17, 2023
1c50caa
initial draft pr
jmacmahonr3 Sep 22, 2023
36c9a16
Now adding test cordapp
jmacmahonr3 Sep 25, 2023
0a0c3db
Update to serializer
jmacmahonr3 Oct 3, 2023
a310103
Add new serializers and deserializers to main Jackson service (for now).
szymonsztuka Oct 3, 2023
559529c
1. Create unit test to recreate: java.lang.ClassCastException: class …
szymonsztuka Oct 4, 2023
0aba6eb
Serializing and deserializing Proof Of action solved.
szymonsztuka Oct 5, 2023
953f17c
Key creation from bytes solved.
szymonsztuka Oct 5, 2023
b671b25
Remove test class is the extended version is now in the other module.
szymonsztuka Oct 6, 2023
6f3ee6f
Test flow (unrealistic one) to check signature verification.
szymonsztuka Oct 6, 2023
96c5ec3
Update api version to a temporary build, add TODOs
szymonsztuka Oct 6, 2023
3f3a606
Merge branch 'release/interop/syntax' into interop/jmac/CORE-16983
szymonsztuka Oct 6, 2023
27bb137
Remove unnecessary function with "default" parameter.
szymonsztuka Oct 6, 2023
004a0aa
Remove unnecessary function with "default" parameter use by tests only.
szymonsztuka Oct 6, 2023
8693ed0
Facade Specification doesn't require new serializers, only producing …
szymonsztuka Oct 6, 2023
255f084
FacadeRequestSerializer / FacadeResponseSerializer don't need the exp…
szymonsztuka Oct 6, 2023
2ffecc0
Remove unused change in a dependency.
szymonsztuka Oct 6, 2023
dbe4cc9
Verify Signature via RPC flow call.
szymonsztuka Oct 9, 2023
cb8296d
Merge branch 'release/interop/syntax' into interop/jmac/CORE-16983
szymonsztuka Oct 9, 2023
b773212
Update LockState and Contract to use DigitalSignatureAndMetadata.
szymonsztuka Oct 9, 2023
ed56413
Enable serialisation of PublicKey.
szymonsztuka Oct 10, 2023
f14fd93
Query to check owned states.
szymonsztuka Oct 11, 2023
527170a
Swap - part on the transaction draft side.
szymonsztuka Oct 17, 2023
b766e4d
Merge branch 'release/interop/syntax' into interop/jmac/CORE-16983
szymonsztuka Oct 17, 2023
f60bf11
Fix compilation issues after merge from syntax branch.
szymonsztuka Oct 17, 2023
0f034be
Swap - part on transaction with a lock side.
szymonsztuka Oct 18, 2023
69c2ac9
Swap - facade method lock with a generic asset ID instead of a more s…
szymonsztuka Oct 18, 2023
f6afa48
DTOs returned by Query Flows.
szymonsztuka Oct 20, 2023
913ae14
Move new additions to UtxoLedgerTest for now into interop test.
szymonsztuka Oct 23, 2023
99a77ae
Test signature proof generation by a notary.
szymonsztuka Oct 23, 2023
ad15cc2
Test signature proof generation by a notary.
szymonsztuka Oct 24, 2023
579395c
Merge branch 'szymon/interop/add-signing-utxo-derived-test' into inte…
szymonsztuka Oct 24, 2023
fc85ece
Merge branch 'release/interop/syntax' into interop/jmac/CORE-16983
szymonsztuka Oct 24, 2023
f10cfe6
Updates after merge from syntax.
szymonsztuka Oct 24, 2023
3fbb794
Updates after merge from syntax.
szymonsztuka Oct 24, 2023
c0e931a
Updates after merge from syntax.
szymonsztuka Oct 24, 2023
cc62a4b
Merge branch 'release/interop/syntax' into interop/jmac/CORE-16983
szymonsztuka Oct 24, 2023
86a1e95
Remove dependency on implementation library by Json Service, use serv…
szymonsztuka Oct 25, 2023
288018f
Separate service
szymonsztuka Oct 25, 2023
192f7a1
Separate service
szymonsztuka Oct 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions components/flow/flow-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,16 @@ dependencies {
implementation project(':libs:platform-info')
implementation project(":libs:serialization:serialization-avro")
implementation project(":libs:tracing")
implementation project(':libs:crypto:merkle-impl')
Copy link
Contributor

@vlajos vlajos Sep 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on -impl looks a bit suspicious.
Dependencies should be injected via OSGi.
Although probably some direct constructors are not available via OSGi.


implementation platform("net.corda:corda-api:$cordaApiVersion")

implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion"

implementation project(':libs:serialization:json-validator')
implementation project(":libs:serialization:json-serializers")

implementation "com.typesafe:config:$typeSafeConfigVersion"
implementation "net.corda:corda-application"
implementation "net.corda:corda-ledger-utxo"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.corda.flow.application.services.impl


import net.corda.flow.application.services.impl.interop.dispatch.buildDispatcher
import net.corda.flow.application.services.impl.interop.facade.FacadeReaders
import net.corda.flow.application.services.impl.interop.facade.FacadeRequestImpl
Expand Down Expand Up @@ -75,7 +76,8 @@ class FacadeServiceImpl @Activate constructor(
mapOf(
"org.corda.interop/platform/tokens/v1.0" to "/tokens-v1.0.json",
"org.corda.interop/platform/tokens/v2.0" to "/tokens-v2.0.json",
"org.corda.interop/platform/tokens/v3.0" to "/tokens-v3.0.json"
"org.corda.interop/platform/tokens/v3.0" to "/tokens-v3.0.json",
"org.corda.interop/platform/lock/v1.0" to "/locking-facade.json"
).mapValues { (_, value) -> this::class.java.getResource(value)?.readText().toString().trimIndent() }
.mapValues { (_, value) ->
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
@file:Suppress("WildcardImport")
package net.corda.flow.application.services.impl.interop

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.module.SimpleModule
import net.corda.base.internal.ByteSequence
import net.corda.base.internal.OpaqueBytes
import net.corda.crypto.cipher.suite.SignatureSpecImpl
import net.corda.crypto.core.DigitalSignatureWithKeyId
import net.corda.crypto.core.SecureHashImpl
import net.corda.crypto.core.parseSecureHash
import net.corda.crypto.merkle.impl.IndexedMerkleLeafImpl
import net.corda.crypto.merkle.impl.MerkleProofImpl
import net.corda.v5.application.crypto.DigitalSignatureAndMetadata
import net.corda.v5.application.crypto.DigitalSignatureMetadata
import net.corda.v5.crypto.DigitalSignature
import net.corda.v5.crypto.SecureHash
import net.corda.v5.crypto.SignatureSpec
import net.corda.v5.crypto.merkle.IndexedMerkleLeaf
import net.corda.v5.crypto.merkle.MerkleProof
import net.corda.v5.crypto.merkle.MerkleProofType
import java.lang.RuntimeException
import java.time.Instant

object ProofOfActionSerialisationModule {
val module = SimpleModule().apply {
addSerializer(MerkleProof::class.java, MerkleProofSerializer())
addDeserializer(MerkleProof::class.java, MerkleProofDeserializer())
addSerializer(SignatureSpec::class.java, SignatureSpecSerializer())
addDeserializer(SignatureSpec::class.java, SignatureSpecDeserializer())
addSerializer(DigitalSignatureMetadata::class.java, DigitalSignatureMetadataSerializer())
addDeserializer(DigitalSignatureMetadata::class.java, DigitalSignatureMetadataDeserializer())
addSerializer(DigitalSignature.WithKeyId::class.java, DigitalSignatureWithKeyIdSerializer())
addSerializer(DigitalSignatureWithKeyId::class.java, DigitalSignatureWithKeyIdSerializer())
addDeserializer(DigitalSignature.WithKeyId::class.java, DigitalSignatureWithKeyIdDeserializer())
addSerializer(DigitalSignatureAndMetadata::class.java, DigitalSignatureAndMetadataSerializer())
addDeserializer(DigitalSignatureAndMetadata::class.java, DigitalSignatureAndMetadataDeserializer())
addSerializer(IndexedMerkleLeaf::class.java, IndexedMerkleLeafSerializer())
addDeserializer(IndexedMerkleLeaf::class.java, IndexedMerkleLeafDeserializer())
addSerializer(OpaqueBytes::class.java, OpaqueBytesSerializer())
addDeserializer(OpaqueBytes::class.java, OpaqueBytesDeserializer())
addSerializer(ByteSequence::class.java, ByteSequenceSerializer())
addDeserializer(ByteSequence::class.java, ByteSequenceDeserializer())
addSerializer(SecureHash::class.java, SecureHashSerializer())
addSerializer(SecureHashImpl::class.java, SecureHashSerializer())
addDeserializer(SecureHash::class.java, SecureHashDeserializer())
addSerializer(IndexedMerkleLeaf::class.java, IndexedMerkleLeafSerializer())
addSerializer(IndexedMerkleLeafImpl::class.java, IndexedMerkleLeafSerializer())
addDeserializer(IndexedMerkleLeaf::class.java, IndexedMerkleLeafDeserializer())
}
}

class IndexedMerkleLeafSerializer : JsonSerializer<IndexedMerkleLeaf>() {
override fun serialize(
leaf: IndexedMerkleLeaf,
gen: JsonGenerator,
serializers: SerializerProvider
) {
gen.writeStartObject()
gen.writeObjectField("index", leaf.getIndex())
gen.writeObjectField("nonce", leaf.getNonce())
gen.writeObjectField("leafData", leaf.getLeafData())
gen.writeEndObject()
}
}

class IndexedMerkleLeafDeserializer : JsonDeserializer<IndexedMerkleLeaf>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): IndexedMerkleLeafImpl {
val node: JsonNode = parser.codec.readTree(parser)
val index = node.get("index").asInt()
val nonce = if (node.has("nonce")) node.get("nonce").binaryValue() else null
val leafData = node.get("leafData").binaryValue()

return IndexedMerkleLeafImpl(index, nonce, leafData)
}
}

class MerkleProofSerializer : JsonSerializer<MerkleProof>() {
override fun serialize(
merkleProof: MerkleProof,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeStartObject()
generator.writeStringField("proofType", merkleProof.getProofType().name)
generator.writeNumberField("treeSize", merkleProof.getTreeSize())
generator.writeArrayFieldStart("leaves")
for (leaf in merkleProof.leaves) {
generator.writeObject(leaf)
}
generator.writeEndArray()
generator.writeArrayFieldStart("hashes")
for (hash in merkleProof.hashes) {
generator.writeObject(hash)
}
generator.writeEndArray()
generator.writeEndObject()
}
}

class MerkleProofDeserializer : JsonDeserializer<MerkleProof>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): MerkleProof {
val node: JsonNode = parser.codec.readTree(parser)
val proofType = MerkleProofType.valueOf(node.get("proofType").asText())
val treeSize = node.get("treeSize").asInt()
val leavesNode = node.get("leaves")
val leaves = mutableListOf<IndexedMerkleLeaf>()
for (leafNode in leavesNode) {
val leaf = parser.codec.treeToValue(leafNode, IndexedMerkleLeaf::class.java)
leaves.add(leaf)
}
val hashesNode = node.get("hashes")
val hashes = mutableListOf<SecureHash>()
for (hashNode in hashesNode) {
val hash = parser.codec.treeToValue(hashNode, SecureHash::class.java)
hashes.add(hash)
}
return MerkleProofImpl(proofType, treeSize, leaves, hashes)
}
}

class SignatureSpecSerializer : JsonSerializer<SignatureSpec>() {
override fun serialize(
spec: SignatureSpec,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeString(spec.getSignatureName())
}
}

class SignatureSpecDeserializer : JsonDeserializer<SignatureSpec>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): SignatureSpec {
val signatureName = parser.text
return SignatureSpecImpl(signatureName)
}
}


class DigitalSignatureMetadataSerializer : JsonSerializer<DigitalSignatureMetadata>() {
override fun serialize(
metadata: DigitalSignatureMetadata,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeStartObject()
generator.writeStringField("timestamp", metadata.timestamp.toString())
generator.writeObjectField("signatureSpec", metadata.signatureSpec)
generator.writeObjectField("properties", metadata.properties)
generator.writeEndObject()
}
}

class DigitalSignatureMetadataDeserializer : JsonDeserializer<DigitalSignatureMetadata>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): DigitalSignatureMetadata {
val node = parser.codec.readTree(parser) as JsonNode
val timestamp = Instant.parse(node.get("timestamp").asText())
val signatureSpec = parser.codec.treeToValue(node.get("signatureSpec"), SignatureSpec::class.java)
@Suppress("unchecked_cast")
val properties = parser.codec.treeToValue(node.get("properties"), Map::class.java).toMutableMap() as MutableMap<String, String>
return DigitalSignatureMetadata(timestamp, signatureSpec, properties)
}
}

class DigitalSignatureAndMetadataSerializer : JsonSerializer<DigitalSignatureAndMetadata>() {
override fun serialize(
signatureAndMetadata: DigitalSignatureAndMetadata,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeStartObject()
generator.writeObjectField("signature", signatureAndMetadata.signature)
generator.writeObjectField("metadata", signatureAndMetadata.metadata)
generator.writeObjectField("proof", signatureAndMetadata.proof)
generator.writeEndObject()
}
}

class OpaqueBytesSerializer : JsonSerializer<OpaqueBytes>() {
override fun serialize(
opaqueBytes: OpaqueBytes,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeBinary(opaqueBytes.getBytes())
}
}

class OpaqueBytesDeserializer : JsonDeserializer<OpaqueBytes>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): OpaqueBytes {
val bytes = parser.getBinaryValue()
return OpaqueBytes(bytes)
}
}

class DigitalSignatureAndMetadataDeserializer : JsonDeserializer<DigitalSignatureAndMetadata>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): DigitalSignatureAndMetadata {
val node = parser.codec.readTree(parser) as JsonNode
val signature = parser.codec.treeToValue(node.get("signature"), DigitalSignature.WithKeyId::class.java)
val metadata = parser.codec.treeToValue(node.get("metadata"), DigitalSignatureMetadata::class.java)
val proof = parser.codec.treeToValue(node.get("proof"), MerkleProof::class.java)
return DigitalSignatureAndMetadata(signature, metadata, proof)
}
}

class ByteSequenceSerializer : JsonSerializer<ByteSequence>() {
override fun serialize(
byteSequence: ByteSequence,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeBinary(byteSequence.getBytes())
}
}

class ByteSequenceDeserializer : JsonDeserializer<ByteSequence>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): ByteSequence {
val bytes = parser.getBinaryValue()
return OpaqueBytes(bytes)
}
}

class DigitalSignatureWithKeyIdSerializer : JsonSerializer<DigitalSignature.WithKeyId>() {
override fun serialize(
signature: DigitalSignature.WithKeyId?,
generator: JsonGenerator?,
provider: SerializerProvider
) {
generator!!.writeStartObject()
generator.writeObjectField("by", signature!!.by)
generator.writeFieldName("bytes")
generator.writeBinary(signature.bytes)
generator.writeEndObject()
}
}

class DigitalSignatureWithKeyIdDeserializer : JsonDeserializer<DigitalSignature.WithKeyId>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): DigitalSignature.WithKeyId {
val node = parser.codec.readTree(parser) as JsonNode
val by = parser.codec.treeToValue(node.get("by"), SecureHash::class.java)
val bytes = node.get("bytes").binaryValue()
return DigitalSignatureWithKeyId(by, bytes)
}
}

class SecureHashSerializer : JsonSerializer<SecureHash>() {
override fun serialize(
secureHash: SecureHash,
generator: JsonGenerator,
provider: SerializerProvider
) {
generator.writeString(secureHash.toString())
}
}

@Suppress("TooGenericExceptionThrown")
class SecureHashDeserializer : JsonDeserializer<SecureHash>() {
override fun deserialize(
parser: JsonParser,
ctxt: DeserializationContext
): SecureHash {
if (parser.currentToken == JsonToken.VALUE_STRING) {
return parseSecureHash(parser.text)
}

throw RuntimeException("Expected a string for SecureHash")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.corda.flow.application.services.impl.interop.binding.FacadeInParamete
import net.corda.flow.application.services.impl.interop.binding.FacadeInterfaceBinding
import net.corda.flow.application.services.impl.interop.binding.FacadeMethodBinding
import net.corda.flow.application.services.impl.interop.binding.FacadeOutParameterBindings
import net.corda.v5.application.crypto.DigitalSignatureAndMetadata
import net.corda.v5.application.interop.binding.BindsFacade
import net.corda.v5.application.interop.binding.BindsFacadeMethod
import net.corda.v5.application.interop.binding.BindsFacadeParameter
Expand Down Expand Up @@ -485,6 +486,8 @@ private fun validateTypeAgreement(
ParameterTypeLabel.BYTES -> parameterType == ByteArray::class.javaPrimitiveType ||
parameterType == ByteBuffer::class.java

ParameterTypeLabel.SIGNED_TX -> parameterType == DigitalSignatureAndMetadata::class.java

ParameterTypeLabel.JSON -> true
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package net.corda.flow.application.services.impl.interop.dispatch

import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.corda.common.json.serializers.standardTypesModule
import net.corda.flow.application.services.impl.interop.ProofOfActionSerialisationModule
import net.corda.flow.application.services.impl.interop.binding.FacadeInterfaceBinding
import net.corda.flow.application.services.impl.interop.binding.FacadeMethodBinding
import net.corda.flow.application.services.impl.interop.binding.FacadeOutParameterBindings
Expand All @@ -18,6 +23,7 @@ import net.corda.v5.application.interop.facade.FacadeResponse
import net.corda.v5.application.interop.parameters.TypedParameter
import net.corda.v5.application.interop.parameters.TypedParameterValue
import net.corda.v5.base.annotations.Suspendable
import java.util.*

object FacadeServerDispatchers {

Expand Down Expand Up @@ -46,7 +52,18 @@ fun Any.buildDispatcher(facade: Facade, typeConverter: TypeConverter): FacadeSer
}

fun Any.buildDispatcher(facade: Facade): FacadeServerDispatcher =
buildDispatcher(facade, TypeConverter(JacksonJsonMarshaller(ObjectMapper().registerKotlinModule())))
buildDispatcher(facade, TypeConverter(
JacksonJsonMarshaller(
ObjectMapper().apply { //TODO could be removed as not used in runtime code
registerModule(KotlinModule.Builder().build())
registerModule(JavaTimeModule())
enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)
setTimeZone(TimeZone.getTimeZone("UTC"))
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
registerModule(ProofOfActionSerialisationModule.module)
registerModule(standardTypesModule())
})
))

fun Any.buildDispatcher(facade: Facade, marshaller: JsonMarshaller): FacadeServerDispatcher =
buildDispatcher(facade, TypeConverter(marshaller))
Expand Down
Loading