mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
Merge remote-tracking branch 'origin/release/os/4.11' into colljos/encryption_receiver_self_recovery
This commit is contained in:
commit
32c3b0c517
@ -30,7 +30,7 @@ import java.util.*
|
|||||||
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
class ContractUpgradeFlowTest : WithContracts, WithFinality {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()))
|
private val classMockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()))
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
@ -40,6 +40,7 @@ import net.corda.core.utilities.unwrap
|
|||||||
import net.corda.coretesting.internal.matchers.flow.willReturn
|
import net.corda.coretesting.internal.matchers.flow.willReturn
|
||||||
import net.corda.coretesting.internal.matchers.flow.willThrow
|
import net.corda.coretesting.internal.matchers.flow.willThrow
|
||||||
import net.corda.coretests.flows.WithFinality.FinalityInvoker
|
import net.corda.coretests.flows.WithFinality.FinalityInvoker
|
||||||
|
import net.corda.coretests.flows.WithFinality.OldFinalityInvoker
|
||||||
import net.corda.finance.GBP
|
import net.corda.finance.GBP
|
||||||
import net.corda.finance.POUNDS
|
import net.corda.finance.POUNDS
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
@ -50,6 +51,9 @@ import net.corda.finance.test.flows.CashIssueWithObserversFlow
|
|||||||
import net.corda.finance.test.flows.CashPaymentWithObserversFlow
|
import net.corda.finance.test.flows.CashPaymentWithObserversFlow
|
||||||
import net.corda.node.services.persistence.DBTransactionStorage
|
import net.corda.node.services.persistence.DBTransactionStorage
|
||||||
import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery
|
import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery
|
||||||
|
import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord
|
||||||
|
import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord
|
||||||
|
import net.corda.node.services.persistence.HashedDistributionList
|
||||||
import net.corda.node.services.persistence.ReceiverDistributionRecord
|
import net.corda.node.services.persistence.ReceiverDistributionRecord
|
||||||
import net.corda.node.services.persistence.SenderDistributionRecord
|
import net.corda.node.services.persistence.SenderDistributionRecord
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
@ -59,7 +63,6 @@ import net.corda.testing.core.BOB_NAME
|
|||||||
import net.corda.testing.core.CHARLIE_NAME
|
import net.corda.testing.core.CHARLIE_NAME
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.testing.node.internal.CustomCordapp
|
|
||||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
||||||
@ -87,9 +90,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val mockNet = InternalMockNetwork(cordappsForAllNodes = setOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp(),
|
override val mockNet = InternalMockNetwork(cordappsForAllNodes = setOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP, DUMMY_CONTRACTS_CORDAPP, enclosedCordapp(),
|
||||||
findCordapp("net.corda.finance.test.flows"),
|
findCordapp("net.corda.finance.test.flows")))
|
||||||
CustomCordapp(targetPlatformVersion = 3, classes = setOf(FinalityFlow::class.java))))
|
|
||||||
|
|
||||||
private val aliceNode = makeNode(ALICE_NAME)
|
private val aliceNode = makeNode(ALICE_NAME)
|
||||||
|
|
||||||
private val notary = mockNet.defaultNotaryIdentity
|
private val notary = mockNet.defaultNotaryIdentity
|
||||||
@ -124,7 +125,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
val oldBob = createBob(cordapps = listOf(tokenOldCordapp()))
|
val oldBob = createBob(cordapps = listOf(tokenOldCordapp()))
|
||||||
val stx = aliceNode.issuesCashTo(oldBob)
|
val stx = aliceNode.issuesCashTo(oldBob)
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
aliceNode.startFlowAndRunNetwork(FinalityFlow(stx)).resultFuture.getOrThrow()
|
aliceNode.startFlowAndRunNetwork(OldFinalityInvoker(stx)).resultFuture.getOrThrow()
|
||||||
assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull
|
assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,8 +395,10 @@ class FinalityFlowTests : WithFinality {
|
|||||||
assertEquals(StatesToRecord.ONLY_RELEVANT, this?.statesToRecord)
|
assertEquals(StatesToRecord.ONLY_RELEVANT, this?.statesToRecord)
|
||||||
assertEquals(aliceNode.info.singleIdentity().name.hashCode().toLong(), this?.initiatorPartyId)
|
assertEquals(aliceNode.info.singleIdentity().name.hashCode().toLong(), this?.initiatorPartyId)
|
||||||
// note: Charlie assertion here is using the hinted StatesToRecord value passed to it from Alice
|
// note: Charlie assertion here is using the hinted StatesToRecord value passed to it from Alice
|
||||||
assertEquals(mapOf(BOB_NAME.hashCode().toLong() to StatesToRecord.ONLY_RELEVANT,
|
assertEquals(mapOf(
|
||||||
CHARLIE_NAME.hashCode().toLong() to StatesToRecord.ALL_VISIBLE), this?.peersToStatesToRecord)
|
BOB_NAME.hashCode().toLong() to StatesToRecord.ONLY_RELEVANT,
|
||||||
|
CHARLIE_NAME.hashCode().toLong() to StatesToRecord.ALL_VISIBLE
|
||||||
|
), distList.peerHashToStatesToRecord)
|
||||||
}
|
}
|
||||||
validateSenderAndReceiverTimestamps(sdrs, rdr!!)
|
validateSenderAndReceiverTimestamps(sdrs, rdr!!)
|
||||||
|
|
||||||
@ -511,6 +514,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
@InitiatedBy(SpendFlow::class)
|
@InitiatedBy(SpendFlow::class)
|
||||||
class AcceptSpendFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
class AcceptSpendFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
||||||
|
|
||||||
@ -547,6 +551,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
@InitiatedBy(SpeedySpendFlow::class)
|
@InitiatedBy(SpeedySpendFlow::class)
|
||||||
class AcceptSpeedySpendFlow(private val otherSideSession: FlowSession) : FlowLogic<SignedTransaction>() {
|
class AcceptSpeedySpendFlow(private val otherSideSession: FlowSession) : FlowLogic<SignedTransaction>() {
|
||||||
|
|
||||||
@ -580,7 +585,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FinaliseSpeedySpendFlow(val id: SecureHash, val sigs: List<TransactionSignature>) : FlowLogic<SignedTransaction>() {
|
class FinaliseSpeedySpendFlow(val id: SecureHash, private val sigs: List<TransactionSignature>) : FlowLogic<SignedTransaction>() {
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): SignedTransaction {
|
override fun call(): SignedTransaction {
|
||||||
@ -606,6 +611,7 @@ class FinalityFlowTests : WithFinality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
@InitiatedBy(MimicFinalityFailureFlow::class)
|
@InitiatedBy(MimicFinalityFailureFlow::class)
|
||||||
class TriggerReceiveFinalityFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
class TriggerReceiveFinalityFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
|
@ -4,7 +4,13 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import com.natpryce.hamkrest.MatchResult
|
import com.natpryce.hamkrest.MatchResult
|
||||||
import com.natpryce.hamkrest.Matcher
|
import com.natpryce.hamkrest.Matcher
|
||||||
import com.natpryce.hamkrest.equalTo
|
import com.natpryce.hamkrest.equalTo
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.FinalityFlow
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.FlowSession
|
||||||
|
import net.corda.core.flows.InitiatedBy
|
||||||
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.flows.ReceiveFinalityFlow
|
||||||
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.FlowStateMachineHandle
|
import net.corda.core.internal.FlowStateMachineHandle
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
@ -58,4 +64,13 @@ interface WithFinality : WithMockNet {
|
|||||||
subFlow(ReceiveFinalityFlow(otherSide))
|
subFlow(ReceiveFinalityFlow(otherSide))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@StartableByRPC
|
||||||
|
class OldFinalityInvoker(private val transaction: SignedTransaction) : FlowLogic<SignedTransaction>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): SignedTransaction {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
return subFlow(FinalityFlow(transaction))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,6 +219,7 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
|
|||||||
|
|
||||||
val requiresNotarisation = needsNotarySignature(transaction)
|
val requiresNotarisation = needsNotarySignature(transaction)
|
||||||
val useTwoPhaseFinality = serviceHub.myInfo.platformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY
|
val useTwoPhaseFinality = serviceHub.myInfo.platformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY
|
||||||
|
&& serviceHub.getAppContext().cordapp.targetPlatformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY
|
||||||
|
|
||||||
if (useTwoPhaseFinality) {
|
if (useTwoPhaseFinality) {
|
||||||
val stxn = if (requiresNotarisation) {
|
val stxn = if (requiresNotarisation) {
|
||||||
@ -250,6 +251,7 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
|
|||||||
return stxn
|
return stxn
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
logger.warnOnce("The current usage of FinalityFlow is not using Two Phase Finality. Please consider upgrading your CorDapp (refer to Corda 4.11 release notes).")
|
||||||
val stxn = if (requiresNotarisation) {
|
val stxn = if (requiresNotarisation) {
|
||||||
notarise().first
|
notarise().first
|
||||||
} else transaction
|
} else transaction
|
||||||
@ -501,6 +503,8 @@ class ReceiveFinalityFlow(private val otherSideSession: FlowSession,
|
|||||||
|
|
||||||
val requiresNotarisation = needsNotarySignature(stx)
|
val requiresNotarisation = needsNotarySignature(stx)
|
||||||
val fromTwoPhaseFinalityNode = serviceHub.networkMapCache.getNodeByLegalIdentity(otherSideSession.counterparty)?.platformVersion!! >= PlatformVersionSwitches.TWO_PHASE_FINALITY
|
val fromTwoPhaseFinalityNode = serviceHub.networkMapCache.getNodeByLegalIdentity(otherSideSession.counterparty)?.platformVersion!! >= PlatformVersionSwitches.TWO_PHASE_FINALITY
|
||||||
|
&& serviceHub.getAppContext().cordapp.targetPlatformVersion >= PlatformVersionSwitches.TWO_PHASE_FINALITY
|
||||||
|
|
||||||
if (fromTwoPhaseFinalityNode) {
|
if (fromTwoPhaseFinalityNode) {
|
||||||
if (requiresNotarisation) {
|
if (requiresNotarisation) {
|
||||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordUnnotarisedTransaction", flowLogic = this) {
|
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordUnnotarisedTransaction", flowLogic = this) {
|
||||||
@ -537,6 +541,7 @@ class ReceiveFinalityFlow(private val otherSideSession: FlowSession,
|
|||||||
otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (deferredAck)
|
otherSideSession.send(FetchDataFlow.Request.End) // Finish fetching data (deferredAck)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
logger.warnOnce("The current usage of ReceiveFinalityFlow is not using Two Phase Finality. Please consider upgrading your CorDapp (refer to Corda 4.11 release notes).")
|
||||||
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordTransactions", flowLogic = this) {
|
serviceHub.telemetryServiceInternal.span("${this::class.java.name}#recordTransactions", flowLogic = this) {
|
||||||
serviceHub.recordTransactions(statesToRecord, setOf(stx))
|
serviceHub.recordTransactions(statesToRecord, setOf(stx))
|
||||||
}
|
}
|
||||||
|
@ -432,7 +432,7 @@ object LoggerSerializer : Serializer<Logger>() {
|
|||||||
object ClassSerializer : Serializer<Class<*>>() {
|
object ClassSerializer : Serializer<Class<*>>() {
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<Class<*>>): Class<*> {
|
override fun read(kryo: Kryo, input: Input, type: Class<Class<*>>): Class<*> {
|
||||||
val className = input.readString()
|
val className = input.readString()
|
||||||
return Class.forName(className, true, kryo.classLoader)
|
return if (className == "void") Void.TYPE else Class.forName(className, true, kryo.classLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(kryo: Kryo, output: Output, clazz: Class<*>) {
|
override fun write(kryo: Kryo, output: Output, clazz: Class<*>) {
|
||||||
|
@ -351,6 +351,20 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
|
|||||||
assertEquals(randomHash, exception2.requested)
|
assertEquals(randomHash, exception2.requested)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout=300_000)
|
||||||
|
fun `serialize - deserialize primative void`() {
|
||||||
|
val original = JavaVoidHolder()
|
||||||
|
val roundtrip = original.checkpointSerialize(context).checkpointDeserialize(context)
|
||||||
|
assertThat(roundtrip.voidClass).isEqualTo(original.voidClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaVoidHolder {
|
||||||
|
val voidClass: Class<Void> = Void.TYPE
|
||||||
|
init {
|
||||||
|
check(voidClass.name == "void") // Sanity check to make sure we're dealing with the primitive void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `compression has the desired effect`() {
|
fun `compression has the desired effect`() {
|
||||||
compression ?: return
|
compression ?: return
|
||||||
@ -373,6 +387,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `compression reduces number of bytes significantly`() {
|
fun `compression reduces number of bytes significantly`() {
|
||||||
|
@Suppress("unused")
|
||||||
class Holder(val holder: ByteArray)
|
class Holder(val holder: ByteArray)
|
||||||
|
|
||||||
val obj = Holder(ByteArray(20000))
|
val obj = Holder(ByteArray(20000))
|
||||||
|
@ -36,6 +36,7 @@ class AesDbEncryptionService(private val database: CordaPersistence) : Encryptio
|
|||||||
companion object {
|
companion object {
|
||||||
private const val INITIAL_KEY_COUNT = 10
|
private const val INITIAL_KEY_COUNT = 10
|
||||||
private const val UUID_BYTES = 16
|
private const val UUID_BYTES = 16
|
||||||
|
private const val VERSION_TAG = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private val aesKeys = ArrayList<Pair<UUID, SecretKey>>()
|
private val aesKeys = ArrayList<Pair<UUID, SecretKey>>()
|
||||||
@ -73,7 +74,7 @@ class AesDbEncryptionService(private val database: CordaPersistence) : Encryptio
|
|||||||
val (keyId, aesKey) = aesKeys[newSecureRandom().nextInt(aesKeys.size)]
|
val (keyId, aesKey) = aesKeys[newSecureRandom().nextInt(aesKeys.size)]
|
||||||
val ciphertext = AesEncryption.encrypt(aesKey, plaintext, additionalData)
|
val ciphertext = AesEncryption.encrypt(aesKey, plaintext, additionalData)
|
||||||
val buffer = ByteBuffer.allocate(1 + UUID_BYTES + Integer.BYTES + (additionalData?.size ?: 0) + ciphertext.size)
|
val buffer = ByteBuffer.allocate(1 + UUID_BYTES + Integer.BYTES + (additionalData?.size ?: 0) + ciphertext.size)
|
||||||
buffer.put(1) // Version tag
|
buffer.put(VERSION_TAG.toByte())
|
||||||
// Prepend the key ID to the returned ciphertext. It's OK that this is not included in the authenticated additional data because
|
// Prepend the key ID to the returned ciphertext. It's OK that this is not included in the authenticated additional data because
|
||||||
// changing this value will lead to either an non-existent key or an another key which will not be able decrypt the ciphertext.
|
// changing this value will lead to either an non-existent key or an another key which will not be able decrypt the ciphertext.
|
||||||
buffer.putUUID(keyId)
|
buffer.putUUID(keyId)
|
||||||
@ -88,9 +89,7 @@ class AesDbEncryptionService(private val database: CordaPersistence) : Encryptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun decrypt(ciphertext: ByteArray): EncryptionService.PlaintextAndAAD {
|
override fun decrypt(ciphertext: ByteArray): EncryptionService.PlaintextAndAAD {
|
||||||
val buffer = ByteBuffer.wrap(ciphertext)
|
val buffer = wrap(ciphertext)
|
||||||
val version = buffer.get().toInt()
|
|
||||||
require(version == 1)
|
|
||||||
val keyId = buffer.getUUID()
|
val keyId = buffer.getUUID()
|
||||||
val aesKey = requireNotNull(aesKeys.find { it.first == keyId }?.second) { "Unable to decrypt" }
|
val aesKey = requireNotNull(aesKeys.find { it.first == keyId }?.second) { "Unable to decrypt" }
|
||||||
val additionalData = buffer.getAdditionaData()
|
val additionalData = buffer.getAdditionaData()
|
||||||
@ -100,11 +99,18 @@ class AesDbEncryptionService(private val database: CordaPersistence) : Encryptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun extractUnauthenticatedAdditionalData(ciphertext: ByteArray): ByteArray? {
|
override fun extractUnauthenticatedAdditionalData(ciphertext: ByteArray): ByteArray? {
|
||||||
val buffer = ByteBuffer.wrap(ciphertext)
|
val buffer = wrap(ciphertext)
|
||||||
buffer.position(1 + UUID_BYTES)
|
buffer.position(buffer.position() + UUID_BYTES)
|
||||||
return buffer.getAdditionaData()
|
return buffer.getAdditionaData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun wrap(ciphertext: ByteArray): ByteBuffer {
|
||||||
|
val buffer = ByteBuffer.wrap(ciphertext)
|
||||||
|
val version = buffer.get().toInt()
|
||||||
|
require(version == VERSION_TAG) { "Unknown version $version" }
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
private fun ByteBuffer.getAdditionaData(): ByteArray? {
|
private fun ByteBuffer.getAdditionaData(): ByteArray? {
|
||||||
val additionalDataSize = getInt()
|
val additionalDataSize = getInt()
|
||||||
return if (additionalDataSize > 0) ByteArray(additionalDataSize).also { get(it) } else null
|
return if (additionalDataSize > 0) ByteArray(additionalDataSize).also { get(it) } else null
|
||||||
|
@ -11,6 +11,7 @@ import net.corda.core.internal.VisibleForTesting
|
|||||||
import net.corda.core.node.StatesToRecord
|
import net.corda.core.node.StatesToRecord
|
||||||
import net.corda.core.node.services.vault.Sort
|
import net.corda.core.node.services.vault.Sort
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.node.CordaClock
|
import net.corda.node.CordaClock
|
||||||
import net.corda.node.services.EncryptionService
|
import net.corda.node.services.EncryptionService
|
||||||
import net.corda.node.services.network.PersistentPartyInfoCache
|
import net.corda.node.services.network.PersistentPartyInfoCache
|
||||||
@ -105,8 +106,7 @@ class DBTransactionStorageLedgerRecovery(private val database: CordaPersistence,
|
|||||||
return ReceiverDistributionRecord(
|
return ReceiverDistributionRecord(
|
||||||
SecureHash.parse(this.txId),
|
SecureHash.parse(this.txId),
|
||||||
this.compositeKey.peerPartyId,
|
this.compositeKey.peerPartyId,
|
||||||
hashedDL.peerHashToStatesToRecord,
|
OpaqueBytes(this.distributionList),
|
||||||
hashedDL.senderStatesToRecord,
|
|
||||||
this.compositeKey.timestamp
|
this.compositeKey.timestamp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ class DBTransactionStorageLedgerRecovery(private val database: CordaPersistence,
|
|||||||
val timestamp: Instant,
|
val timestamp: Instant,
|
||||||
val timestampDiscriminator: Int = nextDiscriminatorNumber.andIncrement
|
val timestampDiscriminator: Int = nextDiscriminatorNumber.andIncrement
|
||||||
) {
|
) {
|
||||||
constructor(key: TimestampKey, partyId: Long): this(partyId = partyId, timestamp = key.timestamp, timestampDiscriminator = key.timestampDiscriminator)
|
constructor(key: TimestampKey, partyId: Long): this(partyId, key.timestamp, key.timestampDiscriminator)
|
||||||
companion object {
|
companion object {
|
||||||
val nextDiscriminatorNumber = AtomicInteger()
|
val nextDiscriminatorNumber = AtomicInteger()
|
||||||
}
|
}
|
||||||
@ -268,30 +268,34 @@ class DBTransactionStorageLedgerRecovery(private val database: CordaPersistence,
|
|||||||
val txnMetadata = criteriaQuery.from(DBReceiverDistributionRecord::class.java)
|
val txnMetadata = criteriaQuery.from(DBReceiverDistributionRecord::class.java)
|
||||||
val predicates = mutableListOf<Predicate>()
|
val predicates = mutableListOf<Predicate>()
|
||||||
val compositeKey = txnMetadata.get<PersistentKey>("compositeKey")
|
val compositeKey = txnMetadata.get<PersistentKey>("compositeKey")
|
||||||
predicates.add(criteriaBuilder.greaterThanOrEqualTo(compositeKey.get<Instant>(PersistentKey::timestamp.name), timeWindow.fromTime))
|
val timestamp = compositeKey.get<Instant>(PersistentKey::timestamp.name)
|
||||||
predicates.add(criteriaBuilder.and(criteriaBuilder.lessThanOrEqualTo(compositeKey.get<Instant>(PersistentKey::timestamp.name), timeWindow.untilTime)))
|
predicates.add(criteriaBuilder.greaterThanOrEqualTo(timestamp, timeWindow.fromTime))
|
||||||
|
predicates.add(criteriaBuilder.and(criteriaBuilder.lessThanOrEqualTo(timestamp, timeWindow.untilTime)))
|
||||||
if (excludingTxnIds.isNotEmpty()) {
|
if (excludingTxnIds.isNotEmpty()) {
|
||||||
predicates.add(criteriaBuilder.and(criteriaBuilder.not(txnMetadata.get<String>(DBSenderDistributionRecord::txId.name).`in`(
|
val txId = txnMetadata.get<String>(DBSenderDistributionRecord::txId.name)
|
||||||
excludingTxnIds.map { it.toString() }))))
|
predicates.add(criteriaBuilder.and(criteriaBuilder.not(txId.`in`(excludingTxnIds.map { it.toString() }))))
|
||||||
}
|
}
|
||||||
if (initiators.isNotEmpty()) {
|
if (initiators.isNotEmpty()) {
|
||||||
val initiatorPartyIds = initiators.map { partyInfoCache.getPartyIdByCordaX500Name(it) }
|
val initiatorPartyIds = initiators.map(partyInfoCache::getPartyIdByCordaX500Name)
|
||||||
predicates.add(criteriaBuilder.and(compositeKey.get<Long>(PersistentKey::peerPartyId.name).`in`(initiatorPartyIds)))
|
predicates.add(criteriaBuilder.and(compositeKey.get<Long>(PersistentKey::peerPartyId.name).`in`(initiatorPartyIds)))
|
||||||
}
|
}
|
||||||
criteriaQuery.where(*predicates.toTypedArray())
|
criteriaQuery.where(*predicates.toTypedArray())
|
||||||
// optionally order by timestamp
|
// optionally order by timestamp
|
||||||
orderByTimestamp?.let {
|
orderByTimestamp?.let {
|
||||||
val orderCriteria =
|
val orderCriteria = when (orderByTimestamp) {
|
||||||
when (orderByTimestamp) {
|
|
||||||
// when adding column position of 'group by' shift in case columns were removed
|
// when adding column position of 'group by' shift in case columns were removed
|
||||||
Sort.Direction.ASC -> criteriaBuilder.asc(compositeKey.get<Instant>(PersistentKey::timestamp.name))
|
Sort.Direction.ASC -> criteriaBuilder.asc(timestamp)
|
||||||
Sort.Direction.DESC -> criteriaBuilder.desc(compositeKey.get<Instant>(PersistentKey::timestamp.name))
|
Sort.Direction.DESC -> criteriaBuilder.desc(timestamp)
|
||||||
}
|
}
|
||||||
criteriaQuery.orderBy(orderCriteria)
|
criteriaQuery.orderBy(orderCriteria)
|
||||||
}
|
}
|
||||||
session.createQuery(criteriaQuery).resultList
|
session.createQuery(criteriaQuery).resultList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun decryptHashedDistributionList(encryptedBytes: ByteArray): HashedDistributionList {
|
||||||
|
return HashedDistributionList.decrypt(encryptedBytes, encryptionService)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -301,35 +305,33 @@ class DistributionRecords(
|
|||||||
val receiverRecords: List<DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord> = emptyList()
|
val receiverRecords: List<DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord> = emptyList()
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
assert(senderRecords.isNotEmpty() || receiverRecords.isNotEmpty()) { "Must set senderRecords or receiverRecords or both." }
|
require(senderRecords.isNotEmpty() || receiverRecords.isNotEmpty()) { "Must set senderRecords or receiverRecords or both." }
|
||||||
}
|
}
|
||||||
|
|
||||||
val size = senderRecords.size + receiverRecords.size
|
val size = senderRecords.size + receiverRecords.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
open class DistributionRecord(
|
abstract class DistributionRecord {
|
||||||
open val txId: SecureHash,
|
abstract val txId: SecureHash
|
||||||
open val statesToRecord: StatesToRecord,
|
abstract val timestamp: Instant
|
||||||
open val timestamp: Instant
|
}
|
||||||
)
|
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class SenderDistributionRecord(
|
data class SenderDistributionRecord(
|
||||||
override val txId: SecureHash,
|
override val txId: SecureHash,
|
||||||
val peerPartyId: Long, // CordaX500Name hashCode()
|
val peerPartyId: Long, // CordaX500Name hashCode()
|
||||||
override val statesToRecord: StatesToRecord,
|
val statesToRecord: StatesToRecord,
|
||||||
override val timestamp: Instant
|
override val timestamp: Instant
|
||||||
) : DistributionRecord(txId, statesToRecord, timestamp)
|
) : DistributionRecord()
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class ReceiverDistributionRecord(
|
data class ReceiverDistributionRecord(
|
||||||
override val txId: SecureHash,
|
override val txId: SecureHash,
|
||||||
val initiatorPartyId: Long, // CordaX500Name hashCode()
|
val initiatorPartyId: Long, // CordaX500Name hashCode()
|
||||||
val peersToStatesToRecord: Map<Long, StatesToRecord>, // CordaX500Name hashCode() -> StatesToRecord
|
val encryptedDistributionList: OpaqueBytes,
|
||||||
override val statesToRecord: StatesToRecord,
|
|
||||||
override val timestamp: Instant
|
override val timestamp: Instant
|
||||||
) : DistributionRecord(txId, statesToRecord, timestamp)
|
) : DistributionRecord()
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
enum class DistributionRecordType {
|
enum class DistributionRecordType {
|
||||||
|
@ -71,6 +71,7 @@ import net.corda.testing.internal.IS_OPENJ9
|
|||||||
import net.corda.testing.internal.LogHelper
|
import net.corda.testing.internal.LogHelper
|
||||||
import net.corda.testing.internal.vault.VaultFiller
|
import net.corda.testing.internal.vault.VaultFiller
|
||||||
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||||
|
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
|
||||||
import net.corda.testing.node.internal.InternalMockNetwork
|
import net.corda.testing.node.internal.InternalMockNetwork
|
||||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
import net.corda.testing.node.internal.InternalMockNodeParameters
|
||||||
import net.corda.testing.node.internal.TestStartedNode
|
import net.corda.testing.node.internal.TestStartedNode
|
||||||
@ -140,7 +141,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
|||||||
// We run this in parallel threads to help catch any race conditions that may exist. The other tests
|
// We run this in parallel threads to help catch any race conditions that may exist. The other tests
|
||||||
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
||||||
// allow interruption half way through.
|
// allow interruption half way through.
|
||||||
mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP), threadPerNode = true)
|
mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP), threadPerNode = true)
|
||||||
val notaryNode = mockNet.defaultNotaryNode
|
val notaryNode = mockNet.defaultNotaryNode
|
||||||
val notary = mockNet.defaultNotaryIdentity
|
val notary = mockNet.defaultNotaryIdentity
|
||||||
notaryNode.services.ledger(notary) {
|
notaryNode.services.ledger(notary) {
|
||||||
@ -247,7 +248,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
|||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `shutdown and restore`() {
|
fun `shutdown and restore`() {
|
||||||
Assume.assumeTrue(!IS_OPENJ9)
|
Assume.assumeTrue(!IS_OPENJ9)
|
||||||
mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP))
|
mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP))
|
||||||
val notaryNode = mockNet.defaultNotaryNode
|
val notaryNode = mockNet.defaultNotaryNode
|
||||||
val notary = mockNet.defaultNotaryIdentity
|
val notary = mockNet.defaultNotaryIdentity
|
||||||
notaryNode.services.ledger(notary) {
|
notaryNode.services.ledger(notary) {
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
import net.corda.finance.issuedBy
|
import net.corda.finance.issuedBy
|
||||||
|
import net.corda.testing.node.internal.CustomCordapp
|
||||||
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.InternalMockNetwork
|
import net.corda.testing.node.internal.InternalMockNetwork
|
||||||
import net.corda.testing.node.internal.startFlow
|
import net.corda.testing.node.internal.startFlow
|
||||||
@ -23,7 +24,8 @@ import rx.schedulers.Schedulers
|
|||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
class ServiceHubConcurrentUsageTest {
|
class ServiceHubConcurrentUsageTest {
|
||||||
private val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP))
|
private val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP,
|
||||||
|
CustomCordapp(classes = setOf(TestFlow::class.java))))
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun stopNodes() {
|
fun stopNodes() {
|
||||||
|
@ -75,7 +75,9 @@ class TimedFlowTests {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setup() {
|
fun setup() {
|
||||||
mockNet = InternalMockNetwork(
|
mockNet = InternalMockNetwork(
|
||||||
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, enclosedCordapp()),
|
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP,
|
||||||
|
CustomCordapp(classes = setOf(FinalityFlow::class.java)),
|
||||||
|
enclosedCordapp()),
|
||||||
defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()),
|
defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin()),
|
||||||
threadPerNode = true
|
threadPerNode = true
|
||||||
)
|
)
|
||||||
|
@ -24,6 +24,8 @@ import net.corda.node.services.network.PersistentNetworkMapCache
|
|||||||
import net.corda.node.services.network.PersistentPartyInfoCache
|
import net.corda.node.services.network.PersistentPartyInfoCache
|
||||||
import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.IN_FLIGHT
|
import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.IN_FLIGHT
|
||||||
import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.VERIFIED
|
import net.corda.node.services.persistence.DBTransactionStorage.TransactionStatus.VERIFIED
|
||||||
|
import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord
|
||||||
|
import net.corda.node.services.persistence.DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord
|
||||||
import net.corda.nodeapi.internal.DEV_ROOT_CA
|
import net.corda.nodeapi.internal.DEV_ROOT_CA
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
@ -260,11 +262,12 @@ class DBTransactionStorageLedgerRecoveryTests {
|
|||||||
transactionRecovery.addReceiverTransactionRecoveryMetadata(receiverTransaction.id, ALICE_NAME,
|
transactionRecovery.addReceiverTransactionRecoveryMetadata(receiverTransaction.id, ALICE_NAME,
|
||||||
TransactionMetadata(ALICE_NAME, ReceiverDistributionList(encryptedDL, ALL_VISIBLE)))
|
TransactionMetadata(ALICE_NAME, ReceiverDistributionList(encryptedDL, ALL_VISIBLE)))
|
||||||
assertEquals(IN_FLIGHT, readTransactionFromDB(receiverTransaction.id).status)
|
assertEquals(IN_FLIGHT, readTransactionFromDB(receiverTransaction.id).status)
|
||||||
readReceiverDistributionRecordFromDB(receiverTransaction.id).let {
|
readReceiverDistributionRecordFromDB(receiverTransaction.id).let { record ->
|
||||||
assertEquals(ONLY_RELEVANT, it.statesToRecord)
|
val distList = transactionRecovery.decryptHashedDistributionList(record.encryptedDistributionList.bytes)
|
||||||
assertEquals(ALL_VISIBLE, it.peersToStatesToRecord.map { it.value }[0])
|
assertEquals(ONLY_RELEVANT, distList.senderStatesToRecord)
|
||||||
assertEquals(ALICE_NAME, partyInfoCache.getCordaX500NameByPartyId(it.initiatorPartyId))
|
assertEquals(ALL_VISIBLE, distList.peerHashToStatesToRecord.values.first())
|
||||||
assertEquals(setOf(BOB_NAME), it.peersToStatesToRecord.map { (peer, _) -> partyInfoCache.getCordaX500NameByPartyId(peer) }.toSet() )
|
assertEquals(ALICE_NAME, partyInfoCache.getCordaX500NameByPartyId(record.initiatorPartyId))
|
||||||
|
assertEquals(setOf(BOB_NAME), distList.peerHashToStatesToRecord.map { (peer) -> partyInfoCache.getCordaX500NameByPartyId(peer) }.toSet() )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +326,7 @@ class DBTransactionStorageLedgerRecoveryTests {
|
|||||||
assertThat(roundtrip).isEqualTo(hashedDistList)
|
assertThat(roundtrip).isEqualTo(hashedDistList)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readTransactionFromDB(id: SecureHash): DBTransactionStorage.DBTransaction {
|
private fun readTransactionFromDB(txId: SecureHash): DBTransactionStorage.DBTransaction {
|
||||||
val fromDb = database.transaction {
|
val fromDb = database.transaction {
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from ${DBTransactionStorage.DBTransaction::class.java.name} where txId = :transactionId",
|
"from ${DBTransactionStorage.DBTransaction::class.java.name} where txId = :transactionId",
|
||||||
@ -334,22 +337,22 @@ class DBTransactionStorageLedgerRecoveryTests {
|
|||||||
return fromDb[0]
|
return fromDb[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readSenderDistributionRecordFromDB(id: SecureHash? = null): List<SenderDistributionRecord> {
|
private fun readSenderDistributionRecordFromDB(txId: SecureHash? = null): List<SenderDistributionRecord> {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
if (id != null)
|
if (txId != null)
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from ${DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord::class.java.name} where txId = :transactionId",
|
"from ${DBSenderDistributionRecord::class.java.name} where txId = :transactionId",
|
||||||
DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord::class.java
|
DBSenderDistributionRecord::class.java
|
||||||
).setParameter("transactionId", id.toString()).resultList.map { it.toSenderDistributionRecord() }
|
).setParameter("transactionId", txId.toString()).resultList.map { it.toSenderDistributionRecord() }
|
||||||
else
|
else
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from ${DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord::class.java.name}",
|
"from ${DBSenderDistributionRecord::class.java.name}",
|
||||||
DBTransactionStorageLedgerRecovery.DBSenderDistributionRecord::class.java
|
DBSenderDistributionRecord::class.java
|
||||||
).resultList.map { it.toSenderDistributionRecord() }
|
).resultList.map { it.toSenderDistributionRecord() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readReceiverDistributionRecordFromDB(id: SecureHash): ReceiverDistributionRecord {
|
private fun readReceiverDistributionRecordFromDB(txId: SecureHash): ReceiverDistributionRecord {
|
||||||
val fromDb = database.transaction {
|
val fromDb = database.transaction {
|
||||||
session.createQuery(
|
session.createQuery(
|
||||||
"from ${DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord::class.java.name} where txId = :transactionId",
|
"from ${DBTransactionStorageLedgerRecovery.DBReceiverDistributionRecord::class.java.name} where txId = :transactionId",
|
||||||
|
@ -58,6 +58,7 @@ import net.corda.testing.internal.IS_OPENJ9
|
|||||||
import net.corda.testing.internal.LogHelper
|
import net.corda.testing.internal.LogHelper
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer
|
import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer
|
||||||
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin
|
||||||
|
import net.corda.testing.node.internal.CustomCordapp
|
||||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||||
import net.corda.testing.node.internal.InternalMockNetwork
|
import net.corda.testing.node.internal.InternalMockNetwork
|
||||||
@ -124,7 +125,7 @@ class FlowFrameworkTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setUpMockNet() {
|
fun setUpMockNet() {
|
||||||
mockNet = InternalMockNetwork(
|
mockNet = InternalMockNetwork(
|
||||||
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, FINANCE_CONTRACTS_CORDAPP),
|
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP, FINANCE_CONTRACTS_CORDAPP, CustomCordapp(setOf("net.corda.node.services.statemachine"))),
|
||||||
servicePeerAllocationStrategy = RoundRobin()
|
servicePeerAllocationStrategy = RoundRobin()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
//apply plugin: 'net.corda.plugins.cordapp'
|
apply plugin: 'net.corda.plugins.cordapp'
|
||||||
//apply plugin: 'net.corda.plugins.quasar-utils'
|
//apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(":core")
|
cordaCompile project(":core")
|
||||||
compile project(':finance:workflows')
|
cordapp project(':finance:workflows')
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@ -15,3 +15,17 @@ jar {
|
|||||||
attributes('Corda-Testing': true)
|
attributes('Corda-Testing': true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cordapp {
|
||||||
|
targetPlatformVersion corda_platform_version.toInteger()
|
||||||
|
minimumPlatformVersion 1
|
||||||
|
workflow {
|
||||||
|
name "Corda Cash Observers Test CorDapp"
|
||||||
|
versionId 1
|
||||||
|
vendor "R3"
|
||||||
|
licence "Open Source (Apache 2)"
|
||||||
|
}
|
||||||
|
signing {
|
||||||
|
enabled false
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
//apply plugin: 'net.corda.plugins.cordapp'
|
apply plugin: 'net.corda.plugins.cordapp'
|
||||||
//apply plugin: 'net.corda.plugins.quasar-utils'
|
//apply plugin: 'net.corda.plugins.quasar-utils'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(":core")
|
cordaCompile project(":core")
|
||||||
compile project(":testing:cordapps:dbfailure:dbfcontracts")
|
cordapp project(":testing:cordapps:dbfailure:dbfcontracts")
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@ -15,3 +15,17 @@ jar {
|
|||||||
attributes('Corda-Testing': true)
|
attributes('Corda-Testing': true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cordapp {
|
||||||
|
targetPlatformVersion corda_platform_version.toInteger()
|
||||||
|
minimumPlatformVersion 1
|
||||||
|
workflow {
|
||||||
|
name "Corda DB Failure Test CorDapp"
|
||||||
|
versionId 1
|
||||||
|
vendor "R3"
|
||||||
|
licence "Open Source (Apache 2)"
|
||||||
|
}
|
||||||
|
signing {
|
||||||
|
enabled false
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user