Merge remote-tracking branch 'origin/release/os/4.11' into colljos/encryption_receiver_self_recovery

This commit is contained in:
Jose Coll 2023-08-22 12:31:29 +01:00
commit 32c3b0c517
15 changed files with 153 additions and 67 deletions

View File

@ -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

View File

@ -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

View File

@ -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))
}
}
} }

View File

@ -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))
} }

View File

@ -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<*>) {

View File

@ -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))

View File

@ -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

View File

@ -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 {

View File

@ -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) {

View File

@ -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() {

View File

@ -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
) )

View File

@ -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",

View File

@ -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()
) )

View File

@ -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
}
}

View File

@ -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
}
}