diff --git a/finance/build.gradle b/finance/build.gradle index 7180a5f9eb..8f54fc8c7d 100644 --- a/finance/build.gradle +++ b/finance/build.gradle @@ -17,6 +17,9 @@ sourceSets { runtimeClasspath += main.output + test.output srcDir file('src/integration-test/kotlin') } + resources { + srcDir file('src/integration-test/resources') + } } } diff --git a/finance/src/integration-test/kotlin/net/corda/finance/compat/CompatibilityTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/compat/CompatibilityTest.kt new file mode 100644 index 0000000000..bf1dc2cb88 --- /dev/null +++ b/finance/src/integration-test/kotlin/net/corda/finance/compat/CompatibilityTest.kt @@ -0,0 +1,38 @@ +package net.corda.finance.compat + +import net.corda.core.serialization.SerializationDefaults +import net.corda.core.serialization.deserialize +import net.corda.core.serialization.serialize +import net.corda.core.transactions.SignedTransaction +import net.corda.finance.contracts.asset.Cash +import net.corda.testing.core.SerializationEnvironmentRule +import org.junit.Rule +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +// TODO: If this type of testing gets momentum, we can create a mini-framework that rides through list of files +// and performs necessary validation on all of them. +class CompatibilityTest { + + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule() + + @Test + fun issueCashTansactionReadTest() { + val inputStream = javaClass.classLoader.getResourceAsStream("compatibilityData/v3/node_transaction.dat") + assertNotNull(inputStream) + val inByteArray: ByteArray = inputStream.readBytes() + val transaction = inByteArray.deserialize(context = SerializationDefaults.STORAGE_CONTEXT) + assertNotNull(transaction) + val commands = transaction.tx.commands + assertEquals(1, commands.size) + assertTrue(commands.first().value is Cash.Commands.Issue) + + // Serialize back and check that representation is byte-to-byte identical to what it was originally. + val serializedForm = transaction.serialize(context = SerializationDefaults.STORAGE_CONTEXT) + assertTrue(inByteArray.contentEquals(serializedForm.bytes)) + } +} \ No newline at end of file diff --git a/finance/src/integration-test/resources/compatibilityData/v3/node_transaction.dat b/finance/src/integration-test/resources/compatibilityData/v3/node_transaction.dat new file mode 100644 index 0000000000..21392230dd Binary files /dev/null and b/finance/src/integration-test/resources/compatibilityData/v3/node_transaction.dat differ diff --git a/node-api/build.gradle b/node-api/build.gradle index cf0429fec0..4f3beb3890 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -35,7 +35,7 @@ dependencies { compile "de.javakaffee:kryo-serializers:0.41" // For AMQP serialisation. - compile "org.apache.qpid:proton-j:0.21.0" + compile "org.apache.qpid:proton-j:0.27.1" // FastClasspathScanner: classpath scanning - needed for the NetworkBootstraper compile 'io.github.lukehutch:fast-classpath-scanner:2.12.3' diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt index a5a2b94675..330b947a81 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt @@ -130,7 +130,7 @@ class AMQPBridgeManager(config: NodeSSLConfiguration, val artemisMessageClientFa private fun clientArtemisMessageHandler(artemisMessage: ClientMessage) { val data = ByteArray(artemisMessage.bodySize).apply { artemisMessage.bodyBuffer.readBytes(this) } - val properties = HashMap() + val properties = HashMap() for (key in P2PMessagingHeaders.whitelistedHeaders) { if (artemisMessage.containsProperty(key)) { var value = artemisMessage.getObjectProperty(key) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/NettyWritable.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/NettyWritable.kt index 1bb97851ed..230b712fe8 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/NettyWritable.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/NettyWritable.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.protonwrapper.engine import io.netty.buffer.ByteBuf +import org.apache.qpid.proton.codec.ReadableBuffer import org.apache.qpid.proton.codec.WritableBuffer import java.nio.ByteBuffer @@ -57,6 +58,10 @@ internal class NettyWritable(val nettyBuffer: ByteBuf) : WritableBuffer { nettyBuffer.writeBytes(payload) } + override fun put(payload: ReadableBuffer) { + nettyBuffer.writeBytes(payload.byteBuffer()) + } + override fun limit(): Int { return nettyBuffer.capacity() } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/ApplicationMessage.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/ApplicationMessage.kt index f42116f950..03911bb50f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/ApplicationMessage.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/ApplicationMessage.kt @@ -10,5 +10,5 @@ interface ApplicationMessage { val topic: String val destinationLegalName: String val destinationLink: NetworkHostAndPort - val applicationProperties: Map + val applicationProperties: Map } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/ReceivedMessageImpl.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/ReceivedMessageImpl.kt index 15c893e41a..7bf83505c5 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/ReceivedMessageImpl.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/ReceivedMessageImpl.kt @@ -16,7 +16,7 @@ internal class ReceivedMessageImpl(override val payload: ByteArray, override val sourceLink: NetworkHostAndPort, override val destinationLegalName: String, override val destinationLink: NetworkHostAndPort, - override val applicationProperties: Map, + override val applicationProperties: Map, private val channel: Channel, private val delivery: Delivery) : ReceivedMessage { data class MessageCompleter(val status: MessageStatus, val delivery: Delivery) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/SendableMessageImpl.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/SendableMessageImpl.kt index 13065c960e..6adc9b2bbc 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/SendableMessageImpl.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/messages/impl/SendableMessageImpl.kt @@ -15,7 +15,7 @@ internal class SendableMessageImpl(override val payload: ByteArray, override val topic: String, override val destinationLegalName: String, override val destinationLink: NetworkHostAndPort, - override val applicationProperties: Map) : SendableMessage { + override val applicationProperties: Map) : SendableMessage { var buf: ByteBuf? = null @Volatile var status: MessageStatus = MessageStatus.Unsent diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt index 62838ba626..3c7bb32db2 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt @@ -170,7 +170,7 @@ class AMQPClient(val targets: List, fun createMessage(payload: ByteArray, topic: String, destinationLegalName: String, - properties: Map): SendableMessage { + properties: Map): SendableMessage { return SendableMessageImpl(payload, topic, destinationLegalName, currentTarget, properties) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt index 007729f512..d8ead67682 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPServer.kt @@ -155,7 +155,7 @@ class AMQPServer(val hostName: String, topic: String, destinationLegalName: String, destinationLink: NetworkHostAndPort, - properties: Map): SendableMessage { + properties: Map): SendableMessage { val dest = InetSocketAddress(destinationLink.host, destinationLink.port) require(dest in clientChannels.keys) { "Destination not available" diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index b8c857846e..1090aef766 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -152,7 +152,7 @@ class ProtonWrapperTests { artemis.session.createQueue(sendAddress, RoutingType.ANYCAST, "queue", true) val consumer = artemis.session.createConsumer("queue") val testData = "Test".toByteArray() - val testProperty = mutableMapOf() + val testProperty = mutableMapOf() testProperty["TestProp"] = "1" val message = amqpClient.createMessage(testData, sendAddress, CHARLIE_NAME.toString(), testProperty) amqpClient.write(message)